为什么要采用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. disconf-自动注入属性变化

    disconf-自动注入属性变化 disconf官网:http://disconf.readthedocs.io/zh_CN/latest/ 自动注入属性:http://disconf.readthe ...

  2. Windows系统内存分析工具的介绍

      Windows系统内存分析工具的介绍(进程管理器,资源管理器,性能监视器, VMMap, RamMap,PoolMon) 微软官方提供多种工具来分析Windows 的内存使用情况,除了系统自带的任 ...

  3. form表单自动回车提交

    对于使用了submit按钮的form表单,浏览器会直接建立回车与submit按钮之间的关联

  4. Android版-微信APP支付

    首发地址: Android版-微信APP支付 欢迎留言.转发 微信极速开发系列文章(微信支付.授权获取用户信息等):点击这里 目录 1.注册账号.开发者认证 2.添加应用 3.申请微信支付 4.技术开 ...

  5. 理解linux sed命令

    理解linux sed命令(2010-02-27 18:21:20) 标签:linuxshellsed替换 分类:革命本钱 1. Sed简介sed是一种在线编辑器,它一次处理一行内容.处理时,把当 前 ...

  6. Android:安装时提示:INSTALL_FAILED_INSUFFICIENT_STORAGE

    在将程序发布到手机上时提示该错误: INSTALL_FAILED_INSUFFICIENT_STORAGE 解决方法: 1. adb shell 2. #df # df df Filesystem   ...

  7. ZH奶酪:AngularJS/JavaScript上传图片【PC端】

    [功能介绍] 类似与修改个人信息的时候,点击头像,就可以完成选择照片.上传照片等步骤达到替换头像的目的. [运行流程] (1)点击头像 (2)选择头像 (3)点击“完成”,上传头像 1.HTML图片部 ...

  8. scala中Option和Some

    Option的解释: Represents optional values. Instances of Option are either an instance of scala.Some or t ...

  9. [置顶] 单例模式lua实现

    --[[ 优点 一.实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例. 二.灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程. 缺点 一.开销 ...

  10. 很好的vmware目录

    http://www.globalknowledge.com/training/course.asp?pageid=9&courseid=18023&country=United+St ...