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 sockets 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参数一样。