文章目录
IOCP多连接管理基本模型
server端IOCP多连接管理基本模型如下:
1)启动一个接受线程,用于接受client发起的连接请求;
2)创建一个IOCP(I/O complete port)和一组工作线程;当client发起连接请求时,接受线程将client socket绑定到IOCP上;工作线程负责对这些client socket的收/发完成消息进行响应,同时也发起收/发请求,或者说工作线程负责实现client socket的通信过程;
连接状态
TCP连接是全双工通信,收/发是独立的,因此状态包括idle, sending, receiving三种。
服务端通信过程
当一个client连接到服务端之后,服务端处理过程如下:
1)发起接受请求,该clien 处于receiving状态
2)处理接收报文,如果接受报文是数据请求数据包,则组织数据报文,然后将数据报文发送出去,client更新为sending状态。否则状态不变,继续等待接收事件。
3)处理发送完成事件,数据发送完成后转入步骤1,否则状态不变。
现象
1)多工作线程下,单条client很容易出现假死状态(对接收数据无响应)
2)单个工作线程下,单条和多条client运行正常
分析原因
有上面现象可以分析出一定与多个工作线程有关,但很想知道这种情况是怎么出现的。于是在代码中添加了一些调试信息:在每次client状态改变时,打印线程ID,client的Socket,新状态值。
代码
unsigned __stdcall thread_processcomm(void* p_param)
{
struct s_tcpsrv_context *p_ctx = (struct s_tcpsrv_context*)p_param;
c_tcpserver *p_server = (c_tcpserver*)p_ctx->p_tcpsrv;
unsigned ui_iocpidx = p_ctx->ui_iocpidx;
assert(NULL != p_server);
HANDLE hdl_iocp = (HANDLE)p_server->hdl_iocps[ui_iocpidx];
c_protocol* p_ptl = (c_protocol*)p_server->get_protocol();
assert(NULL != p_ptl);
unsigned ui_threadid = GetCurrentThreadId();
unsigned ui_sendbufsize = p_server->get_sendbufsize();
unsigned ui_rcvbufsize = p_server->get_rcvbufsize();
unsigned ui_sendbufidxlimit = ui_sendbufsize / 2;
unsigned ui_rcvbufidxlimit = ui_sendbufsize / 4 * 3;
p_server->inc_acctivethreadcnt();
unsigned ui_interval = p_server->get_interval();
OVERLAPPED *p_ol = NULL;
c_clientcontext *p_clientctx = NULL;
DWORD dw_transferbytes = 0;
unsigned ui_now;
DWORD dwBytes = 0, dwFlags = 0;
while (p_server->b_running)
{
BOOL b_ret = GetQueuedCompletionStatus(
hdl_iocp,
&dw_transferbytes,
(PULONG_PTR)&p_clientctx,
&p_ol,
(DWORD)ui_interval);
ui_now = get_curr_usec();
//Get the client context
if (TRUE == b_ret)
{
assert(NULL != p_clientctx);
//deal set/conditoin list
run_clientcontext_pcdlistset(p_clientctx, ui_now);
run_clientcontext_pcdlistcondition(p_clientctx, ui_now);
OVERLAPPED *p_olsend = (OVERLAPPED*)p_clientctx->get_polsend();
OVERLAPPED *p_olrcv = (OVERLAPPED*)p_clientctx->get_polreceive();
int n_rcvhead, n_rcvtail;
int n_sendhead, n_sendtail;
byte *p_buf = NULL;
byte by_connstatus = CLTCTX_STATUS_IDLE;
byte by_newconnstatus = CLTCTX_STATUS_IDLE;
if (p_ol == p_olrcv)
{//receive
by_connstatus = CLTCTX_STATUS_RCV;
p_buf = p_clientctx->get_receivebuffer();
n_rcvhead = p_clientctx->get_rcvhead();
n_rcvtail = p_clientctx->get_rcvtail();
n_rcvtail += dw_transferbytes;
p_clientctx->ui_rcvbytes += dw_transferbytes;
assert(n_rcvtail <= ui_rcvbufsize);
//match receive package
bool b_matched = true;
while (n_rcvhead < n_rcvtail && b_matched)
{
b_matched = false;
POS pos = p_clientctx->pcdlistreceive.GetHeadPosition();
while (NULL != pos)
{
POS pos_curr = pos;
c_procedure* p_pcd = p_clientctx->pcdlistreceive.GetN