WSAWaitForMultipleEvents,如何管理多个线程的多个 event?

sunkill 2007-07-02 10:14:40
在 Client 用 Connect 使用了 非阻塞的socket, 然后用 WSAEventSelect 捕捉事件,
WSAWaitForMultipleEvents 最多等待 WSA_MAXIMUM_WAIT_EVENTS 个事件,
如果需要等待更多的事件,需要开多条线程进行管理,
增加,删除 event 与相应的 socket 的关联,
问题是:有什么策略对多个线程的多个 event 进行管理?
...全文
868 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
huliang66 2011-07-23
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 enterprise54 的回复:]

psusjh(大江风)同志这个代码基本上显示了WSAEventSelect的扩展运用,但处理得不是非常巧妙,如果作为高性能服务器的时候,这个方案不是最好的解决方案,当然做为普通的客户端,这个运用是够用了。

nIndex = ::WSAWaitForMultipleEvents(1, &pThread-> events[i], TRUE, 1000, FALS……
[/Quote]

可以考虑作为单独线程处理.
你说的这个情况在我的项目中测试后发现,在大量连接上看来的时候,确实会出现投递的接收请求不够的情况.
Conry 2009-09-07
  • 打赏
  • 举报
回复
[Quote=引用楼主 sunkill 的回复:]
在   Client   用   Connect   使用了   非阻塞的socket,   然后用   WSAEventSelect   捕捉事件,
WSAWaitForMultipleEvents   最多等待   WSA_MAXIMUM_WAIT_EVENTS   个事件,
如果需要等待更多的事件,需要开多条线程进行管理,
增加,删除   event   与相应的   socket   的关联,
问题是:有什么策略对多个线程的多个   event   进行管理?
[/Quote]
既然是client,那么socket数量可以认为是已知的,就按64一组来处理就行了,没那么复杂吧
shan_dong_ren 2009-09-07
  • 打赏
  • 举报
回复
学习
sunkill 2007-08-02
  • 打赏
  • 举报
回复
ConnectEx 也无法跟 完成端口 绑定,通过 完成端口来通知 CONNECT 消息
僵哥 2007-08-02
  • 打赏
  • 举报
回复
ConnectEx无法同完成端口绑定?那ConnectEx的最后一个参数是干什么用的?当然需要你事先把端口与完成端口绑定,然后再执行ConnectEx.
JessC 2007-07-31
  • 打赏
  • 举报
回复
使用IOCP时,如果想要异步连接,可以使用ConnectEx,像AcceptEx那样,之后就是WSARecv,WSASend了, 再配合DisconnectEx使用,设计连接。可惜这两个API只能在NT5.1及之后的系统中使用
sunkill 2007-07-26
  • 打赏
  • 举报
回复
unsigned(僵哥)

我之所以引入 WSAWaitForMultipleEvents 就是为了 Connect 不阻塞,然后通过 FD_CONNECT 通知····

因为我要做多个客户端连到服务器。
theendname 2007-07-25
  • 打赏
  • 举报
回复
学习
僵哥 2007-07-25
  • 打赏
  • 举报
回复
完成端口只是一种线程池+队列+完成通知于一体的处理机制,为的就是用来简化线程和事务的管理。至于你用它来干什么事情,这个并不受限制。
僵哥 2007-07-25
  • 打赏
  • 举报
回复
我现在就考虑采用我上面所说的方法去干,测试已经通过了。
僵哥 2007-07-25
  • 打赏
  • 举报
回复
如果要使用完成端口则采用阻塞模式,而阻塞模式的Connect是立即反应连接的结果的,所以无所谓Connect事件,而当Connect连接成功之后,就可以等同于服务器端的一个Accept/AcceptEx成功,之后的操作跟服务器端是一样的。
CW_Wei 2007-07-25
  • 打赏
  • 举报
回复
up
sunkill 2007-07-10
  • 打赏
  • 举报
回复
psusjh(大江风) 贴的代码,我看过,我觉得里面的策略不是很好,每次都是通过 RebuiltArray 来重建整个数组·
enterprise54 2007-07-09
  • 打赏
  • 举报
回复
扩充一个好的IOCP框架做服务器端,在扩展一个好的WASEventSelect框架作为客户端,基本上在windows平台上就足够了。框架的扩充可以学习CAsyncSocket的实现,最好支持CAsyncSocket的所有接口,这样类库的使用者用起来也是轻轻松松!
psusjh 2007-07-09
  • 打赏
  • 举报
回复
是啊,多谢指点,现在正在学习IOCP
enterprise54 2007-07-09
  • 打赏
  • 举报
回复
psusjh(大江风)同志这个代码基本上显示了WSAEventSelect的扩展运用,但处理得不是非常巧妙,如果作为高性能服务器的时候,这个方案不是最好的解决方案,当然做为普通的客户端,这个运用是够用了。

nIndex = ::WSAWaitForMultipleEvents(1, &pThread->events[i], TRUE, 1000, FALSE);

这行代码运行不是非常高效,尤其是如果pThread->events中含有监听socket的事件的时候,在应付大量的并发连接的时候,效率机器低下。

此外,在对公共资源的访问上,还有更高效的做法。
psusjh 2007-07-09
  • 打赏
  • 举报
回复
/////////////////////////////////////////////////////
// EventSelectServer.h文件

DWORD WINAPI ServerThread(LPVOID lpParam);


// 套节字对象
typedef struct _SOCKET_OBJ
{
SOCKET s; // 套节字句柄
HANDLE event; // 与此套节字相关联的事件对象句柄
sockaddr_in addrRemote; // 客户端地址信息

_SOCKET_OBJ *pNext; // 指向下一个SOCKET_OBJ对象,为的是连成一个表
} SOCKET_OBJ, *PSOCKET_OBJ;

// 线程对象
typedef struct _THREAD_OBJ
{
HANDLE events[WSA_MAXIMUM_WAIT_EVENTS]; // 记录当前线程要等待的事件对象的句柄
int nSocketCount; // 记录当前线程处理的套节字的数量 <= WSA_MAXIMUM_WAIT_EVENTS

PSOCKET_OBJ pSockHeader; // 当前线程处理的套节字对象列表,pSockHeader指向表头
PSOCKET_OBJ pSockTail; // pSockTail指向表尾

CRITICAL_SECTION cs; // 关键代码段变量,为的是同步对本结构的访问
_THREAD_OBJ *pNext; // 指向下一个THREAD_OBJ对象,为的是连成一个表

} THREAD_OBJ, *PTHREAD_OBJ;

// 线程列表
PTHREAD_OBJ g_pThreadList; // 指向线程对象列表表头
CRITICAL_SECTION g_cs; // 同步对此全局变量的访问


// 状态信息
LONG g_nTatolConnections; // 总共连接数量
LONG g_nCurrentConnections; // 当前连接数量


// 申请一个套节字对象,初始化它的成员
PSOCKET_OBJ GetSocketObj(SOCKET s)
{
PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
if(pSocket != NULL)
{
pSocket->s = s;
pSocket->event = ::WSACreateEvent();
}
return pSocket;
}

// 释放一个套节字对象
void FreeSocketObj(PSOCKET_OBJ pSocket)
{
::CloseHandle(pSocket->event);
if(pSocket->s != INVALID_SOCKET)
{
::closesocket(pSocket->s);
}
::GlobalFree(pSocket);
}

// 申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中
PTHREAD_OBJ GetThreadObj()
{
PTHREAD_OBJ pThread = (PTHREAD_OBJ)::GlobalAlloc(GPTR, sizeof(THREAD_OBJ));
if(pThread != NULL)
{
::InitializeCriticalSection(&pThread->cs);
// 创建一个事件对象,用于指示该线程的句柄数组需要重组
pThread->events[0] = ::WSACreateEvent();

// 将新申请的线程对象添加到列表中
::EnterCriticalSection(&g_cs);
pThread->pNext = g_pThreadList;
g_pThreadList = pThread;
::LeaveCriticalSection(&g_cs);
}
return pThread;
}

// 释放一个线程对象,并将它从线程对象列表中移除
void FreeThreadObj(PTHREAD_OBJ pThread)
{
// 在线程对象列表中查找pThread所指的对象,如果找到就从中移除
::EnterCriticalSection(&g_cs);
PTHREAD_OBJ p = g_pThreadList;
if(p == pThread) // 是第一个?
{
g_pThreadList = p->pNext;
}
else
{
while(p != NULL && p->pNext != pThread)
{
p = p->pNext;
}
if(p != NULL)
{
// 此时,p是pThread的前一个,即“p->pNext == pThread”
p->pNext = pThread->pNext;
}
}
::LeaveCriticalSection(&g_cs);

// 释放资源
::CloseHandle(pThread->events[0]);
::DeleteCriticalSection(&pThread->cs);
::GlobalFree(pThread);
}

// 重新建立线程对象的events数组
void RebuildArray(PTHREAD_OBJ pThread)
{
::EnterCriticalSection(&pThread->cs);
PSOCKET_OBJ pSocket = pThread->pSockHeader;
int n = 1; // 从第1个开始写,第0个用于指示需要重建了
while(pSocket != NULL)
{
pThread->events[n++] = pSocket->event;
pSocket = pSocket->pNext;
}
::LeaveCriticalSection(&pThread->cs);
}

/////////////////////////////////////////////////////////////////////

// 向一个线程的套节字列表中插入一个套节字
BOOL InsertSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)
{
BOOL bRet = FALSE;
::EnterCriticalSection(&pThread->cs);
if(pThread->nSocketCount < WSA_MAXIMUM_WAIT_EVENTS - 1)
{
if(pThread->pSockHeader == NULL)
{
pThread->pSockHeader = pThread->pSockTail = pSocket;
}
else
{
pThread->pSockTail->pNext = pSocket;
pThread->pSockTail = pSocket;
}
pThread->nSocketCount ++;
bRet = TRUE;
}
::LeaveCriticalSection(&pThread->cs);

// 插入成功,说明成功处理了客户的连接请求
if(bRet)
{
::InterlockedIncrement(&g_nTatolConnections);
::InterlockedIncrement(&g_nCurrentConnections);
}
return bRet;
}

// 将一个套节字对象安排给空闲的线程处理
void AssignToFreeThread(PSOCKET_OBJ pSocket)
{
pSocket->pNext = NULL;

::EnterCriticalSection(&g_cs);
PTHREAD_OBJ pThread = g_pThreadList;
// 试图插入到现存线程
while(pThread != NULL)
{
if(InsertSocketObj(pThread, pSocket))
break;
pThread = pThread->pNext;
}

// 没有空闲线程,为这个套节字创建新的线程
if(pThread == NULL)
{
pThread = GetThreadObj();
InsertSocketObj(pThread, pSocket);
::CreateThread(NULL, 0, ServerThread, pThread, 0, NULL);
}
::LeaveCriticalSection(&g_cs);

// 指示线程重建句柄数组
::WSASetEvent(pThread->events[0]);
}

// 从给定线程的套节字对象列表中移除一个套节字对象
void RemoveSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)
{
::EnterCriticalSection(&pThread->cs);

// 在套节字对象列表中查找指定的套节字对象,找到后将之移除
PSOCKET_OBJ pTest = pThread->pSockHeader;
if(pTest == pSocket)
{
if(pThread->pSockHeader == pThread->pSockTail)
pThread->pSockTail = pThread->pSockHeader = pTest->pNext;
else
pThread->pSockHeader = pTest->pNext;
}
else
{
while(pTest != NULL && pTest->pNext != pSocket)
pTest = pTest->pNext;
if(pTest != NULL)
{
if(pThread->pSockTail == pSocket)
pThread->pSockTail = pTest;
pTest->pNext = pSocket->pNext;
}
}
pThread->nSocketCount --;

::LeaveCriticalSection(&pThread->cs);

// 指示线程重建句柄数组
::WSASetEvent(pThread->events[0]);

// 说明一个连接中断
::InterlockedDecrement(&g_nCurrentConnections);
}


BOOL HandleIO(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)
{
// 获取具体发生的网络事件
WSANETWORKEVENTS event;
::WSAEnumNetworkEvents(pSocket->s, pSocket->event, &event);
do
{
if(event.lNetworkEvents & FD_READ) // 套节字可读
{
if(event.iErrorCode[FD_READ_BIT] == 0)
{
char szText[256];
int nRecv = ::recv(pSocket->s, szText, strlen(szText), 0);
if(nRecv > 0)
{
szText[nRecv] = '\0';
printf("接收到数据:%s \n", szText);
}
}
else
break;
}
else if(event.lNetworkEvents & FD_CLOSE) // 套节字关闭
{
break;
}
else if(event.lNetworkEvents & FD_WRITE) // 套节字可写
{
if(event.iErrorCode[FD_WRITE_BIT] == 0)
{
}
else
break;
}
return TRUE;
}
while(FALSE);

// 套节字关闭,或者有错误发生,程序都会转到这里来执行
RemoveSocketObj(pThread, pSocket);
FreeSocketObj(pSocket);
return FALSE;
}

PSOCKET_OBJ FindSocketObj(PTHREAD_OBJ pThread, int nIndex) // nIndex从1开始
{
// 在套节字列表中查找
PSOCKET_OBJ pSocket = pThread->pSockHeader;
while(--nIndex)
{
if(pSocket == NULL)
return NULL;
pSocket = pSocket->pNext;
}
return pSocket;
}

DWORD WINAPI ServerThread(LPVOID lpParam)
{
// 取得本线程对象的指针
PTHREAD_OBJ pThread = (PTHREAD_OBJ)lpParam;
while(TRUE)
{
// 等待网络事件
int nIndex = ::WSAWaitForMultipleEvents(
pThread->nSocketCount + 1, pThread->events, FALSE, WSA_INFINITE, FALSE);
nIndex = nIndex - WSA_WAIT_EVENT_0;
// 查看受信的事件对象
for(int i=nIndex; i<pThread->nSocketCount + 1; i++)
{
nIndex = ::WSAWaitForMultipleEvents(1, &pThread->events[i], TRUE, 1000, FALSE);
if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
{
continue;
}
else
{
if(i == 0) // events[0]受信,重建数组
{
RebuildArray(pThread);
// 如果没有客户I/O要处理了,则本线程退出
if(pThread->nSocketCount == 0)
{
FreeThreadObj(pThread);
return 0;
}
::WSAResetEvent(pThread->events[0]);
}
else // 处理网络事件
{
// 查找对应的套节字对象指针,调用HandleIO处理网络事件
PSOCKET_OBJ pSocket = (PSOCKET_OBJ)FindSocketObj(pThread, i);
if(pSocket != NULL)
{
if(!HandleIO(pThread, pSocket))
RebuildArray(pThread);
}
else
printf(" Unable to find socket object \n ");
}
}
}
}
return 0;
}

psusjh 2007-07-09
  • 打赏
  • 举报
回复
用线程池
sunkill 2007-07-08
  • 打赏
  • 举报
回复
各位,我要解决的问题是 完成端口不能捕捉 Connect
enterprise54 2007-07-08
  • 打赏
  • 举报
回复
你是想用完成端口做客户端???
加载更多回复(7)

18,356

社区成员

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

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