1. 程式人生 > 其它 >《網路多人遊戲架構與程式設計》之伯克利套接字

《網路多人遊戲架構與程式設計》之伯克利套接字

一、建立、銷燬、繫結、傳送、接收     SOCKET socket(int af,int type,int protocol);        af:  協議簇
巨集 含義
AF_UNSPEC 未指定
AF_TNET IPV4
AF_IPX 分組交換
AF_APPLETALK Appletalk協議
AF_INET6 IPV6
type:  通過socket傳送和接收分組的形式
巨集 含義
SOCK_STREAM 有序的、可靠的資料流分段
SOCK_DGRAM 離散的報文
SOCK_RAW 包頭可以由應用層自定義
SOCK_SEQPACKET 類似SOCK_STREAM,但要整體讀取資料包
protocol:socket應使用的協議,包括傳輸層協議、各種實用網路層協議。
巨集 需要的型別 含義
IPPROTO_UDP SOCK_DGRAM UDP資料報
IPPROTO_TCP SOCK_STREAM TCP報文段
IPPRPTO_IP/0 Any 實用預設協議
  ///////////////建立SOCKET///////////////// 建立1個IPV4 UDP socket : SOCKET udpSocket = socket( AF_INET, SOCK_DGRAM, 0); 建立1個TCP socket:SOCKET tcpSocket = socket( AF_INET, SOCK_STREAM, 0);   ///////////停止傳輸和接收SOCKET//////////
int shutdown(SOCKET sock, int how); ———————————— how: SD_SEND         停止傳送;產生FIN資料包,所有資料傳送後傳送這個資料包,通知另一端關閉socket,然後另一端會回一個FIN資料包,我們就可以關閉socket了。          SD_RECEIVE     停止接收          SD_BOTH         停止傳送和接收   ///////////////關閉SOCKET///////////////// 關閉不考慮型別:int closesocket (SOCKET sock); ———————————— Tips:關閉前保證所有傳送的和接收的資料都已經傳輸和確認。
  ///////////////繫結SOCKET///////////////// 返回值:0 成功 ; -1 錯誤   ///////////////UDP傳送資料///////////////// int sendto(SOCKET sock, const char *buf, int len, int flags, const sockaddr *to, int tolen); buf:指向待發送資料起始地址的指標; len:待發送資料的大小;避免傳送大於1300位元組的資料包。 flags:對傳送標誌進行按位或運算的結果,遊戲中通常是0。 to:接收端的sockaddr。 tolen:to指向的sockaddr的大小。對於IPV4,等於sizeof(sockaddr_in)。 返回值:成功:等待發送資料長度; 失敗:-1。   ///////////////UDP接收資料///////////////// int recvfrom(SOCKET sock, const char *buf, int len, int flags, const sockaddr *from, int *fromlen); buf:接收的資料包的緩衝區; len:buf可以儲存的最大位元組數。 flags:對接收標誌進行按位或運算的結果,遊戲中通常是0。 from:指向sockaddr的指標。 fromlen:from指向的sockaddr的大小。 返回值:成功:複製到buf的位元組數; 失敗:-1。   ///////////////TCP啟動監聽///////////////// int listen(SOCKET sock, int backlog); backlog:允許傳入的最大連線數。SOMAXCONN表示預設值。 返回值:成功 0;錯誤 -1。   ///////////////TCP接收連線///////////////// SOCKET accept(SOCKET sock, sockaddr *addr, int *addrlen);   ///////////////TCP發起連結///////////////// SOCKET connect(SOCKET sock, const sockaddr *addr, int *addrlen);   ///////////////TCP傳送資料///////////////// int send(SOCKET sock, const char *buf, int len, int flags);   ///////////////TCP接收資料///////////////// int recv(SOCKET sock, char *buf, int len, int flags);     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 二、阻塞和非阻塞I/O   三種解決執行緒阻塞的辦法:
  1. 多執行緒
  • 目的:給每一個可能的阻塞呼叫生成一個執行緒:每個客戶端1個,監聽1個...
  • 缺點:每個客戶端需要1個執行緒,不利於擴充套件和管理。
  • 非阻塞I/O
    • 使用ioctlsocket設定socket為非阻塞模式:int ioctlsocket(SOCKET sock, long cmd, u_long *argp);  //cmd是控制引數;argp是該引數的取值,0阻止開啟非阻塞,其他值開啟。
    • 優點:每幀檢查是否有準備好的待接收資料,有,先處理第一個掛起;沒有,進行到下一幀,沒有等待。
    • 缺點:輪詢的socket數量很大時,效率很低。
  • select函式
    • 優點:可以同時檢查多個socket,只要有1個準備好了就開始執行
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 三、平臺差異   ///////////////Windows標頭檔案///////////////// Windows平臺使用標頭檔案 WinSock2.h ,包含了socket相關的函式宣告和資料型別。 ———————————— Windows.h是舊版本,和Winsock2.h一起用會造成命名衝突,想要避免衝突就要在Windows.h之前引用Winsock2.h   ///////////////Windows啟用socket///////////////// 使用WSAStartup啟用Windows上的socket庫: int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);  返回值:0 或 錯誤程式碼。 必須先成功啟動WSAStartup, 才能正確執行Winsock2中的函式。 ———————————— wVersionRequested 是兩位元組的WORD,低位元組表示主版本號,高位元組表示Winsock實現的最低版本。 lpWSAData 指向Windows特定的資料結構。 WSAStartup填入被啟用的socket庫的資訊。   ///////////////Windows關閉socket///////////////// int WSACleanup();      //結束所有未完成的socket操作,釋放所有socket資源 返回值:錯誤程式碼,通常返回-1,要找到錯誤來源要在返回-1後呼叫WSAGetLastError()。 ———————————— int WSAGetLastError();    返回當前執行執行緒最近的錯誤程式碼   ///////////////Windows資料包地址///////////////// 存放地址資訊的資料型別:struct  sockaddr  {                                                 uint16_t    sa_family;        //指定地址型別,應與af一致。                                                 char          sa_data[14];    //儲存真正的地址。                                         };   建立1個IPV4資料包的地址:struct  sockaddr_in  {                                                 short                 sin_family;      //指定地址型別,應與af一致。                                                 uint16_t            sin_port;         //儲存地址中16位埠                                                     struct  in_addr  sin_addr;        //儲存4位元組IPV4地址;in_addr 在不同平臺有差異。                                                 char                  sin_zero[8];    //儲存真正的地址。                                         };   IP地址從字串表示轉換為 in_addr  表示:inet_pton POSIX系統,InetPton Windows系統 src:儲存.分隔的地址 dst:指向待賦值的sin_addr欄位 返回值:1 成功;0 源字串錯誤;-1 其他   將域名解析為IP地址:   ///////////////socket位元組序///////////////// 原因:TCP/IP和主機可能存在多位元組數的位元組序上採用不同標準 解決:多位元組數賦值必須將 主機位元組序 轉換為 網路位元組序 方法:uint16_t  htons( uint16_t hostshort);           uint16_t  htonl( uint16_t hostlong);   網路位元組序 轉換成 主機位元組序: uint16_t  ntohs( uint16_t networkshort);   uint16_t  ntohl( uint16_t networklong);