3

我正在用 c++ 编写一个 XMLRPC 客户端,旨在与 python XMLRPC 服务器通信。

不幸的是,此时,python XMLRPC 服务器只能在连接上处理一个请求,然后它会关闭,我发现这要归功于 mhawke 对我之前关于相关主题的查询的回复

因此,每次我想发出 XMLRPC 请求时,我都必须创建一个到我的 python 服务器的新套接字连接。这意味着创建和删除大量套接字。一切正常,直到我接近 4000 个请求。此时我收到套接字错误10048, Socket in use

我试过让线程休眠以让winsock修复它的文件描述符,当我的python客户端遇到相同问题时,这个技巧有效,但无济于事。我试过以下

int err = setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,(char*)TRUE,sizeof(BOOL));

没有成功。

我使用的是winsock 2.0,所以 WSADATA::iMaxSockets 不应该发挥作用,无论哪种方式,我检查并将其设置为 0(我假设这意味着无穷大)

在应用程序运行期间发出 4000 个请求似乎并不奇怪。当服务器不断关闭和重新打开时,有什么方法可以在客户端使用 SO_KEEPALIVE 吗?

我完全错过了什么吗?

4

3 回答 3

11

问题是由在 TIME_WAIT 状态下挂起的套接字引起的,一旦关闭客户端的套接字就会进入该状态。默认情况下,套接字将在此状态下保持 4 分钟,然后才能重新使用。您的客户(可能在其他进程的帮助下)在 4 分钟内将它们全部消耗掉。请参阅此答案以获得良好的解释和可能的非代码解决方案。

当您未显式绑定套接字地址时,Windows 会动态分配 1024-5000 范围内的端口号(3977 个端口)。此 Python 代码演示了该问题:

import socket
sockets = []
while True:
    s = socket.socket()
    s.connect(('some_host', 80))
    sockets.append(s.getsockname())
    s.close()

print len(sockets)    
sockets.sort()
print "Lowest port: ", sockets[0][1], " Highest port: ", sockets[-1][1]
# on Windows you should see something like this...
3960
Lowest port: 1025  Highest port: 5000

如果您尝试再次立即运行,它应该会很快失败,因为所有动态端口都处于 TIME_WAIT 状态。

有几种方法可以解决这个问题:

  1. 管理您自己的端口分配并用于bind()将您的客户端套接字显式绑定到您每次创建套接字时递增的特定端口。您仍然需要处理端口已在使用中的情况,但您将不限于动态端口。例如

    port = 5000
    while True:
        s = socket.socket()
        s.bind(('your_host', port))
        s.connect(('some_host', 80))
        s.close()
        port += 1
    
  2. 摆弄 SO_LINGER 套接字选项。我发现这有时在 Windows 中有效(虽然不完全确定为什么): s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 1)

  3. 我不知道这是否会对您的特定应用程序有所帮​​助,但是,可以使用 multicall方法通过同一连接发送多个 XMLRPC 请求。基本上,这允许您累积多个请求,然后一次发送所有请求。在您实际发送累积的请求之前,您不会得到任何响应,因此您基本上可以将其视为批处理 - 这是否适合您的应用程序设计?

于 2009-09-17T01:09:07.587 回答
1

更新:

我把它扔进了代码中,它现在似乎可以工作了。

if(::connect(s_, (sockaddr *) &addr, sizeof(sockaddr))) 
  {
    int err = WSAGetLastError();
    if(err == 10048)   //if socket in user error,   force kill and reopen socket
    {
        closesocket(s_);
        WSACleanup();
        WSADATA info;
        WSAStartup(MAKEWORD(2,0), &info);
        s_ = socket(AF_INET,SOCK_STREAM,0);
        setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,(char*)&x,sizeof(BOOL));
    }
  }

基本上,如果你遇到 10048 错误(socket in use),你可以简单地关闭socket,调用cleanup,然后重启WSA,reset the socket and its sockopt

(最后一个 sockopt 可能不是必需的)

我之前一定错过了 WSACleanup/WSAStartup 调用,因为肯定会调用 closesocket() 和 socket()

此错误仅每 4000 次调用发生一次。

我很好奇为什么会这样,尽管这似乎可以解决它。如果有人对这个主题有任何意见,我会很想听听

于 2009-09-16T20:42:54.457 回答
0

使用后是否关闭套接字?

于 2009-09-16T19:21:14.160 回答