windows socket函數詳解
近期一直用第三方庫寫網絡編程,反倒是遺忘了網絡編程最底層的知識。因而產生了整理Winsock函數庫的想法。以下知識點均來源於MSDN,本人只做翻譯工作。雖然很多前輩都做已了此類工作,但親力親為總記得清楚點。
0:函數庫頭文件
1 #include <WinSock2.h> 2 #pragma comment(lib,"Ws2_32.lib ")
1:WSAStartup 初始化Ws2_32.dll的函數
description:The WSAStartup function initiates use of the Winsock DLL by a process.
WSAStartup 函數用於初始化供進程調用的Winsock相關的dll。
1 int WSAStartup( 2 __in WORD wVersionRequested, 3 __out LPWSADATA lpWSAData 4 );
Parameters
- wVersionRequested
-
The highest version of Windows Sockets specification that the caller can use. The high-order byte specifies the minor version number; the low-order byte specifies the major version number.
The current version of the Windows Sockets specification is version 2.2. The current Winsock DLL, Ws2_32.dll, supports applications that request any of the following versions of Windows Sockets specification,1.0、1.1、2.0、2.1、2.2。
標識了用戶調用的Winsock的版本號。高字節指明輔版本編號,低字節指明主版本編號。通常使用MAKEWORD來生成一個版本號。 當前Winsock sockets的版本號為2.2,用到的dll是 Ws2_32.dll。
- lpWSAData
-
A pointer to the WSADATA data structure that is to receive details of the Windows Sockets implementation.
指向WSADATA結構體的指針,lpWSAData返回了系統對Windows Sockets 的描述。
Return Value
If successful, the WSAStartup function returns zero. Otherwise, it returns one of the error codes listed below.
WSAVERNOTSUPPORTED、WSAVERNOTSUPPORTED、WSAEINPROGRESS、WSAEPROCLIM、WSAEFAULT。
The WSAStartup function directly returns the extended error code in the return value for this function. A call to the WSAGetLastError function is not needed and should not be used.
如果調用成功,WSAStartup 函數返回0。否則,將返回五種錯誤代碼之一。但絕對不能使用WSAGetLastError獲取錯誤代碼。
1 WSAData wsa;
2 if (::WSAStartup(MAKEWORD(2,2),&wsa) != 0) 3 { 4 cout<<"WSAStartup error"<<endl; 5 return 0; 6 }
2:WSACleanup 釋放Ws2_32.dl的l函數
description:The WSACleanup function terminates use of the Winsock 2 DLL (Ws2_32.dll).
該函數釋放對Winsock鏈接庫的調用。
int WSACleanup(void);
Return Value
The return value is zero if the operation was successful. Otherwise, the value SOCKET_ERROR is returned, and a specific error number can be retrieved by calling WSAGetLastError.
In a multithreaded environment, WSACleanup terminates Windows Sockets operations for all threads.
返回值0表示正常退出,返回值SOCKET_ERROR表示異常。返回值是SOCKET_ERROR,可以調用 WSAGetLastError.查看錯誤代碼。需要注意的是,在多線程環境下,WSACleanup 函數將終止所有線程的socket操作。
3:socket 創建socket的函數
description:The socket function creates a socket that is bound to a specific transport service provider。
socket函數將創建指定傳輸服務的socket。
1 SOCKET WSAAPI socket( 2 __in int af, 3 __in int type, 4 __in int protocol 5 );
Parameters
af ( address family)
The address family specification. Possible values for the address family are defined in the Winsock2.h header file。 The table below lists common values for address family although many other values are possible。
指明地址簇類型,常用的地址簇如下,其余地址簇在Winsock2.h中定義。
AF_UNSPEC(未指明)、AF_INET(IPv4)、AF_NETBIOS(NETBIOS地址簇)、AF_INET6(IPv6)、AF_IRDA(Infrared Data Association (IrDA)地址簇)、AF_BTM(Bluetooth)。
type
The type specification for the new socket. Possible values for the socket type are defined in the Winsock2.h header file.The following table lists the possible values for the type parameter supported for Windows Sockets 2:
指明socket的類型,Windows Sockets 2常見類型如下:
SOCK_STREAM(流套接字,使用TCP協議)、SOCK_DGRAM(數據報套接字,使用UDP協議)、SOCK_RAW(原始套接字)、SOCK_RDM(提供可靠的消息數據報文,reliable message datagram)、SOCK_SEQPACKET(Provides a pseudo-stream packet based on datagrams,在UDP的基礎上提供了偽流數據包)。
protocol
The protocol to be used. The possible options for the protocol parameter are specific to the address family and socket type specified. Possible values for the protocol are defined are defined in the Winsock2.h and Wsrm.h header files.
指明數據傳輸協議,該參數取決於af和type參數的類型。protocol參數在Winsock2.h and Wsrm.h定義。通常使用如下3中協議:
IPPROTO_TCP(TCP協議,使用條件,af是AF_INET or AF_INET6、type是SOCK_STREAM )
IPPROTO_UDP(UDP協議,使用條件,af是AF_INET or AF_INET6、type是SOCK_DGRAM)
IPPROTO_RM(PGM(Pragmatic General Multicast,實際通用組播協議)協議,使用條件,af是AF_INET 、type是SOCK_RDM)
Return Value
If no error occurs, socket returns a descriptor referencing the new socket. Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.
如果不出錯,socket函數將返回socket的描述符(句柄),否則,將返回INVALID_SOCKET。
1 SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 2 if (s == INVALID_SOCKET) 3 { 4 int er = WSAGetLastError(); 5 return 0; 6 }
4:bind 服務端將socket與地址關聯
description:The bind function associates a local address with a socket.
bind函數將socket關聯一個本地地址。
1 int bind( 2 __in SOCKET s, 3 __in const struct sockaddr* name, 4 __in int namelen 5 );
Parameters
s
Descriptor identifying an unbound socket.指定一個未綁定的socket。
name
Address to assign to the socket from the sockaddr structure.
指向sockaddr地址的指針,該結構含有IP和PORT
namelen
Length of the value in the name parameter, in bytes。
參數name的字節數。
Return Value
If no error occurs, bind returns zero. Otherwise, it returns SOCKET_ERROR, and a specific error code can be retrieved by calling WSAGetLastError.
無錯誤返回0,又錯誤返回SOCKET_ERROR。
1 sockaddr_in service; 2 service.sin_family = AF_INET; 3 service.sin_addr.s_addr = inet_addr("127.0.0.1"); 4 service.sin_port = htons(27015); 5 6 //---------------------- 7 // Bind the socket. 8 if (bind( ListenSocket, (SOCKADDR*) &service,sizeof(service)) == SOCKET_ERROR) { 9 closesocket(ListenSocket); 10 return; 11 }
附注:地址轉換與賦值的相關操作
sockaddr結構體如下

sockaddr內部使用數組來參數,賦值不方便,一般使用格式化后的結構體SOCKADDR_IN來賦值。
SOCKADDR_IN結構體

SOCKADDR_IN里面又包含了in_addr 結構體

htons(host to unsigned short)和htonl(host to unsigned long)
各個機器cpu對數據存儲和表示的方法不通,intel機器用littele-endian存數據,而IBM機器用big-endian存數據。網絡協議為取消這種差異,一致采用big-endian方式。htons用於將unsigned short的數值從littele-endian轉換為big-endian,由於short只有2字節,常用語port數值的轉換。htons用於將unsigned long的數值從littele-endian轉換為big-endian,long有4字節,常用於ipv4地址的轉換。
1 u_short htons( __in u_short hostshort); 2 u_long htonl(__in u_long hostlong);
inet_addr和inet_ntoa
inet_addr用於將ipv4格式的字符串轉換為unsigned long的數值。inet_ntoa用於將struct in_addr的地址轉換為ipv4格式的字符串。
1 unsigned long inet_addr( __in const char* cp); 2 char* FAR inet_ntoa( __in struct in_addr in);
INADDR_ANY
數值為0。很多帖子對這個值的理解不一。我查了一下CMU(卡耐基梅隆大學cs課程,深入理解計算機系統作者是該校計算機學院院長)的資料,看到如下解釋:
源自:https://www.cs.cmu.edu/~srini/15-441/F01.full/www/assignments/P2/htmlsim_split/node18.html
用INADDR_ANY來配置IP地址,意味着不需要知道當前服務器的IP地址。對於多網卡的服務器,INADDR_ANY允許你的服務接收一個服務器上所有網卡發來的數據。下面例子也有寫,如果某個socket使用INADDR_ANY和8000端口,那么它將接收該所有網卡傳來的數據。而其他socket將無法再使用8000端口。
1 sockaddr_in service; 2 ZeroMemory((char *)&service,sizeof(sockaddr_in)); 3 service.sin_family = AF_INET; 4 //service.sin_addr.S_un.S_addr =/*INADDR_ANY*/ inet_addr("127.0.0.1"); 5 service.sin_addr.s_addr = INADDR_ANY; 6 service.sin_port = htons(8278); 7 if (bind(s,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR) 8 { 9 int er = WSAGetLastError(); 10 closesocket(s); 11 }
4:listen 服務端網絡監聽
description:The listen function places a socket in a state in which it is listening for an incoming connection
int listen( __in SOCKET s, __in int backlog );
Parameters
- s
- Descriptor identifying a bound, unconnected socket.
- socket描述符,該socket是一個未連接狀態的socket
- backlog
- Maximum length of the queue of pending connections. If set to SOMAXCONN, the underlying service provider responsible for socket s will set the backlog to a maximum reasonable value. There is no standard provision to obtain the actual backlog value.
- 掛起連接的最大長度,如果該值設置為SOMAXCONN,負責socket的底部服務提供商將設置該值為最大合理值。並沒有該值的明確規定。
- Return Value
- If no error occurs, listen returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
- 沒有錯誤發生將返回0,否則返回SOCKET_ERROR
1 if (listen(s,SOMAXCONN ) == SOCKET_ERROR)
2 { 3 int er = WSAGetLastError(); 4 closesocket(s); 5 }
5:accept 服務端connect接收
description:The accept function permits an incoming connection attempt on a socket.
1 SOCKET accept( 2 __in SOCKET s, 3 __out struct sockaddr* addr, 4 __in_out int* addrlen 5 );
Parameters
- s
-
A descriptor that identifies a socket that has been placed in a listening state with the listen function. The connection is actually made with the socket that is returned by accept.
listen函數用到的socket。accept函數將創建連接。
- addr
-
An optional pointer to a buffer that receives the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family that was established when the socket from the sockaddr structure was created.
指向通信層連接實體地址的指針。addr 的格式取決於bind函數內地址簇的類型。
- addrlen
-
An optional pointer to an integer that contains the length of structure pointed to by the addr parameter.
addr的長度。
Return Value
If no error occurs, accept returns a value of type SOCKET that is a descriptor for the new socket. This returned value is a handle for the socket on which the actual connection is made.
Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.
The integer referred to by addrlen initially contains the amount of space pointed to by addr. On return it will contain the actual length in bytes of the address returned.
如果不發生錯誤,accept將返回一個新的SOCKET描述符,即新建連接的socket句柄。否則,將返回INVALID_SOCKET。傳進去的addrlen應該是參數addr的長度,返回的addrlen是實際長度。
1 SOCKET ac = accept(s,NULL,NULL); 2 if (ac == INVALID_SOCKET) 3 { 4 int er = WSAGetLastError(); 5 closesocket(s); 6 }
如果不發生錯誤,accept將返回一個新的SOCKET描述符,即新建連接的socket句柄。否則,將返回INVALID_SOCKET。傳進去的addrlen應該是參數addr的長度,返回的addrlen是實際長度
6:connect 客戶端請求服務端連接
description:The connect function establishes a connection to a specified socket
1 int connect( 2 __in SOCKET s, 3 __in const struct sockaddr* name, 4 __in int namelen 5 );
Parameters
- s
-
Descriptor identifying an unconnected socket.
- name
-
Name of the socket in the sockaddr structure to which the connection should be established.
與bind函數的name參數類似,指明待連接的地址
- namelen
-
Length of name, in bytes.
Return Value
If no error occurs, connect returns zero. Otherwise, it returns SOCKET_ERROR, and a specific error code can be retrieved by calling WSAGetLastError.
On a blocking socket, the return value indicates success or failure of the connection attempt.
0表示正確,否則,將返回SOCKET_ERROR。如果是阻塞式的socket連接,返回值代表了連接正常與失敗。
1 sockaddr_in server; 2 server.sin_family = AF_INET; 3 server.sin_port = htons(8828); 4 server.sin_addr.s_addr = inet_addr("127.0.0.1"); 5 if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR) 6 { 7 break; 8 }
7:send、recv 發送接收數據
description:The send function sends data on a connected socket。The recv function receives data from a connected or bound socket.
1 int send( 2 __in SOCKET s, 3 __in const char* buf, 4 __in int len, 5 __in int flags 6 );
1 int recv( 2 __in SOCKET s, 3 __out char* buf, 4 __in int len, 5 __in int flags 6 );
Parameters
- s
-
A descriptor identifying a connected socket.
- buf
-
A pointer to a buffer containing the data to be transmitted.
數據buffer
- len
-
The length, in bytes, of the data in buffer pointed to by the buf parameter.
待發送(接受)數據長度
- flags
-
A set of flags that specify the way in which the call is made. This parameter is constructed by using the bitwise OR operator with any of the following values.
send(recv)函數的發送(接收)數據方式。MSDN給了以下幾種發送方式:
1:MSG_DONTROUTE(Specifies that the data should not be subject to routing. A Windows Sockets service provider can choose to ignore this flag.)
2:MSG_OOB(Sends OOB data (stream-style socket such as SOCK_STREAM only)
3:0
Return Value
If no error occurs, send returns the total number of bytes sent, which can be less than the number requested to be sent in the len parameter. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
send的返回值標識已發送數據的長度,這個值可能比參數len小,這也意味着數據緩沖區沒有全部發出去,要進行后續處理。返回SOCKET_ERROR標識send出錯。
If no error occurs, recv returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
recv的返回值標識已接收數據的長度。如果連接已關閉,返回值將是0。返回SOCKET_ERROR標識recv出錯。
客戶端例子:
1 sockaddr_in server; 2 server.sin_family = AF_INET; 3 server.sin_port = htons(8828); 4 server.sin_addr.s_addr = inet_addr("127.0.0.1"); 5 if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR) 6 { 7 break; 8 } 9 str += "windows socket test!"; 10 while (1) 11 { 12 int len = send(cnetsocket,str.c_str(),str.length(),0); 13 cout<<"send data:"<<str.c_str()<<" ,length = "<<str.length()<<endl; 14 if (len < str.length()) 15 { 16 cout<<"data send uncompleted"<<endl; 17 str = str.substr(len+1,str.length()); 18 len = send(cnetsocket,str.c_str(),str.length(),0); 19 cout<<"send data uncomplete,send remaining data :"<<str.c_str()<<" ,length = "<<str.length()<<endl; 20 } 21 else if (len == SOCKET_ERROR) 22 { 23 break; 24 } 25 Sleep(1); 26 }
服務端例子:
1 SOCKET acp = accept(listensocket,NULL,NULL); 2 if (acp == INVALID_SOCKET) 3 { 4 cout<<"accept error,error code "<<WSAGetLastError()<<endl; 5 break; 6 } 7 while (1) 8 { 9 char buf[1024]; 10 int len = recv(acp,buf,1024,0); 11 if (len == 0) 12 { 13 cout<<"connection has been closed "<<endl; 14 break; 15 } 16 else if (len == SOCKET_ERROR) 17 { 18 cout<<"recv error,error code "<<WSAGetLastError()<<endl; 19 break; 20 } 21 else 22 { 23 char* outbuf = new char[len+1]; 24 memcpy(outbuf,buf,len); 25 outbuf[len] = 0; 26 cout<<"recv data,"<<outbuf<<endl; 27 delete outbuf; 28 } 29 }
8:closesocket 關閉socket
description:The closesocket function closes an existing socket.
1 int closesocket( 2 __in SOCKET s 3 );
Parameters
- s
-
A descriptor identifying the socket to close.
Return Value
If no error occurs, closesocket returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
如果無錯誤發生,函數返回0。否則,返回SOCKET_ERROR。
9:shutdown 禁止在socket收發數據
description:The shutdown function disables sends or receives on a socket.
1 int shutdown( 2 __in SOCKET s, 3 __in int how 4 );
Parameters
- s
-
Descriptor identifying a socket.
- how
-
Flag that describes what types of operation will no longer be allowed.
how有幾類取值:
1:SD_RECEIVE,不允許在socket上recv數據。
2:SD_RECEIVE,不允許在socket上send數據。
3:SD_RECEIVE,不允許在socket上recv和send數據。
Return Value
If no error occurs, shutdown returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
shutdown並不關閉socket,只是禁止掉socket的recv或send行為。為保證數據收發完整性,在關閉socket之前應調用shutdown。
10 :getsockname 獲取本地IP和PORT
Description:The getsockname function retrieves the local name for a socket.
1 int getsockname( 2 __in SOCKET s, 3 __out struct sockaddr* name, 4 __in_out int* namelen 5 );
Parameters
- s
-
Descriptor identifying a socket.
- name
-
Pointer to a SOCKADDR structure that receives the address (name) of the socket.
- namelen
-
Size of the name buffer, in bytes.
Return Value
If no error occurs, getsockname returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
11 :getpeername 獲取對端IP和PORT
1 int getpeername( 2 __in SOCKET s, 3 __out struct sockaddr* name, 4 __in_out int* namelen 5 );
Getpeername和getsockname參數一樣。