Снова запустите TCPServer с ThreadingMixIn в коде сразу после его закрытия.(Дает адрес, который уже используется) - PullRequest
5 голосов
/ 07 марта 2011

Я пытаюсь запрограммировать TCPServer с потоками (ThreadingMixIn) в Python.Проблема в том, что я не могу выключить его должным образом, поскольку я получаю socket.error: [Errno 48] Address already in use, когда я пытаюсь запустить его снова.Это минимальный пример кода Python, который вызывает проблему:

import socket
import threading
import SocketServer

class FakeNetio230aHandler(SocketServer.BaseRequestHandler):

    def send(self,message):
        self.request.send(message+N_LINE_ENDING)

    def handle(self):
        self.request.send("Hello\n")

class FakeNetio230a(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    def __init__(self, server_address, RequestHandlerClass):
        self.allow_reuse_address = True
        SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)

if __name__ == '__main__':
    for i in range(2):
        fake_server = FakeNetio230a(("", 1234), FakeNetio230aHandler)
        server_thread = threading.Thread(target=fake_server.serve_forever)
        server_thread.setDaemon(True)
        server_thread.start()
        # might add some client connection here
        fake_server.shutdown()

Все, что должен сделать основной код, - это запустить сервер, завершить работу и запустить его снова.Но это вызывает ошибку, указанную выше, потому что сокет не был освобожден после первого выключения.

Я думал, что установка self.allow_reuse_address = True может решить проблему, но это не сработало.Когда программа python завершает работу, я могу сразу же запустить ее снова, и она может запустить сервер один раз (но снова не дважды).
Однако проблема исчезла, когда я рандомизировал порт (например, заменил 1234 на 1234+i), поскольку ни один другой сервер не прослушивает этот адрес.

Существует аналогичный SO Q Изящное завершение работы с ThreadingTCPServer , но решение (для allow_reuse_address установлено значение True не работает длямой код и я не использую ThreadingTCPServer).

Как мне изменить код, чтобы иметь возможность дважды запустить сервер в моем коде?

Дополнительная информация: причина, по которой я это делаю, заключается в том, что я хочу запустить некоторые модульные тесты для своего проекта на python.Это требует предоставления (поддельного) сервера, к которому должно подключаться мое программное обеспечение.

edit :
Я только что нашел самый правильный ответ на мою проблему: я должен добавить fake_server.server_close() в конце основного кода выполнения (сразу после fake_server.shutdown()).Я нашел это в исходном файле реализации TCPServer.Все, что он делает, это self.socket.close().

Ответы [ 3 ]

4 голосов
/ 17 июня 2012

Этот пост помог мне преодолеть проблему с закрытым сокетом.У меня была та же проблема, и я хотел опубликовать здесь свою простую реализацию для класса сервера TCP (и метода клиента).

Я создал класс TCPThreadedServer.Для использования его необходимо наследовать, а метод process(msg) должен быть переопределен.переопределенный метод вызывается каждый раз, когда сервер получает сообщение msg, и если он возвращает объект не None, он будет возвращен подключенному клиенту как строка.прост в использовании:

class EchoServerExample(TCPThreadedServer):
    def __init__(self):
        TCPThreadedServer.__init__(self, "localhost", 1234, "Server")

    def process(self, data):
        print "EchoServer Got: " + data
        return str.upper(data)

for i in range(10):
    echo = EchoServerExample()
    echo.serve_forever()

    response = client("localhost", 1234, "hi-%i" % i)
    print "Client received: " + response

    echo.shutdown()

Я использовал метод: импорт сокета

def client(ip, port, msg, recv_len=4096, 
           timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
    msg = str(msg)
    response = None
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((ip, port))
        if timeout != socket._GLOBAL_DEFAULT_TIMEOUT:
            sock.settimeout(timeout)
        sock.send(msg + "\n")
        if recv_len > 0:
            response = sock.recv(recv_len)
    finally:
        sock.close()
        return response

Наслаждайтесь!

4 голосов
/ 07 марта 2011

Каким-то образом fake_server не отменяется при назначении (в первой строке в for операторе).

Чтобы это исправить, просто удалите fake_server в конце цикла:

        del fake_server # force server to unbind
2 голосов
/ 07 марта 2011

Измените ваше FakeNetio230a определение следующим образом:

class FakeNetio230a(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    def __init__(self, server_address, RequestHandlerClass):
        self.allow_reuse_address = True
        SocketServer.TCPServer.__init__(self,
                                        server_address,
                                        RequestHandlerClass,
                                        False)  # do not implicitly bind

Затем добавьте эти две строки в вашу точку входа под вашей FakeNetio230a:

fake_server.server_bind()    # explicitly bind
fake_server.server_activate()   # activate the server

Вот пример:

if __name__ == '__main__':
    for i in range(2):
        fake_server = FakeNetio230a(("", 1234), FakeNetio230aHandler)
        fake_server.server_bind()    # explicitly bind
        fake_server.server_activate()   # activate the server
        server_thread = threading.Thread(target=fake_server.serve_forever)
        server_thread.setDaemon(True)
        server_thread.start()
        # might add some client connection here
        fake_server.shutdown()
...