Ошибка сокета Python при получении данных UDP. (10054) - PullRequest
3 голосов
/ 05 апреля 2010

В настоящее время у меня проблема с использованием модулей сокетов UDP и Python. У нас есть сервер и клиенты. Проблема возникает, когда мы отправляем данные пользователю. Возможно, пользователь закрыл соединение с сервером из-за сбоя клиента, отключения интернет-провайдера или другого неправильного метода. Таким образом, можно отправлять данные в закрытый сокет.

Конечно, с UDP вы не можете определить, действительно ли достигнуты данные или они закрыты, так как это не волнует (по крайней мере, это не вызывает исключения). Однако, если вы отправляете данные, и они закрываются, вы каким-то образом возвращаете данные (???), что приводит к ошибке сокета на sock.recvfrom. [Errno 10054] Существующее соединение было принудительно закрыто удаленным хостом. Почти похоже на автоматический ответ от соединения.

Хотя это нормально, и может быть обработано блоком try: exception: (даже если это немного снижает производительность сервера). Проблема в том, что я не могу сказать, от кого это происходит или какая розетка закрыта. Есть ли способ узнать, кто (ip, socket #) отправил это? Было бы здорово, так как я мог бы мгновенно просто отключить их и удалить их из данных. Какие-либо предложения? Спасибо.

Сервер:

import socket

class Server(object):
    def __init__(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.connected = {}

    def connect(self):
        self.socket.bind(('127.0.0.1', 5579))

    def find_data(self):
        while 1:
            data, address = self.socket.recvfrom(1024)
            self.got_data(data,address)
            if self.connected.has_key(address):
                pass
            else:
                self.connected[address] = None

    def got_data(self, data, address):
        print "GOT",data,"FROM",address
        for people in self.connected:
            print people
            self.send_data('hi', people)

    def send_data(self, data, address):
        self.socket.sendto(data,address)


if __name__ == '__main__':
    server = Server()
    server.connect()
    print "NOW SEARCHING FOR DATA"
    server.find_data()

Клиент:

import socket, time

class Client(object):
    def __init__(self):
        self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    def connect(self):
        self.socket.connect(('127.0.0.1', 5579))

    def send_data(self):
        self.socket.sendto('hi',('127.0.0.1', 5579))

    def got_data(self, data, address):
        print "GOT",data,"FROM",address


if __name__ == '__main__':
    client = Client()
    client.connect()
    while 1:
        client.send_data()
        time.sleep(5)

Ответы [ 3 ]

8 голосов
/ 05 апреля 2010

Во-первых, это, возможно, зависит от платформы, и вы не упоминаете платформу, на которой вы работаете; тем не менее, 10054 - это WSAECONNRESET, поэтому я предполагаю платформу Windows какой-то.

Во-вторых, как указывалось ранее, нет связи с UDP. Ваш звонок на Connect() в клиенте просто приводит к тому, что сетевой код на вашем клиентском компьютере позволяет вам инициировать Send() вызовы вместо SendTo() вызовов и просто назначает адрес по умолчанию, на который вы отправляете данные при вводе Send() звонки по адресу, указанному для звонка на Connect().

В-третьих, я удивлен, что вы получаете WSAECONNRESET, а не ERROR_PORT_UNREACHABLE; однако основная причина, вероятно, та же. Стек UDP на удаленной машине, скорее всего, отправит ошибку ICMP Port Unreachable, если на порте, на который вы отправляете, нет открытого сокета. Итак, если ваш клиент отправляет данные, а затем закрывает сокет, а затем ваш сервер отправляет данные обратно на адрес клиента, вы получите порт, недоступный, и некоторые версии Windows могут преобразовать это в ошибку сброса соединения ...

Проблема с этими недоступными ошибками порта ICMP заключается в том, что они сообщаются через код Winsock при сбое ожидающего вызова UDP Recv / RecvFrom. Как я объясняю здесь и вопрос здесь стек UDP, очевидно, знает адрес, который сгенерировал порт, недоступный, но он не передает эту информацию вызывающей стороне, поэтому вы ничего не можете сделать сопоставить эти сообщения с чем-то полезным. Возможно, вы работаете в версии Windows, предшествующей Vista, и стек UDP делает что-то полезное с адресом, и он сообщает об ошибке в правильный сокет, но не стоит на это ставить.

Наконец, у вас все равно есть проблема; ошибка недоступности порта ICMP не доставлена ​​надежно, поэтому вы не можете точно знать, что получите ошибку, если попытаетесь отправить данные UDP удаленному клиенту. ИМХО, это означает, что вы не должны полагаться на это, даже если это иногда работает.

Вы, очевидно, пытаетесь создать какой-то протокол, ориентированный на соединение, поверх UDP (почему иначе ваш сервер будет удерживать адреса клиентов). Вам нужно будет сделать гораздо больше, чтобы создать жизнеспособное псевдосоединение по UDP, и одна из первых вещей, которые нужно понять, это то, что единственный способ узнать, когда клиент ушел, - это установить собственный тайм-аут и «отключить» ваш псевдо-соединение, если вы не слышите от них в течение определенного периода времени.

Конечно, это не отвечает на ваш вопрос; как избежать исключения Я ожидаю, что вы не можете. Причиной исключения, вероятно, является код возврата «сбой» из вызова Recv() или RecvFrom(), и сетевой код python, вероятно, преобразует все такие возвраты ошибок в исключения для вас.

2 голосов
/ 05 апреля 2010

Проблема гораздо проще, чем кажется. Используйте socket.recv () вместо socket.recvfrom () - я сделал это изменение локально, и ваш код затем работает.

1 голос
/ 05 апреля 2010

Ну, это кажется очевидным.

  1. У UDP нет соединений, поэтому Client.connect неверно
  2. Вы сохраняете адрес клиента в Server.connected dict. Когда клиент закрыт, никто не будет там, чтобы получить то, что вы отправляете.

Правильно настроить сетевое взаимодействие сложно, поскольку socket библиотека слишком низкоуровневая (это тонкая оболочка вокруг C-сокетов). Много деталей было оставлено в вашем коде. Я предлагаю попробовать библиотеку более высокого уровня, например twisted . Вот пример для UDP , с которого можно начать.

...