当重复使用套接字时,出现套接字正在使用的错误

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?

我是不是完全忽略了什么?

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请求。基本上,这允许您累积多个请求,然后一次性发送它们。直到实际发送累积的请求之前,您将不会收到任何响应,因此您可以将其视为批处理 - 这适合您的应用程序设计吗?


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错误(套接字正在使用中),你可以简单地关闭套接字,调用cleanup,然后重新启动WSA,重置套接字及其sockopt

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

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

这个错误只会在大约4000次调用中出现一次。

我很好奇为什么会这样,尽管这似乎可以解决它。如果有人对此有任何意见,我会非常好奇听到它


0

使用完毕后,你会关闭套接字吗?


是的,在每个请求后,我都会调用closesocket()。我已经检查过了,即使在出现错误之前的最后一个请求也会发生这种情况,因此套接字不会保持打开状态。 - DanJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接