IOCP模式中,在socket上同时投递WSARecv和WSASend的问题?

亚细亚 2019-07-16 04:56:47
typedef struct _SocketState // socket state & control
{
char operation;
SOCKET socket;
DWORD length;
char buf[MAX_BUF];
} SocketState;

static SocketState* new_socket_state(void)
{
return (SocketState *)calloc (1, sizeof(SocketState));
}

static WSAOVERLAPPED* new_overlapped(void)
{
return (WSAOVERLAPPED *)calloc (1, sizeof(WSAOVERLAPPED));
}

newSocketState = new_socket_state();
newSocketState->socket = socketState->socket;//此处的socketState->socket是已经建立连接的socket;

CreateIoCompletionPort((HANDLE)newSocketState->socket, cpl_port,(ULONG_PTR)newSocketState, 0) //绑定到完成端口上;
==================
我要投递两个io:
socketState->operation = OP_READ;
WSARecv(newSocketState ->socket, &wsabuf, 1, NULL, &flags, ovl, NULL)
socketState->operation = OP_WRITE;
WSASend(newSocketState ->socket, &wsabuf, 1, NULL, 0, ovl, NULL)
这里我不可能让socketState->operation同时为OP_READ或者OP_WRITE两个值吧?这里应该如何处理?
...全文
475 13 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
Hippop314 2019-09-06
  • 打赏
  • 举报
回复
一个支持上万高并发的简单HTTP服务器写法: https://github.com/xiaoliang314/libatask/blob/master/httpserver_win/httpserver.c
Hippop314 2019-09-06
  • 打赏
  • 举报
回复
IOCP用起来可以说是最方便的框架了。完全不必搞得这么麻烦。事件驱动你可以这么写: iocp事件的定义: struct iocp_evt; /* iocp事件处理函数 */ typedef void (*iocp_evt_handler)(void *priv, struct iocp_evt *evt); /* 定义iocp事件,将overlapped与事件绑定起来 */ struct iocp_evt { iocp_evt_handler handler; void *priv; OVERLAPPED overlapped; DOWRD numberOfBytes; DOWRD dwError; ULONG_PTR key; } /* 事件初始化 */ void iocp_evt_init(struct iocp_evt *evt, iocp_evt_handler handler, void *priv) { evt->handler = handler; evt->priv = priv; memset(&evt->overlapped, 0, sizeof(OVARLAPPED)); } 事件被触发时的处理函数: void on_wsa_recv(void *priv, struct iocp_evt *evt) { /* 拿到IOCP中的错误码 */ DOWRD dwError = evt->dwError; /* 拿到IOCP传输的字节数 */ DOWRD numberOfBytes = evt->numberOfBytes; /* 拿到IOCP绑定时的Key */ ULONG_PTR key = evt->key; /* 拿到自己携带的上下文 */ void *context = priv; /* ... */ } 投递IOCP事件处的写法: /* 生成iocp事件 */ struct iocp_evt *evt = (struct iocp_evt *)malloc(sizeof(struct iocp_evt)); /* 传入事件回调函数和自己的上下文指针对事件进行初始化 */ iocp_evt_init(evt, on_wsa_recv, user_data); /* 投递时,将IOCP事件中的overlapped成员传入 */ WSARecv(socket, &wsabuf, 1, NULL, &flags, &evt->overlapped, NULL); GetQueuedCompletionStatus处的写法: while (1) { DWORD numberOfBytes; ULONG_PTR key; LPOVERLAPPED *pOverlapped; struct iocp_evt *iocp_evt; DWORD dwError = GetQueuedCompletionStatus(iocp_handle, &numberOfBytes, &key, &pOverlapped, INFINITE) ? 0 : GetLastError(); /* 若pOverlapped不为零,则有一个iocp事件被完成 */ if (pOverlapped != NULL) { /* 通过struct iocp_evt的overlapped成员指针拿到iocp_evt指针 */ iocp_evt = CONTAINING_RECORD(pOverlapped, struct iocp_evt, overlapped); /* 设置此次IOCP事件的处理结果 */ iocp_evt->dwError = dwError; iocp_evt->key = key; iocp_evt->numberOfBytes = numberOfBytes; /* 处理事件 */ iocp_evt->handler(iocp_evt->priv, iocp_evt); } };
ima_zhan 2019-08-16
  • 打赏
  • 举报
回复
逻辑先不论,如果要投递两个请求,使用两个newSocketState 就可以了
Eleven 2019-08-16
  • 打赏
  • 举报
回复
你可以同时投递Send和Recv,绑定2个WSBuf,在投递期间保持绑定的buffer有效。
xian_wwq 2019-07-22
  • 打赏
  • 举报
回复
8楼分析的有道理 投递是依赖于业务的,并不是连接后就直接投递读和写 server是被动响应的, 需要接收数据才会发出读投递; 如果需要发送数据,才会进行写投递。
tiger波波 2019-07-19
  • 打赏
  • 举报
回复
大致看了下,感觉你理解有误。作为服务器端,recv不是随便投递的,recv的对象不是创建出来的,而是accept函数接到连接请求得到的,得到客户端socket后,才可以对其投递recv操作。
socketState->operation = OP_READ;是什么操作?没见过。而且现实中不可能上来就投递send和recv,就像别人打电话问你问题,电话接通后,你不可能问别人什么问题的同时回答他的问题,必须先听他的问题,再回答他。回到socket,服务器端都是accept后,投递recv,看看客户端请求是什么,然后再完成例程里相应请求,也就是投递send回复。当然,多个客户端连接的话,这个客户端的recv和另一个客户端的send可能同时存在,但那是不同的会话。
ima_zhan 2019-07-18
  • 打赏
  • 举报
回复
投递两次不会覆盖吧,应该会返回两次
亚细亚 2019-07-17
  • 打赏
  • 举报
回复
先后几乎同时投递WSARecv和WSASend,投递WSARecv时,设置newSocketState ->operation = OP_READ; 紧接着投递WSASend时,设置newSocketState ->operation = OP_WRITE;
当GetQueuedCompletionStatus返回两次(WSARecv和WSASend)后,程序都会进入OP_READ分支处理。

亚细亚 2019-07-17
  • 打赏
  • 举报
回复
引用 4 楼 zgl7903 的回复:
IOCP二:同时发送和接收

关键是绑定的key是相同的newSocketState。
zgl7903 2019-07-17
  • 打赏
  • 举报
回复
亚细亚 2019-07-17
  • 打赏
  • 举报
回复
我的意思是这样的,先投递:
socketState->operation = OP_READ;
WSARecv(newSocketState ->socket, &wsabuf, 1, NULL, &flags, ovl, NULL)
这个时候GetQueuedCompletionStatus还没有返回,也就是WSARecv还没有完成 ,紧接着投递
socketState->operation = OP_WRITE;
WSASend(newSocketState ->socket, &wsabuf, 1, NULL, 0, ovl, NULL)
这样一来socketState->operation = OP_WRITE就覆盖了socketState->operation = OP_READ,然后
GetQueuedCompletionStatus返回来的操作只能socketState->operation = OP_WRITE,而不会得到socketState->operation = OP_READ。这个怎么解决?
亚细亚 2019-07-17
  • 打赏
  • 举报
回复
引用 1 楼 zgl7903 的回复:
用 WSAEventSelect(s, hEventObject, FD_READ|FD_WRITE|FD_CLOSE); 事件驱动可以么, 然后用 WSAEnumNetworkEvents 检查

IOCP还要用事件选择,没有其它方式吗?
zgl7903 2019-07-17
  • 打赏
  • 举报
回复
用 WSAEventSelect(s, hEventObject, FD_READ|FD_WRITE|FD_CLOSE); 事件驱动可以么, 然后用 WSAEnumNetworkEvents 检查
结构层次及相互联系 (1)、工作线程:响应连接的IO投递返回并负责投递读请求,并将IO返回结果投递给处理线程,可设定参数决定工作线程数量; (2)、处理线程:处理线程调用回调函数将信息传递给应用层或协议栈,可设定参数决定工作处理数量; (3)、看守线程:响应Accept事件调用AcceptEx,检测连接和心跳超时 ,将信息投递给工作线程,模块仅有一个看守线程。 1. 技术要求 (1)、线程同步:Lock指令、临界段; (2)、主要Socket API:WSASendWSARecv、AcceptEx、DisconnectEx; (3)、内存管理:连接池(句柄重用)、内存池; (4)、数据0拷贝:通过内置处理线程,上层应用可以避免自建线程池及复制数据的过程。同时提供GBuf内存分配功能,应用层获得分配地址及填充数据之后亦可直接投递给内核/驱动层; (5)、数据顺序同步:同一个连接同时只有一个处理线程响应其IO事件; (6)、IO请求投递:单投递读、多投递写; (7)、0缓冲读投递:可条件编译实现,以适用大规模连接要求。 (8)、超时机制:可设置空连接(连接不发送数据)超时时间以防止DOS攻击,也可设置心跳超时时间防止网络故障导致的现有连接成为虚连接避免耗尽系统资源。 (9)、接口技术:API、回调函数、客户句柄(客户连接句柄)。 (10)、主、被动发送:不使用HASH、MAP及LIST技术,即可提供安全可靠高效的客户连接句柄,以实现服务器端主被动发送数据功能; (11)、PerHandleData的回收不以IO投递的计数器或链表来做依据但仍能安全回收,同时尽量避免在高频的读写操作时做其他无关的操作以提高读写效率。 (12)、处理线程和工作线程有着良好分工界限,繁重的工作交给处理线程完成,工作线程工作量最大限度的减少,仅响应投递返回及读投递的操作; (13)、支持AWE,模块自动识别AWE是否开启(需手动开启),“否”则使用虚拟内存机制。 2. 功能要求 (1)、多IP多端口监听,每个监听可设置不同的回调函数,以高效的区别处理数据 (2)、可设置每秒最大的连接并发量和空连接(连接不发数据)超时时间以防止DOS攻击造成的服务瘫痪、具有心跳处理(防网络异常造成的虚连接)功能 (3)、不加协议的透明传输,可适用广泛的网络通讯环境 (4)、可现实主、被动发送数据,但不会因兼顾主动发送而额外增加降低效率的工作 (5)、内置处理线程,上层应用可不必自建线程池处理数据,所有IO事件按顺序调用回调函数并可以在回调函数内直接处理数据,不必担心多线程造成的接收数据乱序的问题。 (6)、高效率的数据对应关联机制,在初次连接并根据登录数据设置每个连接对应的宿主(Owner)之后,再接收的数据即可立即获得该连接对应的宿主,而不必再做额外的查询工作,并且模块内部采用的是指针关联方式,对于长连接、主动发送的服务器系统而言是高效率的。 (7)、可兼容IPv6 3. 注意事项 因硬件环境和应用环境不同,不合理的配置会出现效率及性能上的问题,因此以下情况出现时,请务必与作者联系以确保获得更好的参数配置: (1)、连接量超过1000个的。超过的应结合具体硬件配置和网络带宽等因素综合设定运行参数。 (2)、带宽使用率超过20%的。工作线程和处理线程数量的设置也是综合考虑数据吞吐量和数据处理负载的因素来设置的,过多的线程会在调度上浪费时间,同时也应该综合考虑线程优先级别来设置工作线程和处理线程数量,两者的设置也不一定能相等。 (3)、服务器端有主动发送需求的、短连接(含网络故障造成的连接断开)出现频率高的。 压力测试工具介绍: 一、 使用G-TcpClient模块 二、 可以设定间隔时间发起大规模长、短连接 三、 可以发起密集数据包,包括即时和定时发送,1M的光纤带宽最大可以达到100K/S(单向)以上,100M本地网最大可以达到10M/S(单向)以上 四、 数据发送仅由一个独立线程但当,每点击一次Connect就创建一个线程根据当前参数发起连接。 五、 测试前提:服务器接收客户端数据后立即原样返回给客户端

18,363

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧