Почему это соединение сокета позволяет только 1 отправлять и получать?

Предыстория
У меня есть простая настройка сервера сокетов, к которой я пытаюсь разрешить одновременные подключения и отобразить данные. Клиентская сторона запускает несколько потоков, каждый из которых устанавливает собственное соединение с сервером. Это отлично работает для вызова socket.send(), но все последующие вызовы вызывают либо «сброс соединения одноранговым узлом», либо «сломанный канал». Обратите внимание, что я не нашел изменения, которое переключает сброс и сломанную трубу. Я посмотрел здесь, на SO, для решения, но боюсь, что не знаю, что искать.

Я делаю это неправильно, или я что-то упускаю из виду в своей настройке?

Сервер

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print "{} wrote: {}\n".format(self.client_address[0], self.data)
        self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

Клиент

import socket
import sys
import threading
import time

HOST, PORT = "localhost", 9999
def create_client():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        cur_thread = threading.current_thread()
        sock.connect((HOST, PORT))
        for x in range(55):
            msg = "{}: {}\n".format(cur_thread.name, str(x))
            # Connect to server and send data
            print cur_thread.name + ": sending message\n"
            sock.send(msg)
            # Receive data from the server and shut down
            received = sock.recv(2048)
            print "RX:" + received
    finally:
        cur_thread = threading.current_thread()
        response = "{}: Closing!\n".format(cur_thread.name)
        print response
        sock.close()

if __name__ == "__main__":
    print "testing single thread"
    #create_client()
    print "starting threads"
    client_1 = threading.Thread(target=create_client)
    client_1.daemon = True
    client_1.start()
    client_2 = threading.Thread(target=create_client)
    client_2.daemon = True
    client_2.start()

    time.sleep(20)

person Adam Lewis    schedule 17.12.2011    source источник


Ответы (1)


Когда вы возвращаетесь из handle, сокет закрывается. Используйте цикл while и возвращайтесь из handle только тогда, когда self.data == ''. recv возвращает нулевые байты, когда клиент закрывает соединение. Также не strip() возвращайте результат до тех пор, пока не проверите возвращаемое значение, иначе вы можете получить ложное закрытие. Наконец, используйте ThreadingTCPServer, иначе сервер может обрабатывать только одно соединение за раз.

Пример:

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while True:
            self.data = self.request.recv(1024)
            if self.data == '':
                break
            self.data = self.data.strip()
            print "{} wrote: {}\n".format(self.client_address[0], self.data)
            self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

Также обратите внимание, что send() не гарантирует отправку всех байтов сообщения, поэтому используйте sendall() или проверьте возвращаемое значение. recv() тоже может быть сложно. TCP/IP является потоковым протоколом и не имеет концепции границ сообщения, поэтому вам решать, как реализовать протокол для проверки того, что вы получили полное сообщение. Можно отправить 10000 байт и получить меньше, что потребует нескольких приемов для получения всего сообщения. Также возможно сделать две отправки и получить обе в одном приеме или даже всю одну отправку и часть другой. В вашем примере простая буферизация всех полученных сообщений до тех пор, пока в сообщении не появится \n, подойдет для простого протокола.

person Mark Tolonen    schedule 17.12.2011
comment
Здорово. Спасибо за советы. Это было мое первое использование класса socketserver, и я не совсем понял, что делает дескриптор вне вызова. Мои первоначальные тесты на самом деле использовали ThreadingTCPServer, но я переключился на один поток, чтобы убедиться, что простой (выложенный) сервер работает. Еще раз спасибо. - person Adam Lewis; 18.12.2011