在普通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,代表回掉函数参数为0 for (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; // read
PerIoData->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;
}
// Error
cout << "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; // read
WSARecv(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. AcceptEx与完成端口(IOCP)结合实例

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

  2. 最近学习工作流 推荐一个activiti 的教程文档

    全文地址:http://www.mossle.com/docs/activiti/ Activiti 5.15 用户手册 Table of Contents 1. 简介 协议 下载 源码 必要的软件 ...

  3. IOCP 模型2 AcceptEx

    // IOCP2.cpp : Defines the entry point for the console application. // #include "stdafx.h" ...

  4. C++20协程实例:携程化的IOCP服务端/客户端

    VC支持协程已经有一段时间了,之前一直想不明白协程的意义在哪里,前几天拉屎的时候突然灵光一闪: 以下是伪代码: task server() { for (;;) { sock_context s = ...

  5. 关于完成端口IOCP异步接收连接函数AcceptEx注意事项

    AcceptEx方法有一个参数dwReceiveDataLength,指明了在收到连接后是否需要收到第一包数据才返回.需要注意的是,如果 dwReceiveDataLength=0,则当接收到一个连接 ...

  6. IOCP入门

    完成端口(Completion Port)详解 此文讲解最好,也很全面一下其他文章看看就行,也可不看. 单句柄数据,单IO数据 此文讲述比较清晰,可以辅助理解上文. IOCP编程之基本原理:http: ...

  7. IOCP I/O完成端口(了解)

    IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型.它是应用程序使用线程池处理异步I/O请求的一种机制.在处理多个并发的异步I/O请求时,以往的模型都是在接收 ...

  8. IOCP模型总结(转)

    IOCP模型总结(转) IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型.它是应用程序使用线程池处理异步I/O请求的一种机制.在处理多个并发的异步I/O请求 ...

  9. IOCP模型与网络编程

    IOCP模型与网络编程 一.前言:        在老师分配任务(“尝试利用IOCP模型写出服务端和客户端的代码”)给我时,脑子一片空白,并不知道什么是IOCP模型,会不会是像软件设计模式里面的工厂模 ...

随机推荐

  1. 转--ES6 Promise 用法讲解

    Promise是一个构造函数,自己身上有all.reject.resolve这几个眼熟的方法,原型上有then.catch等同样很眼熟的方法. 那就new一个 var p = new Promise( ...

  2. MPAndroidChart Wiki(译文)~Part 4

    16. 动画 注意:本章的动画效果只会在API 11(Android3.0.x)及以上的Android版本上生效 在低于上述的Android版本中,动画将不会被执行,并不会导致程序崩溃. 所有类型的图 ...

  3. 个人博客网站 www.superzhang.site

    用django新建了一个博客网站,访问地址为www.superzhang.site.欢迎来逛逛.

  4. 类(Classes)

    待写! 这里极力推荐博客园Vamei写的python系列文章,非常精彩,我只是遵照着The Python Tutorial目录来记录自己的学习体会,但也在看Vamei的文章,给大家推荐! 作者:Vam ...

  5. SVM处理多分类问题(one-versus-rest和one-versus-one的不同)

    SVM算法最初是为二值分类问题设计的,当处理多类问题时,就需要构造合适的多类分类器. 目前,构造SVM多类分类器的方法主要有两类:一类是直接法,直接在目标函数上进行修改,将多个分类面的参数求解合并到一 ...

  6. WebForm、MVC图片加载失败处理

    还是那个该死的WebFrom项目,部分功能替换为MVC后感觉好多了,但是WebForm.MVC都有图片加载失败时显示提示图片的需求,并且统一在js中处理.问题来了,js中图片路径怎么处理呢?现场有可能 ...

  7. 隐居网V2.0

    经过一个月加班加点的努力,我们终于把隐居网V2.0版做好了(一个设计两个前端三个程序).上一版的隐居网因为经验不够底层架构没搭好(前台和后台都是),又是赶工期,导致后面越做越坑爹.所谓从哪里跌倒从哪里 ...

  8. POJ3422 Kaka's Matrix Travels 【费用流】*

    POJ3422 Kaka's Matrix Travels Description On an N × N chessboard with a non-negative number in each ...

  9. windows下PyCharm运行和调试scrapy

    Scrapy是爬虫抓取框架,Pycharm是强大的python的IDE,为了方便使用需要在PyCharm对scrapy程序进行调试 python PyCharm Scrapy scrapy指令其实就是 ...

  10. linux 系统下配置java环境变量

    liunx桌面版本系统下载地址:http://www.ubuntukylin.com/downloads/ 1.源码包准备: 首先到官网下载jdk,http://www.oracle.com/tech ...