两中模式执行I/O操作
阻塞模式:执行I/O的Winsock调用(如send和recv),一直到操作完成才返回。
非阻塞模式:Winsock函数会立即返回
1.select(选择)模型
1>增加一个套接字集合fd
2>设置一个可读或者可写的套接字集合fd1
3>使用select函数(可设置等待的时长)获得有I/O操作的套接字集合fd1
4>处理这些I/O
可以理解为:假设有10个快递要取,我隔断时间就去快递站去看有没有我的快递,如果有我就拿回来,如果没有我也立刻回来,重复如此。
2.WSAAsyncSelect模型(异步选择模型)(需要建立一个窗口用于接收消息)
以windows消息的形式接收网络事件通知
可以理解为:我不需要隔段时间就去看快递有没有到,如果快递到了,快递公司打电话给我,我再去拿快递
3.WSAEventSelect模型(事件选择模型)
接受网络事件,不是依靠Windows消息驱动机制,而是经由事件对象句柄通知
1>创建一个事件句柄表和一个对应的套接字句柄表
2>每创建一个套接字就创建一个事件对象,把它们的句柄分别放入到上面的两个表中,并调用WSAEventSelect添加它们的关联
3>调用WSAWaitForMultipleEvents在所有事件对象上等待,以便确认哪些套接字上发生了网络事件
4>处理发生的网络事件,继续在事件对象上等待
可以理解为:有点类似WSAAsyncSelect模型,但是它不需要消息通知,它将套接字和网络事件及事件对象绑定了在一起。就相当于我在快递站安装了一个监测器,如果快递到了,检测器就会发出通知,就不用快递公司一个一个的打电话。
4.重叠(Overlapped)I/O模型
由于读写操作太慢,应用程序不想等待,可以设置一个缓冲区,让操作系统完成读写操作,等完成了再来通知应用程序
可以理解为:快递公司直接将快递送到我家门口,到我家了再通知我开门签收,误区自己去快递公司
5.完成端口模型(iocp)
IOCP采用了线程池(提前创建好线程,更有效)+队列+重叠结构的内核机制完成任务
1>创建完成端口对象
2>创建一个或者多个工作线程(I/O服务线程)
3>将套接字关联到完成端口对象
4>套接字向完成端口提交各种所需请求
5>在完成端口上等待的线程池处理这些I/O
#include"CInitSock.h"
CInitSock init;
#define BUFFER_SIZE 1024
typedef struct _PER_HANDLE_DATA
{
SOCKET s;
sockaddr_in addr;
}PER_HANDLE_DATA,*PPER_HANDLE_DATA;
typedef struct _PER_IO_DATA
{
OVERLAPPED ol;
char buf[BUFFER_SIZE];
int nOperationType;
#define OP_READ 1
#define OP_WRITE 2
#define OP_ACCEPT 3
}PER_IO_DATA, *PPER_IO_DATA;
DWORD WINAPI ServerThread(LPVOID lpParam);
int main()
{
int nPort = 4567;
HANDLE hCompletion = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);
SOCKET sListen = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN si;
si.sin_family = AF_INET;
si.sin_port = ntohs(nPort);
si.sin_addr.S_un.S_addr = INADDR_ANY;
bind(sListen, (sockaddr*)&si, sizeof(si));
listen(sListen, 5);
while (TRUE)
{
SOCKADDR_IN saRemote;
int nRemoteLen = sizeof(saRemote);
SOCKET sNew = accept(sListen, (sockaddr*)&saRemote, &nRemoteLen);
cout << "接收到" <<inet_ntoa(saRemote.sin_addr) << "的连接" << endl;
PPER_HANDLE_DATA pPerHandle = (PPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
pPerHandle->s=sNew;
memcpy(&pPerHandle->addr, &saRemote, nRemoteLen);
CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);
PPER_IO_DATA pPerIO = (PPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
pPerIO->nOperationType = OP_READ;
WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
DWORD dwRecv;
DWORD dwFlags = 0;
WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);
}
}
DWORD WINAPI ServerThread(LPVOID lpParam)
{
HANDLE hCompletion = (HANDLE)lpParam;
DWORD dwTrans;
PPER_HANDLE_DATA pPerHandle;
PPER_IO_DATA pPerIO;
while (TRUE)
{
BOOL bOK = GetQueuedCompletionStatus(hCompletion, &dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIO, WSA_INFINITE);
if (!bOK)
{
closesocket(pPerHandle->s);
GlobalFree(pPerHandle);
GlobalFree(pPerIO);
continue;
}
if (dwTrans == 0 && (pPerIO->nOperationType == OP_READ || pPerIO->nOperationType == OP_WRITE))
{
closesocket(pPerHandle->s);
GlobalFree(pPerHandle);
GlobalFree(pPerIO);
continue;
}
switch (pPerIO->nOperationType)
{
case OP_READ:
{
pPerIO->buf[dwTrans] = '\0';
cout << inet_ntoa(pPerHandle->addr.sin_addr) << ":";
cout << pPerIO->buf<<endl;
WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
pPerIO->nOperationType = OP_READ;
DWORD nFlags = 0;
WSARecv(pPerHandle->s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);
}
break;
case OP_WRITE:
case OP_ACCEPT:break;
}
}
return 0;
}