windows上的IOCP如何使用,并用C++实现多客户端服务器

在Windows系统中,可以使用IOCP(Input/Output Completion Ports)来实现高性能的I/O多路复用机制。IOCP是Windows系统中一种高效的异步I/O机制,可以用于实现高并发的网络服务器。下面是使用IOCP实现多客户端服务器的基本步骤:

  1. 创建socket

使用socket函数创建一个TCP服务器socket,例如:

#include <winsock2.h>

WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);

SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(8888);
bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address));
listen(server_socket, 10);
  1. 创建IOCP对象

使用CreateIoCompletionPort函数创建一个IOCP对象,并将服务器socket与IOCP对象关联,例如:

HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
CreateIoCompletionPort((HANDLE)server_socket, iocp, 0, 0);
  1. 处理连接请求

使用AcceptEx函数监听客户端连接请求,并将新的客户端socket与IOCP对象关联,例如:

LPFN_ACCEPTEX AcceptExFunc;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
DWORD dwBytes = 0;
WSAIoctl(server_socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &AcceptExFunc, sizeof(AcceptExFunc), &dwBytes, NULL, NULL);

SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int address_length = sizeof(struct sockaddr_in);
char accept_buffer[2 * (sizeof(struct sockaddr_in) + 16)];
AcceptExFunc(server_socket, client_socket, accept_buffer, 0, sizeof(struct sockaddr_in) + 16, sizeof(struct sockaddr_in) + 16, NULL, NULL);
CreateIoCompletionPort((HANDLE)client_socket, iocp, (ULONG_PTR)client_socket, 0);
  1. 处理I/O完成事件

使用GetQueuedCompletionStatus函数获取I/O完成事件,并处理读写操作,例如:

while (true) {
    DWORD bytes_transferred = 0;
    ULONG_PTR completion_key = 0;
    LPOVERLAPPED overlapped = NULL;
    GetQueuedCompletionStatus(iocp, &bytes_transferred, &completion_key, &overlapped, INFINITE);

    if (bytes_transferred == 0) {
        closesocket((SOCKET)completion_key);
        printf("Client disconnected\n");
        continue;
    }

    SOCKET client_socket = (SOCKET)completion_key;
    if (overlapped == NULL) {
        char buffer[1024] = {0};
        recv(client_socket, buffer, bytes_transferred, 0);
        printf("Received data: %s\n", buffer);
    } else {
        WSABUF buffer;
        buffer.buf = new char[1024];
        buffer.len = 1024;
        DWORD flags = 0;
        WSARecv(client_socket, &buffer, 1, NULL, &flags, overlapped, NULL);
    }
}
  1. 发送数据

使用客户端socket发送数据,例如:

char* data = "Hello, world!";
send(client_socket, data, strlen(data), 0);

以上是使用IOCP实现多客户端服务器的基本步骤,需要注意的是,IOCP的代码相对复杂,需要理解异步I/O、事件驱动编程等概念。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
//一个简单的使用例子 //连接远程服务器成功 或 接收到一个远程连接时,本函数将会被ioc.dll回调.在本函数中,应该向客户端列表中添加节点,记得加锁 // //2.s :套接字句柄,标志着一个新的连接 //3.u_addr:对端的IP地址,网络字节序 //4.u_port:对端的端口号,网络字节序 //5.flag :如果是本地连接上了一个远程服务器,flag值为0,或者,是接收到一个远程客户端的连接,这时,flag值为1 //6.返回值:返回一个自定义的值,这个值将在其他回调函数中作为参数传递(注意:就如套接字句柄一样,这个值也最 //好能标志一个连接,比如,客户端列表的某个节点的指针,假设用STL中的MAP来维护客户端列表,那么用KEY作为返回值也不错。) long _stdcall ioc_call_connect(HIOC hIoc,HINT s,long u_addr,long u_port,long flag) { long res=0; printf("socket(%d) connected\n",s); return res; } //断开与远程服务器的连接 或 远程客户端断开,本函数将会被ioc.dll回调.在本函数中,你可以删除客户节点,记得加锁 //s :套接字句柄,标志着哪个连接断开了(在本回调函数返回之前,套接字句柄s不可能被重新利用,所以,用s作为关键字构建客户端列表也是没有问题的) //res:ioc_call_connect回调函数返回的那个值,如果它是客户端列表的某个节点的话,那么可以删除了。 void _stdcall ioc_call_disconnect(HIOC hIoc,HINT s,long res,long flag) { printf("socket(%d) disconnected\n",s); } //当ioc内部一收到数据就会回调本函数,所谓数据有可能是多个数据包,也有可能是一个不完整的数据包 //hIoc,s,flag都不再多做解释 //res :ioc_call_oprate_dat和ioc_call_disconnect一定是被线性回调的,不可能存在同时执行的情况,所以,res如果指向某个节点的话,在本函数中可以不加锁地尽情访问,在本函数返回之前,res不会被释放掉 //hArg:数据调度句柄 //data:数据 //len :数据大小 //返回值:返回剩余未处理完的字节数 long _stdcall ioc_call_oprate_dat(HIOC hIoc,HARG hArg,HINT s,long res,long flag,char *data,long len) { //假如数据包格式是这样: //struct DATAPACK //{ // long size; long cmd; ...... //}; //如果是这样的话,那么典型的处理方法如下: char *p=data; long size_res=len;//收到数据的总字节数 long size_per;//其中某一个数据包的字节数 while(1) { if(size_res<4) return size_res;// size_per=*(long*)(p+0x00);//取数据包的实际长度 if(size_per<0 || size_per>某个最大值) { ::iocCommon_CloseSocket(hIoc,s); return 0; } if(size_res<size_per) return size_res;//剩余数据不够一个完整的数据包,返回 //得到一个完整的数据包,可以就地处理,但如果处理这个数据包将会很耗时,那么为了不阻塞工作线程, //只好将其调度给数据处理线程,这里其实可以定义一个结构,除了将数据包调度出去,还可以附带一些其他信息 if(数据包处理起来比较简单) { //处理 } else//调度给数据处理线程 { char *msg=(char*)::malloc(len); ::memcpy(msg,p,size_per); ::iocCommon_DispatchMessage(hIoc,hArg,msg);//hArg就只有这么一个作用 } p+=size_per; size_res-=size_per; } } //阻塞数据处理线程回调函数 void _stdcall ioc_call_oprate_msg(HIOC hIoc,void *msg) { //处理数据包 ::free(msg);//根据谁分配谁释放的原则,释放msg } int main() { ::iocCommon_Startup(); long addr_con=(long)ioc_call_connect; long addr_dis=(long)ioc_call_disconnect; long addr_dat=(long)ioc_call_oprate_dat; long addr_msg=(long)ioc_call_oprate_msg;//这个参数是可选的,可以不要专门的阻塞数据处理线程 HIOC hIoc=::iocCommon_Create(3072,128,addr_con,0,addr_dis,0,addr_dat,0,addr_msg,0);//创建IOC ::iocCommon_SetOprateThread(hIoc);//增加一个工作线程 ::iocCommon_SetOprateThread(hIoc);//增加一个工作线程 //启动服务器,内部循环调用阻塞的accept函数,ioc不考虑客户端连接服务器有多困难,而只考虑如何高效地进行数据传输 //可以再创建几个线程,多调用几个iocServer_Start,各个iocServer_Start绑定不同端口也可以 ::iocServer_Start(hIoc,NULL,6800); ::iocCommon_Cleanup(); return 0; }
实现一个IOCP(Input/Output Completion Port)服务器,需要按照以下步骤进行: 1. 创建一个监听套接字(listening socket)并绑定到本地IP地址和端口号上。 2. 创建一个完成端口(completion port)并将监听套接字与之关联。 3. 开始监听连接请求,当有客户端连接请求到达时,接收连接,创建一个新的套接字(client socket),并将其与完成端口关联。 4. 将新创建的套接字(client socket)加入到IOCP中,以便异步地接收客户端的数据请求。 5. 当客户端请求到达时,使用异步I/O操作,从套接字中读取数据,并将读取请求与完成端口关联。 6. 当读取完成时,将读取请求与处理请求的工作线程关联,并将读取到的数据传递给处理线程进行处理。 7. 处理线程完成数据的处理后,将处理结果写回客户端使用异步I/O操作将写入请求与完成端口关联。 8. 当写入完成时,将写入请求与处理请求的工作线程关联,完成整个请求处理过程。 在实现IOCP服务器时,需要注意以下几点: 1. 应避免使用阻塞I/O操作,使用异步I/O操作以提高服务器的并发性能。 2. 应充分利用完成端口和线程池等技术,提高服务器的吞吐量和可扩展性。 3. 应实现适当的协议处理逻辑,以保证服务器能够正确地处理客户端请求。 4. 应对客户端请求进行适当的安全性检查,以确保服务器的安全运行。 5. 应实现适当的错误处理逻辑,以便及时发现和处理服务器中的错误和异常情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

telllong

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值