Большое количество сокетов в TIME_WAIT для Python HTTPServer / Unity C# HTTPClient - PullRequest
0 голосов
/ 11 июля 2020

Я пытаюсь заставить программу Unity и Python общаться через HTTP. Сервер Unity отправляет HTTP-запрос через HTTPCient, который программа Python обрабатывает через HTTPServer. Проблема, с которой я столкнулся, заключается в том, что каждый запрос, похоже, отправляет сокет в TIME_WAIT. Связь происходит примерно 20 раз в секунду, и в конце концов сокетов больше нет, и программа перестает работать. Насколько я понимаю, эта проблема вызвана тем, что каждый запрос использует новый сокет без его надлежащего закрытия, что, по-видимому, поддерживает приведенный ниже захват Wireshark (в конце есть только один FIN-ACK, а не два, которые, как я понимаю, требует TCP).

Я пытаюсь использовать меньше сокетов или освободить сокеты раньше, чтобы их можно было использовать снова. Я пробовал несколько подходов к решению этой проблемы, включая 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

1 Ответ

0 голосов
/ 11 июля 2020

Один из случаев, когда вы можете получить такое поведение, - это когда вы не повторно используете HttpClient экземпляры, а создаете новые ( здесь - это дополнительная информация о topi c).

Также вы должны попробовать удалить 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);
}
...