多线程,recv总会停住

joeytry 2009-09-07 11:32:15
请版上的各位高手帮忙看看,感谢万分!

写的一个邮件服务器,每次接到邮件客户端的连接请求,会开启一个新线程,然后从邮件客户端recv命令,并执行,如此循环,直至quit命令,但发送大邮件(超过3M)后,总是在recv()的时候停住。

我的服务器系统和邮件客户端在同一台机器上,原先没有对客户端连接connected的判断,看到有人说可以用select解决,所以加上了,结果问题还是一样,通过调试,可以看到邮件已经向客户端send发送完毕

while(TRUE){
memset(buf, '\0', 1023);
if(!IsConnected(bConnected) || !bConnected || recv(sock, buf, 1023, 0) <= 0){
AfxMessageBox(...);
sprintf(buf, "QUIT");
}

if(strnicmp(buf, "LIST", 4) == 0){
....
}...
else if(strnicmp(buf, "RETR", 4) == 0){
Email->Send();
}...
else if(strnicmp(buf, "QUIT", 4) == 0){
close(sock);
break;
}...
}

BOOL ServiceClient::IsConnected(BOOL &bConnected)
{
timeval timeout = {0, 0};
fd_set servfds;
FD_ZERO(&servfds);
FD_SET(sock, &servfds);
int nStatus = select(0, &servfds, &servfds, NULL, &timeout);
if (nStatus == SOCKET_ERROR)
return FALSE;
else{
bConnected = !(nStatus == 0);
return TRUE;
}
}
...全文
422 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
月竹影 2009-09-15
  • 打赏
  • 举报
回复
呃,那我也搞不明白了
joeytry 2009-09-15
  • 打赏
  • 举报
回复
to moonbamboo:
sprintf(buf, "QUIT");将后续处理推送到quit处理流程,其中我用close关闭了连接

to cftxlin:
我现在已经设置了超时控制,recv不再阻塞,但send依然每次成功,导致我每次都需要重新处理数据->超时->客户端关闭->服务端重新开始,导致特殊大邮件永远无法下载

to lvchun012:
粘包应该没有,因为对于一个客户端线程与一个服务端线程来说,数据的处理是串行的,没有并发多个命令或者并发收取多个邮件的要求
  • 打赏
  • 举报
回复
Select模式是监测是否有客户端连接时用的。
你所说的应该是堵塞,你可以设置一下,接收和发送的超时。具体,你可以参考一下:
//设置发送超时返回
int TimeOut=5;
if(setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&TimeOut, sizeof(TimeOut))==SOCKET_ERROR)
{
sprintf(thismessage, "%s:%d", "设置发送超时错误", uPort);
return 0;
}
//设置接受超时返回
if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
sprintf(thismessage, "%s:%d", "设置接受超时错误", uPort);
return 0;
}
月竹影 2009-09-14
  • 打赏
  • 举报
回复
我把你的代码处理了一下,你试试看行不行。
if(!IsConnected(bConnected) || !bConnected || recv(sock, buf, 1023, 0) <= 0){ 
AfxMessageBox(...);
sprintf(buf, "QUIT");
closesocket(sock);
break;
}
月竹影 2009-09-14
  • 打赏
  • 举报
回复
if(!IsConnected(bConnected) || !bConnected || recv(sock, buf, 1023, 0) <= 0){ 
AfxMessageBox(...);
sprintf(buf, "QUIT");
}

这里,超时了后,你就只显示一个对话框,和提示一下,没做别的处理?
一般来说,这里应该是closesocket,然后退出线程的,否则,本来你应该要断掉这个连接的,(注意,超时的时候,是应该有服务端来主动的关闭连接的,客户端并不知道需要关闭连接),但是实际上你并没有断开这个连接,还是继续的循环接收,这个时候,你下来的Send肯定还是成功的,而且是一直成功下去,直到客户端主动的关闭连接为止。
所以我估计你的问题就在这里。
lvchun012 2009-09-14
  • 打赏
  • 举报
回复
看看你在close中有没有处理粘包的问题,
joeytry 2009-09-12
  • 打赏
  • 举报
回复
To moonbamboo:
感谢你在搬家之余还能想起我的问题,辛苦了。我自行分析了一下:

我用setsockopt设置了recv timeout时间为10s,若10s内没有接收到任何数据recv将返回,这样我的程序就不会被阻塞。

但问题其实没有根本解决,因为之前的send还是成功的,导致我处理过后的数据被认为发送出去,无法被延迟到下一次传送,每次都需要重新处理,每次都send成功,但每次客户端实际都没有接收到任何数据,该邮件就这样永远无法下载。

我在send之前使用IsConnected判断socket是否连接结果一样,是否因为IsConnected中timeout时间设置为0的缘故,但我认为这不应该是select在客户端断开connection后仍然返回成功的原因。

我准备写个测试程序判断一下connection timeout后断开的select和send的返回值
月竹影 2009-09-11
  • 打赏
  • 举报
回复
噢,这几天公司搬家,都没上论坛。
楼主,你这样说,那你的程序就很奇怪。
“服务器端在等待10秒没收到命令自动退出”,这里的自动退出,指的是直接关闭socket?closesocket()?
我现在有点明白你的问题了,我再看了你代码,当你的recv接收超时的时候,似乎是没有处理好,一般来说,你应该是处理一下,然后直接退出线程的,但是,你似乎没有处理这里,导致了后面再继续的recv,你再仔细看看?
MoXiaoRab 2009-09-09
  • 打赏
  • 举报
回复
灵异问题,太灵异了。Send明明是同步函数
joeytry 2009-09-09
  • 打赏
  • 举报
回复
to moonbamboo:
因为我要对文件进行重新调整,所以文件大的时候,处理时间较长。我用的邮件客户端是foxmail,我用setsockopt设置SO_RCVTIMEO为10s,服务器端在等待10秒没收到命令自动退出,但是send仍然成功?这也是我很疑惑的问题,我在send之间用select检查了sockd的读写,居然还是可读写,而foxmail早已停止下载,关闭了下载线程。
请大家帮我看看为何connection的客户端已经终止了,却能send成功?
  • 打赏
  • 举报
回复
是不是要分开多个啊
路人乙2019 2009-09-08
  • 打赏
  • 举报
回复
recv(sock, buf, 1023, 0)这样一次只能收到1023个字节吧?邮件大小不只这个的话是不是应该循环调用几次????
ToCpp 2009-09-08
  • 打赏
  • 举报
回复
阻塞模式吗?如果不是阻塞模式应该是直接返回的,
先确认下socket模式,设置为非阻塞不一定也成功了,呵呵,
jyh_baoding 2009-09-08
  • 打赏
  • 举报
回复
帮顶
月竹影 2009-09-08
  • 打赏
  • 举报
回复
你用的是阻塞模式,如果send不成功,那肯定是连接已经断了,或者是网络出错了,所以你要检查这里,
然后你说邮件终端退出了,那肯定是收不到邮件了,所以也不会发送命令反馈了。
邮件终端超时退出?一般来说这个超时应该是一个不短的时间,所谓的超时是指长时间没有数据交换的处理,你的邮件终端为什么会超时?
MoXiaoRab 2009-09-08
  • 打赏
  • 举报
回复
是阻塞模式还是非阻塞?我估计你是阻塞的模式,Recv一直在等数据
没有WSAECONNRESET返回那就是没问题,WSAAsynSelect看看
joeytry 2009-09-08
  • 打赏
  • 举报
回复
To moonbamboo:
邮件终端的确没有收到邮件,同时邮件终端由于连接时间过长退出了,RETR命令的send()命令执行结束,但是我没有判断send是否发送成功,是否是这边出的问题,就是send不成功,因此底层sock仍然阻塞在发送中,因此recv也被阻塞了?
月竹影 2009-09-08
  • 打赏
  • 举报
回复
嗯,那么你确定终端收完了整个邮件后,发出了反馈到服务端么?
bragi523 2009-09-08
  • 打赏
  • 举报
回复
服务端发送socket设置linger属性试试
joeytry 2009-09-08
  • 打赏
  • 举报
回复
To moonbamboo:
我的意思是,在程序调试中,我发现每次出现问题,都是在我发送了一次较大的邮件给邮件客户端,接下来等待邮件客户端发送下一个命令,结果就死在那里了。粘包应该不会,邮件客户端也是等待命令完成返回结果了才发送下一个命令,若超时,邮件客户端会自动停止发送命令,中断连接。在实际测试中,如果邮件不是太大的话,我根据邮件客户端,连续发送几百封,一点问题都没有。

To safeqq4:
谢谢你给的详细代码,但问题没有解决,bytesRecv<=0的结果我已经做了判断,直接归类于网络连接错误类,需要执行退出,关闭连接,但问题是recv的时候就停住了,导致程序无法继续下去。
加载更多回复(6)

18,356

社区成员

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

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