我继承了要维护的Windows C / C ++代码TCP / IP服务器,以前的程序员定义了一个额外的端口用于基本的HTTP通信。服务器使用Windows套接字lib 2,通过生成要发送的新线程来处理请求。问题似乎是对具有多个项目的HTML页面的HTTP请求似乎无法完全加载页面,这意味着客户端在Web浏览器中获得无限旋转图标。
与代码混淆我发现问题是客户端(例如Chrome v33)在同一端口上发出多个GET请求,这意味着服务器因此启动新线程来在同一个套接字上处理这些请求。也就是说,accept()返回的套接字(即连接套接字,而不是侦听套接字)然后传递给CreateThread()用户函数来处理GET请求。但是,第一个启动的线程在完成发送时关闭此套接字,这意味着其他线程发现套接字在轮到它们发送时已经关闭。我尝试不关闭套接字,这更好地工作并加载了更多的页面,但仍然没有完全完成页面加载,可能是因为没有服务器线程关闭套接字让客户端知道网页已完成。 / p>
所以我的问题是,这种情况如何由多线程HTTP服务器处理?在同一个套接字上有多个线程进行通信是严格禁忌还是不明智?似乎有意义的是,现代Web浏览器会对页面项进行多个并发(而不是顺序)请求,但是对于服务器来说,在单独的线程中处理这些请求而不是按顺序处理这些请求似乎也是有意义的。线。每个请求必须以某种方式打开自己的套接字 - 也许通过在新线程中调用accept()?
这通常是通过超时关闭套接字,还是通过活动计时器或类似方法解决的?或者使用HTTP v1.0来禁用持久连接是否更好?或者是否有另一种方法可以知道何时发送所有网页元素并关闭服务器套接字呢?这是套接字关闭代码,如果有一些其他winsock2选项,我错过了神奇地处理持久连接:
bool ShutdownConnection
(
CONFIG *configSP,
SOCKET sd
)
{
if ( shutdown(sd, SD_SEND) == SOCKET_ERROR)
{
return false;
}
char readBuf[bufSize];
while (1)
{
int newBytes = recv(sd, readBuf, bufSize, 0);
if (newBytes == SOCKET_ERROR)
{
return false;
}
else
{
break;
}
}
if (closesocket(sd) == SOCKET_ERROR)
{
return false;
}
return true;
}
答案 0 :(得分:0)
与代码混淆我发现问题是客户端(例如Chrome v33)在同一端口上发出多个GET请求,这意味着服务器因此启动新线程来在同一个套接字上处理这些请求。
不,不。每个accept()
都会返回一个新套接字。
也就是说,accept()返回的套接字(即连接套接字,而不是侦听套接字)然后被传递给CreateThread()用户函数来处理GET请求。
正确,每次都是一个新的套接字。你已经自相矛盾了。
但是,第一个启动的线程在完成发送后关闭此套接字,这意味着其他线程发现套接字在轮到发送时已经关闭。
没有。这些线程都有自己的套接字。除非你有一个错误的插槽关闭错误的插座,或者让它们以某种方式混淆,例如,通过不正确的变量范围。
我试过没有关闭套接字
关闭套接字不是“实验”,而是要求。
这样做效果更好,加载了更多的页面,但仍然没有完全完成页面加载,大概是因为没有一个服务器线程关闭套接字让客户端知道网页已经完成。
没有。您的副本循环可能不正确,或者您可能正在混合套接字,或者您可能没有完全独立的线程,或者您还有其他编码错误。
所以我的问题是,这种情况如何由多线程HTTP服务器处理?
在正确编写的服务器中,您所知道的情况不可能出现。
是否严格禁忌或建议让多个线程在同一个套接字上进行通信?
应该是不可能的。如果你有,你的代码中有一个错误。
似乎有意义的是,现代Web浏览器会对页面项进行多个并发(而不是顺序)请求,但是对于服务器在单独的线程中处理这些请求而不是处理这些请求的效率似乎也是有意义的。顺序在一个线程中。
他们必须同时处理它们。
每个请求都必须以某种方式打开自己的套接字 - 可能是通过在新线程中调用accept()吗?
您应该已经调用accept()
来获取新的入站套接字,然后再启动线程来处理它。这没有任何意义。
这通常是通过超时关闭套接字,还是通过活动计时器或类似方法解决的?
没有
或者使用HTTP v1.0来禁用持久连接是否更好?
不必要且不具备性能。
或者是否有另一种方法可以知道何时发送所有网页元素并关闭服务器套接字呢?
你不需要知道。有多个插座,每个插座都应该按照自己的时间关闭。你不可能知道你在这里谈论的是什么。
这是套接字结束代码,如果有一些其他的winsock2选项,我错过了神奇地处理持久连接:
您不需要读取循环或关闭。关闭它。这篇文章来自(并且您没有正确复制)的MSDN文章是关于在两端实现 synchronized 关闭,这在HTTP中不是必需的。它被广泛误解为所有TCP连接的要求。事实并非如此。
你在这里完全咆哮错误的树。我建议你发布一些有关评估的实际代码。
答案 1 :(得分:-1)
还没有完全解决这个问题,但最简单的解决方法是切换回HTTP通信的单线程并在每条消息后关闭套接字。
我在网上没有找到太多关于此的信息,但Winsock Programmer's Guide上的FAQ 3.10建议在多个线程中的同一套接字上使用send()是一个坏主意,因为交错发送的可能性数据
因此,如果有人对实际实现多线程HTTP v1.1服务器感兴趣,我猜他们需要实现一个消息队列和一个专用线程,用于在同一个套接字上执行顺序send()调用并使用“Connection:keep -活”。我想如果你这样做,那么你可以在最后一条消息和一个超时时间之后关闭套接字,并在最后一个HTTP标头中发送“Connection:close”,但最好不要随时打开套接字以便复兴
实现具有多个线程的消息队列可能比我的单线程解决方案更有效,但是对于我的低带宽需求,这种额外的代码复杂性是不值得的。