我有一个C服务器应用程序,使用POCO框架编写 . 在这种情况下,服务器应用程序充当HTTP服务器 . 有一个客户端应用程序,我无法控制,无法调试,导致服务器出现问题 . 客户端请求一个大文件,该文件作为HTTP响应返回 . 在文件返回期间,客户端关闭连接 . 我看到套接字移动到CLOSE_WAIT状态,表明客户端已发送FIN . 问题是在我的应用程序中,send()函数然后挂起导致我的一个HTTP线程基本上丢失,并且一旦所有线程进入此状态,服务器就没有响应 .
发送代码在POCO框架内,但看起来像这样:
do
{
if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException();
rc = ::send(_sockfd, reinterpret_cast<const char*>(buffer), length, flags);
}
while (_blocking && rc < 0 && lastError() == POCO_EINTR);
if (rc < 0) error();
return rc;
(调用此函数时标志为0) . 我尝试通过添加以下代码来检测此状态:
char c;
int r;
int rc;
do
{
// Check if FIN received
while ((r = recv(_sockfd, &c, 1, MSG_DONTWAIT)) == 1) {}
if (r == 0) { ::close(_sockfd); _sockfd = POCO_INVALID_SOCKET; } // FIN received
if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException();
rc = ::send(_sockfd, reinterpret_cast<const char*>(buffer), length, flags);
}
while (_blocking && rc < 0 && lastError() == POCO_EINTR);
if (rc < 0) error();
return rc;
这似乎可以使事情变得更好,但仍然无法解决问题 . 我最终得到的服务器没有快速挂起,但更多CLOSE_WAIT套接字,所以我认为我已经部分解决了线程挂起问题,但我仍然没有正确地从破坏的套接字中整理好 . 随着这种变化,问题发生得越来越少,但仍然会发生,所以我认为关键是要理解为什么send()会挂起 .
我在linux上测试这段代码 .
1 回答
要干净地关闭套接字:
使用
SD_SEND
调用shutdown
.继续从套接字读取,直到读取返回零或致命错误 .
关闭套接字 .
关闭套接字后,请勿尝试访问套接字 .
您的代码有两个主要问题 . 无论发生什么情况,它都不能确保始终在套接字上调用
close
,它可以在关闭套接字后访问套接字 . 前者导致您的CLOSE_WAIT问题 . 后者是一个巨大的安全漏洞 .