在普通IOCP的基础上注意两点:
1.记得把监听socket绑定到端口
2.在Accept处理过程中,抛出接受连接的AcceptEx请求,绑定客户端socket到端口和抛出recv请求

客户端要断开连接时,只需发送一个大小为0的内容即可。我们在服务器处理时,收到0,就销毁该socket

// IOCP_TCPIP_Socket_Server.cpp#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#include <iostream>
#include <mswsock.h>using namespace std;#pragma comment(lib, "Ws2_32.lib")      // Socket编程需用的动态链接库
#pragma comment(lib, "Kernel32.lib")    // IOCP需要用到的动态链接库#define SEND 0
#define RECV 1
#define ACCEPT 2/**
* 结构体名称:PER_IO_DATA
* 结构体功能:重叠I/O需要用到的结构体,临时记录IO数据
**/
const int DataBuffSize = 2 * 1024;
typedef struct
{OVERLAPPED overlapped;WSABUF databuff;char buffer[DataBuffSize];int BufferLen;int operationType;SOCKET client;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA, *LPPER_IO_DATA, PER_IO_DATA;/**
* 结构体名称:PER_HANDLE_DATA
* 结构体存储:记录单个套接字的数据,包括了套接字的变量及套接字的对应的客户端的地址。
* 结构体作用:当服务器连接上客户端时,信息存储到该结构体中,知道客户端的地址以便于回访。
**/
typedef struct
{SOCKET socket;SOCKADDR_STORAGE ClientAddr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;// 定义全局变量
const int DefaultPort = 5000;
vector < PER_HANDLE_DATA* > clientGroup;        // 记录客户端的向量组
int g_nThread = 0;//开启线程数量
HANDLE hThread[50];//线程句柄SOCKET srvSocket = NULL;
DWORD dwBytes = 0;HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);
DWORD WINAPI ServerSendThread(LPVOID IpParam);LPFN_ACCEPTEX lpfnAcceptEx = NULL;//AcceptEx函数指针
GUID guidAcceptEx = WSAID_ACCEPTEX;
GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockAddrs = NULL;// 开始主函数
int main()
{// 加载socket动态链接库WORD wVersionRequested = MAKEWORD(2, 2); // 请求2.2版本的WinSock库WSADATA wsaData;    // 接收Windows Socket的结构信息DWORD err = WSAStartup(wVersionRequested, &wsaData);if (0 != err) { // 检查套接字库是否申请成功cerr << "Request Windows Socket Library Error!\n";system("pause");return -1;}if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {// 检查是否申请了所需版本的套接字库WSACleanup();cerr << "Request Windows Socket Version 2.2 Error!\n";system("pause");return -1;}// 创建IOCP的内核对象/*** 需要用到的函数的原型:* HANDLE WINAPI CreateIoCompletionPort(*    __in   HANDLE FileHandle,      // 已经打开的文件句柄或者空句柄,一般是客户端的句柄*    __in   HANDLE ExistingCompletionPort,  // 已经存在的IOCP句柄*    __in   ULONG_PTR CompletionKey,    // 完成键,包含了指定I/O完成包的指定文件*    __in   DWORD NumberOfConcurrentThreads // 真正并发同时执行最大线程数,一般推介是CPU核心数*2* );**/HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);if (NULL == completionPort) {   // 创建IO内核对象失败cerr << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;system("pause");return -1;}// 创建IOCP线程--线程里面创建线程池// 确定处理器的核心数量SYSTEM_INFO mySysInfo;GetSystemInfo(&mySysInfo);// 基于处理器的核心数量创建线程for (DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2); ++i) {// 创建服务器工作器线程,并将完成端口传递到该线程HANDLE ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);//第一NULL代表默认安全选项,第一个0,代表线程占用资源大小,第二个0,代表线程创建后立即执行if (NULL == ThreadHandle) {cerr << "Create Thread Handle failed. Error:" << GetLastError() << endl;system("pause");return -1;}hThread[i] = ThreadHandle;++g_nThread;}// 建立流式套接字srvSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);// Associate SOCKET with IOCP  if (NULL == CreateIoCompletionPort((HANDLE)srvSocket, completionPort, NULL, 0)){cout << "CreateIoCompletionPort failed with error code: " << WSAGetLastError() << endl;if (INVALID_SOCKET != srvSocket){closesocket(srvSocket);srvSocket = INVALID_SOCKET;}return -1;}// 绑定SOCKET到本机SOCKADDR_IN srvAddr;srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);srvAddr.sin_family = AF_INET;srvAddr.sin_port = htons(DefaultPort);int bindResult = bind(srvSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));if (SOCKET_ERROR == bindResult) {cerr << "Bind failed. Error:" << GetLastError() << endl;system("pause");return -1;}// 将SOCKET设置为监听模式int listenResult = listen(srvSocket, 10);if (SOCKET_ERROR == listenResult) {cerr << "Listen failed. Error: " << GetLastError() << endl;system("pause");return -1;}// 开始处理IO数据cout << "本服务器已准备就绪,正在等待客户端的接入...\n"; 创建用于发送数据的线程//HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);//第二个0,代表回掉函数参数为0for (int i = 0; i < 10; ++i){PER_HANDLE_DATA * PerHandleData = NULL;SOCKET acceptSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);if (INVALID_SOCKET == acceptSocket){cerr << "WSASocket failed with error code: %d/n" << WSAGetLastError() << endl;return FALSE;}// 开始在接受套接字上处理I/O使用重叠I/O机制// 在新建的套接字上投递一个或多个异步// WSARecv或WSASend请求,这些I/O请求完成后,工作者线程会为I/O请求提供服务    // 单I/O操作数据(I/O重叠)LPPER_IO_OPERATION_DATA PerIoData = NULL;PerIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED));PerIoData->databuff.len = 1024;PerIoData->databuff.buf = PerIoData->buffer;PerIoData->operationType = ACCEPT;  // readPerIoData->client = acceptSocket;if (SOCKET_ERROR == WSAIoctl(srvSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof(guidAcceptEx), &lpfnAcceptEx,sizeof(lpfnAcceptEx), &dwBytes, NULL, NULL)){cerr << "WSAIoctl failed with error code: " << WSAGetLastError() << endl;if (INVALID_SOCKET != srvSocket){closesocket(srvSocket);srvSocket = INVALID_SOCKET;}//goto EXIT_CODE;return -1;}if (SOCKET_ERROR == WSAIoctl(srvSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs,sizeof(GuidGetAcceptExSockAddrs), &lpfnGetAcceptExSockAddrs, sizeof(lpfnGetAcceptExSockAddrs),&dwBytes, NULL, NULL)){cerr << "WSAIoctl failed with error code: " << WSAGetLastError() << endl;if (INVALID_SOCKET != srvSocket){closesocket(srvSocket);srvSocket = INVALID_SOCKET;}//goto EXIT_CODE;return -1;}if (FALSE == lpfnAcceptEx(srvSocket, PerIoData->client, PerIoData->databuff.buf, PerIoData->databuff.len - ((sizeof(SOCKADDR_IN) + 16) * 2),sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, &(PerIoData->overlapped))){if (WSA_IO_PENDING != WSAGetLastError()){cerr << "lpfnAcceptEx failed with error code: " << WSAGetLastError() << endl;return FALSE;}}}Sleep(1000 * 60 * 60);PostQueuedCompletionStatus(completionPort, 0, NULL, NULL);WaitForMultipleObjects(g_nThread, hThread, TRUE, INFINITE);WSACleanup();system("pause");return 0;
}// 开始服务工作线程函数
DWORD WINAPI ServerWorkThread(LPVOID IpParam)
{HANDLE CompletionPort = (HANDLE)IpParam;DWORD BytesTransferred;LPOVERLAPPED IpOverlapped;LPPER_HANDLE_DATA PerHandleData = NULL;LPPER_IO_DATA PerIoData = NULL;DWORD RecvBytes = 0;DWORD Flags = 0;BOOL bRet = false;while (true) {bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&IpOverlapped, INFINITE);if (bRet == 0) {if (WAIT_TIMEOUT == GetLastError()){continue;}// Errorcout << "GetQueuedCompletionStatus failed with error:" << GetLastError() << endl;continue;}PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(IpOverlapped, PER_IO_DATA, overlapped);//这个宏的作用是:根据一个结构体实例中的成员的地址,取到整个结构体实例的地址//PER_IO_DATA的成员overlapped的地址为&IpOverlapped,结果就可以获得PER_IO_DATA的地址if (NULL == PerIoData){// Exit thread  break;}// 检查在套接字上是否有错误发生if (0 == BytesTransferred && (PerIoData->operationType == RECV || PerIoData->operationType == SEND)){closesocket(PerHandleData->socket);GlobalFree(PerHandleData);GlobalFree(PerIoData);continue;}switch (PerIoData->operationType){case ACCEPT:{SOCKADDR_IN* remote = NULL;SOCKADDR_IN* local = NULL;int remoteLen = sizeof(SOCKADDR_IN);int localLen = sizeof(SOCKADDR_IN);lpfnGetAcceptExSockAddrs(PerIoData->databuff.buf, PerIoData->databuff.len - ((sizeof(SOCKADDR_IN) + 16) * 2),sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&local, &localLen, (LPSOCKADDR*)&remote, &remoteLen);//使用GetAcceptExSockaddrs函数 获得具体的各个地址参数.if (setsockopt(PerIoData->client, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,(char*)&(PerHandleData->socket), sizeof(PerHandleData->socket)) == SOCKET_ERROR)cout << "setsockopt..." << endl;// 创建用来和套接字关联的单句柄数据信息结构PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));  // 在堆中为这个PerHandleData申请指定大小的内存PerHandleData->socket = PerIoData->client;//memcpy(&(perHandleData->clientAddr),raddr,sizeof(raddr));//将新的客户套接字与完成端口连接CreateIoCompletionPort((HANDLE)PerHandleData->socket,CompletionPort, (ULONG_PTR)PerHandleData, 0);memset(&(PerIoData->overlapped), 0, sizeof(OVERLAPPED));PerIoData->operationType = RECV;        //将状态设置成接收//设置WSABUF结构PerIoData->databuff.buf = PerIoData->buffer;PerIoData->databuff.len = PerIoData->BufferLen = 1024;cout << "wait for data arrive(Accept)..." << endl;Flags = 0;if (WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1,&RecvBytes, &Flags, &(PerIoData->overlapped), NULL) == SOCKET_ERROR)if (WSAGetLastError() == WSA_IO_PENDING)cout << "WSARecv Pending..." << endl;continue;}break;case RECV:// 开始数据处理,接收来自客户端的数据//WaitForSingleObject(hMutex, INFINITE);cout << "A Client says: " << PerIoData->databuff.buf << endl;//ReleaseMutex(hMutex);// 为下一个重叠调用建立单I/O操作数据ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空内存PerIoData->databuff.len = 1024;PerIoData->databuff.buf = PerIoData->buffer;//buf是个指针,这一过程会清空buffer的内容PerIoData->operationType = RECV;    // readWSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);continue;break;default:break;}}return 0;
}// 发送信息的线程执行函数
DWORD WINAPI ServerSendThread(LPVOID IpParam)
{while (1) {if (clientGroup.empty()){Sleep(5000);continue;}char talk[200];cin.get(talk, 200);int len;for (len = 0; talk[len] != '\0'; ++len) {// 找出这个字符组的长度}talk[len] = '\n';talk[++len] = '\0';printf("I Say:");cout << talk;//WaitForSingleObject(hMutex, INFINITE);for (unsigned i = 0; i < clientGroup.size(); ++i) {send(clientGroup[i]->socket, talk, 200, 0); // 发送信息}//ReleaseMutex(hMutex);}return 0;
}

IOCP结合AcceptEx实例相关推荐

  1. iocp配合acceptex

    摘自网上: IOCP配合AcceptEx的例子(1)(发表时间: 2006-7-2 14:33:00) [评论] [打印] [字体:大 中 小] 本文链接:http://blog.pfan.cn/ma ...

  2. 【转】IOCP配合AcceptEx的例子

    [转]IOCP配合AcceptEx的例子 原文地址:http://laokaddk.blog.51cto.com/368606/610145/ 本程序在VS2003编译器编译运行.在6.0下可能需要稍 ...

  3. AcceptEx与完成端口(IOCP)结合实例

    前言 在windows平台下实现高性能网络服务器,iocp(完成端口)是唯一选择.编写网络服务器面临的问题有:1 快速接收客户端的连接.2 快速收发数据.3 快速处理数据.本文主要解决第一个问题. A ...

  4. IOCP之AcceptEx的问题(1)

    最近在使用编写IOCP模型时,Server端使用的是AcceptEx异步 而Client端是用的Socket函数,但是在测试中遇到这种情况: CLient并发连接到Server,每次都是连接数1635 ...

  5. IOCP配合AcceptEx的例子

    本程序在VS2003编译器编译运行.在6.0下可能需要稍加修改. #include <iostream> #include <winsock2.h> #include < ...

  6. IOCP 浅析与实例

    这一年半来一直在做游戏项目逻辑层,学会了不少东西,觉得自己应该看看服务器底层的东西了,主要的东西就是网络模块,网络模块是沿用以前项目的,在 我们项目中被我们头改动过几次,现在还是比较稳定的.因为是Wi ...

  7. IOCP使用acceptEX进行异步接收

    示例代码为什么要调用WSAIoctl ()函数 #include <winsock2.h> #include <windows.h> #include <string&g ...

  8. IOCP使用acceptEx产生997错误

    997错误在解释中是重叠IO正在使用中,要么使用acceptEx没有开线程,一些结果没办法处理,要么就是在x64位系统编译中出现的问题.第二种可能 性最大.还在解决了,解决了再来更新 CSDN上对99 ...

  9. uicolor swift_Swift中的UIColor

    uicolor swift UIColor (UIColor) An object that stores color data and sometimes opacity. 存储颜色数据和有时不透明 ...

最新文章

  1. mongodb java 地理位置_Mongodb地理位置索引
  2. 数据结构实验之链表三:链表的逆置
  3. Sicily 1346. 金明的预算方案
  4. C#中 Convert与Parse的区别
  5. 【UI】为项目添加类似于淘宝筛选列表勾选的ui-choose
  6. 我在用的浏览器插件利器
  7. Yii中设置时间分区
  8. 青蛙跳台阶问题暨斐波那契数列
  9. IDEA 如何打jar包
  10. 远程连接服务器的命令工具,windows系统如何实现远程命令?远程命令工具您选哪个?...
  11. 三层交换机如何实现不同网络的相互通信
  12. 实现一个内存池管理的类
  13. 计算机应用基础实训教程侯冬梅,计算机应用基础实训教程
  14. 自然语言处理-003NLP定义以及歧义性-学习笔记
  15. 微信群控系统的实现原理,微信群控系统源码的核心实现代码
  16. os-无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动
  17. 【EtherCAT分析】三、EtherCAT从站设备描述文件设计
  18. 为小米盒子做的两个软件:桌面和浏览器
  19. Flak——跨域问题解决
  20. 如何在Google搜索到我的网站?

热门文章

  1. npm下载forever
  2. Linux terminator终端使用方式,个人认为目前最好的终端工具
  3. 记住:从来没有一个屌丝能够逆袭!
  4. 计算机考研导师推荐,考研导师
  5. Activity之四大启动模式
  6. 8款最给力的学习类应用推荐
  7. iphone xr xs_如何在iPhone X,XS和XR上使用Animoji
  8. Render RenderContents RenderControl 三个方法的区别
  9. 查询目前借书但未归还图书的学生名称及未还图书数量
  10. 格局大了,人生的路才会越走越宽!