Сейчас экспериментирую с сокетами в 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 я обнаружил следующее:
server.listen (x) "запускает" сокет. С этого момента серверный сокет может выполнять трехстороннее рукопожатие, отвечая клиенту, инициирующему трехстороннее рукопожатие.
server.listen (x) допускает до x элементов в отставание. Это означает, что до x элементов могут выполнять трехстороннее рукопожатие. После его выполнения или во время него они помещаются в очередь в бэклог. Они удаляются из очереди, если серверный сокет вызывает .accept (). Таким образом, очередь невыполненных работ всегда содержит клиентское соединение, которое уже выполнило трехстороннее рукопожатие или делает это в настоящее время. x определяет размер этой очереди невыполненных работ.
- Похоже, server.listen (0) имеет тот же эффект, что и server.listen (1). Только одно соединение сможет выполнить трехстороннее рукопожатие и будет содержаться в очереди невыполненных работ. Поэтому я предполагаю, что реализация сокета python такова, что .listen (...) должен иметь значение аргумента не менее 1. Я предполагаю, что оно настраивается на 1, если оно ниже.
Поэтому я хотел бы немного изменить свой вопрос. Теперь совершенно очевидно, почему socket.connect () не генерирует исключение или блокируется. Он может успешно выполнить трехстороннее рукопожатие, и соединение открыто. Но что тогда делает socket.accept ()? Очевидно, что он берет один элемент из очереди невыполненных работ, но что это означает с точки зрения связи между сервером и клиентом и с точки зрения протокола tcp?