为什么要采用Socket模型,而不直接使用Socket?

原因源于recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他链接不能继续。这样我们又想到用多线程来实现,每个socket链接使用一个线程,这样效率十分低下,根本不可能应对负荷较大的情况。于是便有了各种模型的解决方法,总之都是为了实现多个线程同时访问时不产生堵塞。

完成端口(IOCP)模型:

首先来说为什么要使用完成端口:原因还是因为为了解决recv方法为阻塞式的问题,WinSocket封装的WSARecv方法为非堵塞的方法。

          int WSARecv(

                      SOCKET s,

                      LPWSABUF lpBuffers,

                      DWORD dwBufferCount,

                      LPDWORD lpNumberOfBytesRecvd,

                      LPDWORD lpFlags,

                      LPWSAOVERLAPPED lpOverlapped,

                      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

                      );

WSARecv为非阻塞的方法,其中第二个参数是I/O请求成功时,数据保存的地址。

Socket的触发是属于网卡硬件的中断信号,只是此信号CPU不能直接获取状态,此时我们可以使之绑定Event事件,Event内核对象的状态时可以监听到的。这也就是WSAEventSelect模型的原理,当然重叠模型的最终原理也是如此。但Event的方法有着其弊病:当模型处理多线程事件时要调用WSAWaitForMultipleEvents函数,WSAWaitForMultipleEvents函数一次最多只能等待64个事件对象。所以当海量客户端连接服务器时,服务器将没有能力应对,于是我们使用完成端口。

    完成端口:

HANDLE CreateIoCompletionPort(

                                               HANDLE FileHandle,             //要链接的Socket

                                               HANDLE ExistingCompletionPort,     //全局完成端口

                  //同完成端口关联到一起的句柄,此处可为链接的socket,或是id等等(目地使接收到的socket知道是哪个socket)

                                               DWORD CompletionKey,             

                                               DWORD NumberOfConcurrentThreads

                 );

此函数创建创建Socket与完成端口的链接,CreateIoCompletionPort函数被用于完成两个工作:

  • 用于创建—个完成端口对象。
  • 将一个句柄同完成端口关联到一起。

用函数GetQueuedCompletionStatus等待全局完成端口的完成队列:

      BOOL     GetQueuedCompletionStatus(
                  HANDLE         CompletionPort,
                  LPDWORD      lpNumberOfBytes,
                 PULONG_PTR  lpCompletionKey, //此参数为CreateIoCompletionPort第三个参数传过来的句柄,通过此参数获得socket
                  LPOVERLAPPED* lpOverlapped,
                  DWORD         dwMilliseconds
                  );

   完成端口的工作原理是,把Socket和完成端口绑定,通过关联句柄传递传递参数,使得获取到的Socket能得知是那个socket,参数可以自定义可以是socket本身也可以是id等等。

#include "WinSock2.h"
#pragma comment(lib, "ws2_32.lib") #define MESSAGESIZE 1024 SOCKET serverSocket;
DWORD WINAPI SocketProcAccept(LPVOID pParam);
DWORD WINAPI SocketProcMain(LPVOID pParam); enum SOCKETOPERATE
{
soREVC
}; struct SOCKETDATA
{
WSAOVERLAPPED overlapped;
WSABUF buf;
char sMessage[MESSAGESIZE];
DWORD dwBytes;
DWORD flag;
SOCKETOPERATE socketType;
void Clear(SOCKETOPERATE type)
{
ZeroMemory(this, sizeof(SOCKETDATA));
buf.buf = sMessage;
buf.len = MESSAGESIZE;
socketType = type;
}
}; SOCKET CreateServiceSocket(int Port)
{
int iError;
WSAData data;
iError = WSAStartup(0x0202, &data);
SOCKET tmp = socket(AF_INET,SOCK_STREAM,);
if(tmp == INVALID_SOCKET)
{
return INVALID_SOCKET;
} SOCKADDR_IN addr;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(Port);
if((bind(tmp, (sockaddr*)&addr, sizeof(addr))) != )
{
closesocket(tmp);
return INVALID_SOCKET;
} if((listen(tmp, INFINITE)) != )
{
closesocket(tmp);
return INVALID_SOCKET;
} return tmp;
} int _tmain(int argc, _TCHAR* argv[])
{
HANDLE CP = INVALID_HANDLE_VALUE;
CP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, , );
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
for (int i = ; i<systemInfo.dwNumberOfProcessors; i++)
{
CreateThread(NULL, NULL, &SocketProcMain, CP, NULL, NULL);
}
serverSocket = CreateServiceSocket();
if (serverSocket == INVALID_SOCKET)
{
return ;
} CreateThread(NULL, NULL, &SocketProcAccept, CP, NULL, NULL); while()
{
Sleep();
}
CloseHandle(CP);
closesocket(serverSocket);
WSACleanup();
return ;
} DWORD WINAPI SocketProcAccept(LPVOID pParam)
{
HANDLE CP = (HANDLE)pParam;
SOCKADDR_IN addr;
int len = sizeof(SOCKADDR_IN);
SOCKET tmp;
SOCKETDATA *lpSocketData;
while()
{
tmp = accept(serverSocket, (sockaddr*)&addr, &len);
printf("Client Accept:%s\t:%d\n", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
CreateIoCompletionPort((HANDLE)tmp, CP, (DWORD)tmp, INFINITE);
lpSocketData = (SOCKETDATA *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKETDATA));
lpSocketData->Clear(soREVC);
WSARecv(tmp, &lpSocketData->buf, ,&lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);
}
} DWORD WINAPI SocketProcMain(LPVOID pParam)
{
HANDLE CP = (HANDLE)pParam;
SOCKADDR_IN addr;
DWORD dwBytes;
SOCKETDATA *lpSocketData;
SOCKET clientSocket; while()
{
GetQueuedCompletionStatus(CP, &dwBytes, (PULONG_PTR)&clientSocket, (LPOVERLAPPED*)&lpSocketData, INFINITE);
if(dwBytes == 0xFFFFFFFF)
{
return ;
} if(lpSocketData->socketType == soREVC)
{
if(dwBytes == )
{
closesocket(clientSocket);
HeapFree(GetProcessHeap(), , lpSocketData);
}
else
{
lpSocketData->sMessage[dwBytes] = '\0';
printf("%x\t:%s\n", (DWORD)clientSocket, lpSocketData->sMessage);
lpSocketData->Clear(soREVC);
WSARecv(clientSocket, &lpSocketData->buf, , &lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);
}
}
}
}

Socket模型(二):完成端口(IOCP)的更多相关文章

  1. Socket模型详解(转)

    两种I/O模式 一.选择模型 二.异步选择 三.事件选择 四.重叠I/O模型 五.完成端口模型 五种I/O模型的比较 两种I/O模式 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待 ...

  2. 完成端口IOCP详解

    修改自: http://blog.csdn.net/piggyxp/article/details/6922277 ps: 原作者很厉害了, 把一个iocp模型讲解的这么形象,不过在实践过程中发现一些 ...

  3. 手把手教你玩转SOCKET模型之重叠I/O篇(下)

    四.     实现重叠模型的步骤 作 了这么多的准备工作,费了这么多的笔墨,我们终于可以开始着手编码了.其实慢慢的你就会明白,要想透析重叠结构的内部原理也许是要费点功夫,但是只是学会 如何来使用它,却 ...

  4. .NET平台下几种SOCKET模型的简要性能供参考

    转载自:http://www.cnblogs.com/asilas/archive/2006/01/05/311309.html .NET平台下几种SOCKET模型的简要性能供参考 这个内容在cnbl ...

  5. 转:变手把手教你玩转SOCKET模型之重叠I/O篇

    手把手教你玩转SOCKET模型之重叠I/O篇 “身为一个初学者,时常能体味到初学者入门的艰辛,所以总是想抽空作点什么来尽我所能的帮助那些需要帮助的人.我也希望大家能把自己的所学和他人一起分享,不要去鄙 ...

  6. windows下的IO模型之完成端口

    本文整理于:http://blog.csdn.net/piggyxp/article/details/6922277 一. 完成端口的优点 完成端口会充分利用Windows内核来进行I/O的调度,是用 ...

  7. Socket TCP Server一个端口可以有多少个长连接?受到什么影响?linux最大文件句柄数量总结

    Socket TCP Server一个端口可以有多少个长连接? 网上答案很多,不知道那个才是正确的 理论上是无限的 16.Linux中,一个端口能够接受tcp链接数量的理论上限是? A.1024 B. ...

  8. DELPHI中完成端口(IOCP)的简单分析(4)

    DELPHI中完成端口(IOCP)的简单分析(4)   在我以前写的文章中,一直说的是如何接收数据.但是对于如何发送数据却一点也没有提到.因为从代码量上来说接收的代码要比发送多很多.今天我就来写一下如 ...

  9. DELPHI中完成端口(IOCP)的简单分析(3)

    DELPHI中完成端口(IOCP)的简单分析(3)   fxh7622关注4人评论7366人阅读2007-01-17 11:18:24   最近太忙,所以没有机会来写IOCP的后续文章.今天好不容易有 ...

随机推荐

  1. BiLSTM-CRF模型中CRF层的解读

    转自: https://createmomo.github.io/ BiLSTM-CRF模型中CRF层的解读: 文章链接: 标题:CRF Layer on the Top of BiLSTM - 1  ...

  2. 【R】函数-其它实用函数

  3. telnet 163发送邮件

    1.telnet smtp.163.com 25 2. 3.测试成功

  4. Internet传输协议-TCP

    http://phei.eefocus.com/book/08-07/473781276058574.html http://www.eefocus.com/communication/210643 ...

  5. Centos下安装gcc

    虚拟机安装了一个CentOS7,发现没有gcc,通过以下命令安装: yum install gcc yum install gcc-c++

  6. scala VS python2 操作shell对比例子

    Scala: /** * Created by sunxu on 2015/9/30. */ import scala.sys.process._ import java.io.File //在相应目 ...

  7. 如何根据搜索页面内容得到的结果生成该元素的xpath路径

    如何根据搜索页面内容得到的结果生成该元素的xpath路径?

  8. jquery append()方法与html()方法使用方法差别

    append(content):方法在被选元素的结尾(仍然在内部)插入指定内容,有非常多朋友认为append与html差点儿相同.其他从英文意义上append是在原有基础上添加,而html中是替换当前 ...

  9. SpringMVC学习笔记三:拦截器

    一:拦截器工作原理 类比Struts2的拦截器,通过拦截器可以实现在调用controller的方法前.后进行一些操作. 二:拦截器实现 1:实现拦截器类 实现HandlerInterceptor 接口 ...

  10. PgSQL · 源码分析· pg_dump分析

    PostgreSQL本身提供了逻辑导出工具pg_dumpall和pg_dump,其中pg_dumpall导出所有的数据库,pg_dump导出单个数据库,两个工具的用法和参数不再详细介绍,本文从代码层面 ...