Python клиентский сокет подключается, даже если сервер не принимает новое подключение, а размер невыполненного журнала равен 0 - PullRequest
1 голос
/ 26 мая 2020

Сейчас экспериментирую с сокетами в python. Я пробовал следующие 3 варианта (пропустите код, чтобы сначала прочитать вопрос):

class Server(threading.Thread):

    def __init__(self):
        super(Server, self).__init__()
        self.ip = "localhost"
        self.port = 23071
        self.connectionNumber = 0

    def run(self):
        self.server= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((self.ip, self.port))
        self.server.listen(self.connectionNumber)

        print("SERVER: Server is running")

        print("SERVER: Waiting for connection")
        client, addr = self.server.accept()
        print("SERVER: Something connected at {}".format(addr))

        time.sleep(10) # Simulating doing something

        client.close()
        self.server.close()
        print("Server is closed")

class Client(threading.Thread):

    def __init__(self):
        super(Client, self).__init__()
        self.ip = "localhost"
        self.port = 23071

    def run(self):
        time.sleep(3) #Ensure the client socket is created after the server socket

        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        try:
            print("CLIENT. Trying to connect")
            self.server.connect((self.ip, self.port))
            print("CLIENT. Connection sucessful")

            time.sleep(2) # SImulating doing something
        except Exception as e:
            print("CLIENT: Exception \"{}\" happend".format(e))
        finally:
            self.server.close()
            print("CLIENT: Socket closed")

if __name__ == "__main__":
    server = Server()
    client = Client()

    server.start()
    client.start()

    server.join()
    client.join()
class Server(threading.Thread):

    def __init__(self):
        super(Server, self).__init__()
        self.ip = "localhost"
        self.port = 23071
        self.connectionNumber = 0

    def run(self):
        time.sleep(3) #Ensure server socket is created after client socket
        self.server= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((self.ip, self.port))
        self.server.listen(self.connectionNumber)

        print("SERVER: Server is running")

        print("SERVER: Waiting for connection")
        client, addr = self.server.accept()
        print("SERVER: Something connected at {}".format(addr))

        time.sleep(10) # Simulating doing something

        client.close()
        self.server.close()
        print("Server is closed")

class Client(threading.Thread):

    def __init__(self):
        super(Client, self).__init__()
        self.ip = "localhost"
        self.port = 23071

    def run(self):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        try:
            print("CLIENT. Trying to connect")
            self.server.connect((self.ip, self.port))
            print("CLIENT. Connection sucessful")

            time.sleep(2) # SImulating doing something
        except Exception as e:
            print("CLIENT: Exception \"{}\" happend".format(e))
        finally:
            self.server.close()
            print("CLIENT: Socket closed")
class Server(threading.Thread):

    def __init__(self):
        super(Server, self).__init__()
        self.ip = "localhost"
        self.port = 23071
        self.connectionNumber = 0

    def run(self):
        self.server= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((self.ip, self.port))
        self.server.listen(self.connectionNumber)

        print("SERVER: Server is running")

        #Ensure the server socket is created when the client wants to make a connection
        #but the server isn't waiting for new connections when the client establishes a connection
        #(.accept() call delayed)
        time.sleep(3)

        print("SERVER: Waiting for connection")
        client, addr = self.server.accept()
        print("SERVER: Something connected at {}".format(addr))

        time.sleep(10) # Simulating doing something

        client.close()
        self.server.close()
        print("Server is closed")

class Client(threading.Thread):

    def __init__(self):
        super(Client, self).__init__()
        self.ip = "localhost"
        self.port = 23071

    def run(self):
        time.sleep(1) #Ensure client socket is created after server socket
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        try:
            print("CLIENT. Trying to connect")
            self.server.connect((self.ip, self.port))
            print("CLIENT. Connection sucessful")

            time.sleep(2) # SImulating doing something
        except Exception as e:
            print("CLIENT: Exception \"{}\" happend".format(e))
        finally:
            self.server.close()
            print("CLIENT: Socket closed")

if __name__ == "__main__":
    server = Server()
    client = Client()

    server.start()
    client.start()

    server.join()
    client.join()

В первом варианте соединение устанавливается без проблем. Я ожидал, что это будет так, потому что сервер ожидает новых подключений с помощью .accept (). Во втором варианте соединение не может быть установлено sh из-за того, что еще нет серверного сокета. Соединение не может достичь цели (происходит исключение. WinError 1106..something). Тоже ожидаемо. Но третий меня смущает. Я ожидал, что соединение не может быть установлено из-за того, что сокет сервера существует, но сервер еще не принимает новые соединения (с .accept () не вызывается) и макс. количество подключений в очереди - 0 (.listen (0)). Тем не менее, вызов .connect () клиента не блокирует и не генерирует исключение. В нем говорится: «КЛИЕНТ: соединение успешно». Что случилось? Я ожидал, что вызов заблокируется, потому что я никогда не указывал тайм-аут, и соединение никогда не будет принято.

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

Приветствую вас, Tmirror

EDIT:

После дальнейших исследований с wirehark я обнаружил следующее:

  1. server.listen (x) "запускает" сокет. С этого момента серверный сокет может выполнять трехстороннее рукопожатие, отвечая клиенту, инициирующему трехстороннее рукопожатие.

  2. server.listen (x) допускает до x элементов в отставание. Это означает, что до x элементов могут выполнять трехстороннее рукопожатие. После его выполнения или во время него они помещаются в очередь в бэклог. Они удаляются из очереди, если серверный сокет вызывает .accept (). Таким образом, очередь невыполненных работ всегда содержит клиентское соединение, которое уже выполнило трехстороннее рукопожатие или делает это в настоящее время. x определяет размер этой очереди невыполненных работ.

  3. Похоже, server.listen (0) имеет тот же эффект, что и server.listen (1). Только одно соединение сможет выполнить трехстороннее рукопожатие и будет содержаться в очереди невыполненных работ. Поэтому я предполагаю, что реализация сокета python такова, что .listen (...) должен иметь значение аргумента не менее 1. Я предполагаю, что оно настраивается на 1, если оно ниже.

Поэтому я хотел бы немного изменить свой вопрос. Теперь совершенно очевидно, почему socket.connect () не генерирует исключение или блокируется. Он может успешно выполнить трехстороннее рукопожатие, и соединение открыто. Но что тогда делает socket.accept ()? Очевидно, что он берет один элемент из очереди невыполненных работ, но что это означает с точки зрения связи между сервером и клиентом и с точки зрения протокола tcp?

1 Ответ

1 голос
/ 27 мая 2020

Но что тогда делает socket.accept ()? Очевидно, что он берет один элемент из очереди невыполненных работ, но что это означает с точки зрения связи между сервером и клиентом и с точки зрения протокола TCP?

Он ничего не делает связь между сервером и клиентом и по протоколу tcp . Он просто связывает файловый дескриптор процесса с подключением, то есть устанавливает структуру данных операционной системы. Мы можем увидеть эффект с netstat, например, с Windows с опцией -b или -o.

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