JK转至:https://www.cnblogs.com/hgwang/p/6074038.html
流程图:
0:函数库头文件
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")
1:WSAStartup 初始化Ws2_32.dll的函数
WSAStartup 函数用于初始化供进程调用的Winsock相关的dll。
1 int WSAStartup( 2 __in WORD wVersionRequested, 3 __out LPWSADATA lpWSAData 4 );
Parameters
- wVersionRequested
-
标识了用户调用的Winsock的版本号。高字节指明辅版本编号,低字节指明主版本编号。通常使用MAKEWORD来生成一个版本号。 当前Winsock sockets的版本号为2.2,用到的dll是 Ws2_32.dll。
- lpWSAData
-
指向WSADATA结构体的指针,lpWSAData返回了系统对Windows Sockets 的描述。
Return Value
如果调用成功,WSAStartup 函数返回0。否则,将返回五种错误代码之一。但绝对不能使用WSAGetLastError获取错误代码。
WSAData wsa; if (::WSAStartup(MAKEWORD(2,2),&wsa) != 0) { cout<<"WSAStartup error"<<endl; return 0; }
2:WSACleanup 释放Ws2_32.dl的l函数
该函数释放对Winsock链接库的调用。
int WSACleanup(void);
返回值0表示正常退出,返回值SOCKET_ERROR表示异常。返回值是SOCKET_ERROR,可以调用 WSAGetLastError.查看错误代码。需要注意的是,在多线程环境下,WSACleanup 函数将终止所有线程的socket操作。
3:socket 创建socket的函数
socket函数将创建指定传输服务的socket。
1 SOCKET WSAAPI socket( 2 __in int af, 3 __in int type, 4 __in int protocol 5 );
Parameters
af ( address family)
指明地址簇类型,常用的地址簇如下,其余地址簇在Winsock2.h中定义。
AF_UNSPEC(未指明)、
AF_INET(IPv4)、
AF_NETBIOS(NETBIOS地址簇)、
AF_INET6(IPv6)、
AF_IRDA(Infrared Data Association (IrDA)地址簇)、
AF_BTM(Bluetooth)。
type
指明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
指明数据传输协议,该参数取决于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与地址关联
bind函数将socket关联一个本地地址。
1 int bind( 2 __in SOCKET s, 3 __in const struct sockaddr* name, 4 __in int namelen 5 );
Parameters
s
指定一个未绑定的socket。
name
指向sockaddr地址的指针,该结构含有IP和PORT
namelen
参数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 }
sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序(HBO)。
二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。
sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。
题外话,两个函数 htons() 和 inet_addr()。
htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)
各个机器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地址的转换。
inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。
inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。
printf("%s",inet_ntoa(mysock.sin_addr));
htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是两个字节,16位的(short)。
与htonl()和htons()作用相反的两个函数是:ntohl()和ntohs()。
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: https://blog.csdn.net/gqingmo/article/details/52231866
6:connect 客户端请求服务端连接
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 发送数据
函数原型: int send(SOCKET S, const char *buf , int len, int flags)
例子:
char buf[1024] = {"abcdefghijklmnopqrstuvwxyz"};
int sent = 0;//已发送
int total_size = sizeof(buff);
sent = send(sock, buff+sent, total_size - sent, 0);
返回值:
(1),无错:返回发送字节数, 可能小于len;
(2)、出错:返回SOCKET_ERROR,可通过WSAGetLastError获取错误码
int send( __in SOCKET s, __in const char* buf, __in int len, __in int flags );
int recv(
__in SOCKET s,
__out char* buf,
__in int len,
__in int flags
);
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出错。
如果接收缓冲区没有数据可接收,则recv将阻塞直至有数据到达。