Почему в программировании сокетов Python данные recv () непосредственно из сокетов выдают только первое сообщение? - PullRequest
0 голосов
/ 27 февраля 2019

Я играю с программированием сокетов в Python 2 и пробую select() для сценария сервера.Когда у меня есть следующий код для сервера:

print('Server started in port {}.'.format(self.port))
server_socket = socket.socket()
server_socket.bind((self.address, self.port))
server_socket.listen(5)

client_sockets = [server_socket]
while True:
    for s in client_sockets:
        if s is server_socket:
            client_socket, address = server_socket.accept()
            client_sockets.append(client_socket)

            print('Connection received.')
        else:
            data = s.recv(200)
            if data:
                print('Received: {}'.format(data.decode().strip()))
            else:
                client_sockets.remove(s)
                s.close()

Сервер получает только первое сообщение от клиента.Однако второе и последующие сообщения будут получены только после перезапуска клиента.Это сбивает с толку меня (что я приписываю своим неадекватным знаниям в области сетей)Данные, похоже, буферизированы.Почему это происходит?

Я попробовал это:

client_sockets = [server_socket]
while True:
    readable, writable, errored = select.select(client_sockets, [], [])
    for s in readable:
        if s is server_socket:
...

И, наконец, сервер теперь может получать второе и более поздние сообщения от клиента.


Вот код для клиента:

class BasicClient(object):

    def __init__(self, name, address, port):
        self.name = name
        self.address = address
        self.port = int(port)
        self.socket = socket.socket()

    def connect(self):
        self.socket.connect((self.address, self.port))
        self.socket.send(self.name)

    def send_message(self, message):
        self.socket.send(message.encode())


args = sys.argv
if len(args) != 4:
    print "Please supply a name, server address, and port."
    sys.exit()

client = BasicClient(args[1], args[2], args[3])
client.connect()
while True:
    message = raw_input('Message: ')

    # We pad the message by 200 since we only expect messages to be
    # 200 characters long.
    num_space_pads = min(200 - len(message), 200)
    message = message.ljust(num_space_pads, ' ')

    client.send_message(message)

1 Ответ

0 голосов
/ 27 февраля 2019

Здесь много проблем.

Основная проблема в том, что socket.accept() является блокирующим вызовом.Вы принимаете ваше клиентское соединение, затем читаете из него, но затем ваш цикл for возвращается обратно в сокет сервера, и ваш код застревает в accept.Только при повторном подключении к сокету код сервера перемещается вперед, следовательно, вы получаете только одно сообщение.

Лучший способ сделать это - использовать потоки.В вашем главном потоке ждите соединения (всегда ожидайте принятия).Когда появится соединение, примите его, затем создайте поток и обработайте весь трафик клиента.Затем поток блокирует ожидание только клиентского сокета и в конце концов завершается при закрытом соединении.Гораздо проще и меньше подвержено ошибкам.

Существует также проблема понятия «сообщение», так как сокеты TCP ничего не знают о структуре вашего сообщения.Они просто передают поток данных, что бы это ни было.Когда вы отправляете «сообщение» и читаете из сокета TCP, на стороне сервера происходит одно из следующих действий:

  1. Нет данных, и блоки вызовов чтения
  2. Естьровно одно сообщение, и вы получаете, что
  3. Сервер работал медленнее, чем ваш клиент, есть несколько сообщений, и вы получаете их все за один раз
  4. Сервер перепрыгнул через пушку и решил читать, когда толькочастичное сообщение было доступно
  5. Комбинация 3 и 4: вы получаете несколько полных сообщений и одно частичное

Вы должны всегда обслуживать случаи 3-5 (или использовать zeromq или что-то ещееще, который понимает концепцию сообщения, если вы не хотите реализовать это самостоятельно), а не только 1 и 2.

При чтении данных из сокета TCP всегда необходимо проверять его.Имейте «технологический» буфер и добавляйте к нему то, что вы читаете.Затем начните синтаксический анализ с самого начала и обработайте столько полных сообщений, сколько сможете найти, удалите их из буфера и оставьте остаток там.Предположение тогда, что частичное сообщение будет в конечном счете закончено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...