【问题标题】:Large number of sockets in TIME_WAIT for Python HTTPServer / Unity C# HTTPClientPython HTTPServer / Unity C# HTTPClient 的 TIME_WAIT 中的大量套接字
【发布时间】:2020-07-10 21:51:40
【问题描述】:

我正在尝试让 Unity 和 Python 程序通过 HTTP 进行通信。 Unity 服务器通过 HTTPCient 发送 HTTP 请求,Python 程序通过 HTTPServer 处理该请求。我遇到的问题是每个请求似乎都将一个套接字发送到 TIME_WAIT。通信大约每秒发生 20 次,最终没有更多的套接字,程序停止运行。我的理解是这个问题是由于每个请求都使用了一个新的套接字而没有正确关闭它,下面的 Wireshark 捕获似乎支持它(最后只有一个 FIN-ACK,而不是我理解的 TCP 要求的 2 个)。

我正在尝试使用更少的套接字,或者尽快释放套接字以便再次使用它们。我尝试了多种方法来缓解这个问题,包括socket.SO_REUSEADDR、连接池和保持连接。我怀疑我没有正确实施其中的一个(或全部),但我不确定如何确认。下面的代码是我尝试实现下面的代码 - 不幸的是,python 方面的文档感觉有点稀疏。

void Start() {
    HttpClient requester = new HttpClient();
    requester.DefaultRequestHeaders.Add("Connection", "Keep-Alive");
    requester.DefaultRequestHeaders.Add("Keep-Alive", "5000");
    waitForClientResult(requester);
}

private async Task waitForClientResult(HttpClient requester) {
    HttpResponseMessage response = await requester.GetAsync("http://127.0.0.1:5018");
    response.EnsureSuccessStatusCode();
    string responseBody = await response.Content.ReadAsStringAsync();
    PythonServerReturnData data = JsonUtility.FromJson<PythonServerReturnData>(responseBody);
    doSomeStuffWithTheData(data);
    waitForClientResult(requester);
}
agent = UnityAgentHTTP()

class UnityAgentHTTP:
    def __init__(self):
        self.recievedData = None
        self.replyData = "ThisDataUpdatedElsewhere".encode('utf-8')
        self.exit = False

        self.sendThread = threading.Thread(target=self.sendOutput, name='sendThread', daemon = True)
        self.sendThread.start()
    
    def sendOutput(self):
        handler = partial(UnityHTTPServer, self)
        self.httpd = MyHTTPServer(('127.0.0.1', 5018), handler)
        self.httpd.serve_forever()

    def cleanup(self):
        print("cleaning up")
        self.exit = True
        self.httpd.shutdown()
        self.httpd.socket.close()
        self.httpd.server_close()

class MyHTTPServer(HTTPServer):
    allow_reuse_address = True  
    def server_bind(self):
        HTTPServer.server_bind(self)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # self.socket.bind(self.server_address)

class UnityHTTPServer(BaseHTTPRequestHandler):
    
    protocol_version = 'HTTP/1.1'

    def __init__(self, unityAgentHTTP: UnityAgentHTTP, *args, **kwargs):
        self.unityAgentHTTP = unityAgentHTTP
        super().__init__(*args, **kwargs)
    
    def do_HEAD(self):
        self.send_response(200)
        self.send_header("Content-type", "application/json")
        # self.send_header("Connection", "Keep-Alive")
        # self.send_header("keep-alive", "timeout=5, max=30")
        self.send_header("Content-Length", len(self.unityAgentHTTP.replyData) + 20) # TODO horrible hack bc i can't figure out how to count bytes objects properly 
        self.end_headers()

    def do_GET(self):
        self.do_HEAD()
        self.wfile.write(self.unityAgentHTTP.replyData)
        self.close_connection = True

Wireshark capture

【问题讨论】:

  • 不确定这是否能解决您的所有问题,但是否可以像不等待客户端上的异步任务一样简单?我听说如果您在不等待它们的情况下“触发并忘记”异步 void 可能会出现问题,尤其是在错误处理方面。但是,不确定其中有多少延伸到任务。您可以尝试waitForClientResult(requester).GetAwaiter().GetResult() 之类的方法,看看是否有帮助。
  • @joshua.software.dev 感谢您的建议!我试着等待电话,但似乎没有帮助。如果不冻结程序,我就无法让waitForClientResult(requester).GetAwaiter().GetResult() 工作。

标签: python c# unity3d networking


【解决方案1】:

当您没有重复使用 HttpClient 实例并创建新实例时,您可以获得该行为的一种情况(here 是有关该主题的更多信息)。

你也应该在完成工作后尝试dispose response

private async Task waitForClientResult(HttpClient requester) 
{
    HttpResponseMessage response = await requester.GetAsync("http://127.0.0.1:5018");
    try
    {
        response.EnsureSuccessStatusCode();
        string responseBody = await response.Content.ReadAsStringAsync();
        PythonServerReturnData data = JsonUtility.FromJson<PythonServerReturnData>(responseBody);
        doSomeStuffWithTheData(data);
    }
    finally
    {
        // Dispose of HttpResponseMessage to free up system resources and close network connections.
        response.Dispose();
    }
    
    waitForClientResult(requester);
}

【讨论】:

  • 感谢您的回答,我一定会打电话给 dispose!不幸的是,仅此一项并不能解决问题。除非在幕后发生了真正时髦的事情,否则我相信 HttpClient 已经被重用了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多