【问题标题】:Socket in use error when reusing sockets重用套接字时出现 Socket in use 错误
【发布时间】:2023-03-08 18:05:01
【问题描述】:

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

不幸的是,此时,python XMLRPC 服务器只能在连接上处理一个请求,然后它就关闭了,我发现这要感谢 mhawke 对我之前关于 related subject 的查询的回复

因此,每次我想发出 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?

我是不是完全错过了什么?

【问题讨论】:

    标签: c++ python sockets xml-rpc


    【解决方案1】:

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

    当您未显式绑定套接字地址时,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. 我不知道这是否有助于 您的特定应用程序, 但是,可以发送 多个 XMLRPC 请求通过 使用相同的连接 multicall 方法。基本上 这可以让你积累 几个请求,然后发送它们 一次全部。你不会得到任何 回复,直到您实际发送 累积的请求,所以你可以 本质上认为这是批处理 处理 - 这是否适合 你的应用程序设计?

    【讨论】:

      【解决方案2】:

      更新:

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

      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,重置socket和它的sockopt

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

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

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

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

        • 是的,每次请求后,我都会调用 closesocket()。我已经检查过了,这甚至发生在错误发生之前的最后一个请求,所以套接字没有打开
        猜你喜欢
        • 2020-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多