window socketAPI

windows socket函数详解

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结构体如下

View Code

 sockaddr内部使用数组来参数,赋值不方便,一般使用格式化后的结构体SOCKADDR_IN来赋值。

SOCKADDR_IN结构体

View Code

 SOCKADDR_IN里面又包含了in_addr 结构体

View Code

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

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows 环境下,C 语言 TCP socket 客户端的实现也是通过一组 Windows Sockets (Winsock) API 来实现的。下面是一个简单的 Windows 环境下的 TCP socket 客户端的代码示例: ```c #include <stdio.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") // 需要链接的库文件 #define PORT 8080 int main(int argc, char const *argv[]) { WSADATA wsa; SOCKET sock = INVALID_SOCKET; struct sockaddr_in serv_addr; char *hello = "Hello from client"; char buffer[1024] = {0}; // 初始化 Winsock if (WSAStartup(MAKEWORD(2,2), &wsa) != 0) { printf("WSAStartup failed. Error Code : %d", WSAGetLastError()); return 1; } // 创建 socket if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { printf("socket creation failed. Error Code : %d", WSAGetLastError()); WSACleanup(); return 1; } // 指定服务器地址和端口号 memset(&serv_addr, '0', sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { printf("Invalid address/ Address not supported"); WSACleanup(); return 1; } // 连接服务器 if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("connection failed"); WSACleanup(); return 1; } // 向服务器发送数据 send(sock, hello, strlen(hello), 0); printf("Hello message sent\n"); // 从服务器读取数据 recv(sock, buffer, 1024, 0); printf("%s\n", buffer); // 关闭 socket closesocket(sock); // 清理 Winsock WSACleanup(); return 0; } ``` 这个客户端程序首先初始化 Winsock,然后创建一个 socket,指定服务器地址和端口号,并连接到服务器。一旦连接成功,它就会向服务器发送数据,并从服务器读取数据。这只是一个简单的示例,你可以根据自己的需求来修改和扩展它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值