make_server_socket( WSAStartup(MAKEWORD(), &inet_WsaData);
if (LOBYTE(inet_WsaData.wVersion) != 2 || HIBYTE(inet_WsaData.wVersion) != 0)//2
{
WSACleanup();
return -1;
}
tcp_socket = socket(AF_INET, SOCK_STREAM, ); sockaddr_in saddr; saddr.sin_family === (::bind(tcp_socket, ( sockaddr*)&saddr, (saddr)) == -) << << - (::listen(tcp_socket, ) == -)
<< << -
1 WSADATA inet_WsaData;SAStartup(MAKEWORD(1, 1), &inet_WsaData);
在windows下使用socket的相關函數前,必須通過WSAStartup函數完成對Winsock服務的初始化。
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
該函數的第一個參數指明程序請求使用的Socket版本,其中高位字節指明副版本、低位字節指明主版本;第二個參數可以用來返回請求的Socket的版本信息。當一個應用程序調用WSAStartup函數時,操作系統根據請求的Socket版本來搜索相應的Socket庫,然後綁定找到的Socket庫到該應用程序中。以後應用程序就可以調用所請求的Socket庫中的其它Socket函數了。
以前大家使用的都是socket1.1版本,但socket2.0版本已經出來了,所以我這裡使用的是socket2.0版本(MAKEWORD(2.0))
1.1版和2.0版的區別:
兩者的最重要區別是1.1版只支持TCP/IP協議,而2.0版可以支持多協議。2.0版有良好的向後兼容性,任何使用1.1版的源代碼,二進制文件,應用程序都可以不加修改地在2.0規范下使用。
MAKEWORD的定義如下
#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
2 if (LOBYTE(inet_WsaData.wVersion) != 2 || HIBYTE(inet_WsaData.wVersion) != 0)用於檢測當前的Socket是否為2.0
LOBYTE和HIBYTE是兩個宏,在vs2013裡定義如下
#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))
WSACleanup();用於解除與Socket庫的綁定並釋放Socket庫所占用的系統資源。
3 int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
socket函數用於建立一個socket,函數原型如下
SOCKET socket(int af, int type, int protocol);
第一個參數af指定應用程序使用的通信協議的協議族,af一般置為AF_INET(表示internetwork: UDP, TCP等);
第二個參數type為協議的Socket類型,常用的有3種:SOCK_STREAM、SOCK_DGRAM和SOCK_RAW。
SOCK_STREAM對應於TCP。
SOCK_DGRAM對應於UDP。
SOCK_RAW稱為原始Socket,可以讀寫ICMP、IGMP、IP報文。前兩種類型使用得最多。
第三個參數protocol指定所使用的協議。對於SOCK_STREAM、SOCK_DGRAM兩種類型的Socket,該參
數為0,對於原始Socket才需要指定具體的協議。
4 struct sockaddr_in saddr;
sockaddr_in是定義了socket發送和接收數據包的地址的結構體,有四個字段,含義如下
第一個參數short sin_family,指定應用程序使用的通信協議的協議族,af一般置為AF_INET(表示internetwork: UDP, TCP, etc.Internetwork Version 4);
第二個參數u_short sin_port,代表程序使用的IP地址端口,由程序員指定;
第三個參數struct in_addr sin_addr中的s_addr,用於設置IP地址;
第四個參數char sin_zero[8],是為了保證sockaddr_in與SOCKADDR類型的長度相等而填充進來的字段。
例如
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);//使用的端口號
saddr.sin_addr.s_addr = INADDR_ANY;//任意地址均可以,這樣任意客戶端都可以訪問到服務器
5 ::bind(tcp_socket, (const struct sockaddr*)&saddr, sizeof(saddr))
這裡使用::表示的位於全局作用域下的bind,由於我之前使用了using namespace std;所以如果沒有使用::,它會使用std下的bind,出現一系列的錯誤
bind函數用來將一個socket套接字綁定到一個地址,很多函數會隱式的調用bind函數。
bind的函數原型如下
int bind(SOCKET s,const struct sockaddr FAR * name,int namelen);
第一個參數指定待綁定的Socket描述符;
第二個參數指綁定到的地址結構,即一個sockaddr類型的數據;
第三個參數指對應的是地址的大小;
如果bind錯誤,返回-1,
例如
if (::bind(tcp_socket, (const struct sockaddr*)&saddr, sizeof(saddr)) == -1)//綁定到tcp_socket,使用saddr的地址結構,該地址的大小為sizeof(saddr),
{
cerr << "bind error" << endl;
return -1;
}
6 ::listen(tcp_socket, 1)
如果作為一個服務器,在調用socket()、bind()之後需要調用listen()來監聽這個socket,如果客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請求。
listen的函數原型如下
int listen(SOCKET s,int backlog);
第一個參數為要監聽的socket描述字;
第二個參數為相應socket可以排隊的最大連接個數。()(當客戶鏈接請求大於這數時(即緩沖池滿),其它的未進入鏈接緩沖池的客戶端在tcp層上tcp模塊會自動重新鏈接,直到超時(大約57秒後))
如果listen錯誤,返回-1,