对于windows平台的通信模型一般分为两个:客户端使用MFC中的接口、服务器端使用IOCP通信模型。
基础:
在windows中SOCKET是操作系统内部定义的数据结构。
协议族在计算机中表示为一个整数—AF_INET,
Socket类型为SOCK_STREAM和SOCK_DGRAM。
涉及到一些地址相关的类型;
sockaddr:
作为函数参数而存在,在函数调用的时候需要类型转换;
sockaddr_in:
需要配置的数据类型,定义为:
struct sockaddr_in{
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
其中struct in_addr是另外定义的。
相关头文件为:<winsock2.h>,同时需要连接动态库。
初始化动态库:WSAStartup(MAKEWORD(2,2),&wsaData);
其中wsaData为WSADATA类型的变量。
对应的库清理函数为WSACleanup()。
Socket函数原型:SOCKET socket(int af,int type,int protocol);
出错返回INVALID_SOCKET。
常用法为:SOCKET s=socket(AF_INET,SOCK_STREAM,0);
close socket函数:int closesocket(SOCKET s);
成功返回0,否则返回SOCKET_ERROR。
connect函数:int connect(SOCKET s,const struct sockaddr* name,
int namelen);
成功返回0,否则返回SOCKET_ERROR。
对于非阻塞模式为WSAEWOULDBLOCK表示错误。
listen函数:int listen(SOCKET s,int backlog);
成功返回0,否则返回SOCKET_ERROR。
bind函数:int bind(SOCKET s,const struc sockaddr* name,
int namelen);
成功返回0,否则返回SOCKET_ERROR。
accept函数:SOCKET accept(SOCKET s,struct sockaddr* addr,
int * addrlen);
成功返回新连接的SOCKET,否则返回SOCKET_ERROR。
其中addr和addrlen都是额外的返回值。
send函数--阻塞函数:int send(SOCKET s,const cha* buf,int len,
int flags);
成功返回发送成功的字节数,否则返回SOCKET_ERROR。
recv函数--阻塞函数:int recv(SOCKET s,char* buf,int len,int flags);
成功返回接收的字节数,否则返回SOCKET_ERROR。
常用地址设置:SOCKADDR_IN ServerAddr;
ServerAddr.sin_family=AF_INET;
ServerAddr.sin_port=htons(8888);
ServerAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
或ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);
IOCP通信模型:
--基本原理:
IOCP(I/O Completion Port--完成端口)内部采用的是前摄器模式。
我理解的IOCP的工作过程是:
首先需要一个接受新连接的循环:
接受新连接--将新连接绑定到IOCP--执行异步读数据
然后是线程工作的循环:
检查完成的操作--判断完成操作的类型--执行分析判断操作
--执行异步循环
其中执行分析判断操作过程可能有异步写操作;
在绑定socket和完成端口时就交由完成端口来记录socket;
执行异步读取操作则可以保证socket不会丢失。
--数据结构:
重叠端口:是操作系统内部用来在IOCP内部交流的数据结构。
常用法是作为struct结构的首元素--做指针类型转换;
传输数据缓存—WSABUF,用于系统将完成的数据缓存;
同时需要保存关联的SOCKET和记录完成操作的数据的数目;
这些概念说起来很抽象,不过看代码的话就很好理解了。
--接口函数:
主要有两个函数:
1.HANDLE CreateIoCompletionPort(HANDLE FileHandle,
HANDLE ExistingCompletionPort,ULONG_PTR CompletionKey,
DWORD NumberOfConcurrentThreads);
主要作用是创建IOCP和将SOCKET绑定到IOCP。
2.BOOL GetQueuedCompletionStatus(HANDLE CompletinonPort,
LPDWORD lpNumberOfBytes,PULONG_PTR lpCompletionKey,
LPOVERLAPPED* lpOverlapped,DWORD dwMilliseconds);
IOCP内部会管理线程池的,
而线程则需要通过这个函数来查询完成的操作;
--注意事项:
创建线程的数量一般配置为内核的数量或内核数量的2倍;
如果需要深入理解IOCP的话需要学习前摄器模式。