Есть несколько проблем с вашим кодом:
Прежде всего вам нужен цикл над тем, что отправляет клиент.Итак, вы сначала connection, client_address = sock.accept()
, и теперь у вас есть клиент.Но на следующей итерации цикла вы снова .accept()
перезаписываете свой старый connection
новым клиентом.Если нового клиента нет, он просто ждет вечно.И это то, что вы наблюдаете.
Так что это можно исправить следующим образом:
while True:
conn, addr = sock.accept()
while True:
data = conn.recv(1024)
, но у этого кода есть другая проблема: новый клиент не может подключиться, пока старый не отключится (ну, вв тот момент, когда он просто зацикливается бесконечно, независимо от того, жив клиент или нет, мы разберемся с этим позже).Чтобы преодолеть это, вы можете использовать потоки (или асинхронное программирование) и обрабатывать каждого клиента независимо .Например:
from threading import Thread
def client_handler(conn):
while True:
data = conn.recv(1024)
while True:
conn, addr = sock.accept()
t = Thread(target=client_handler, args=(conn,))
t.start()
Асинхронное программирование сложнее, и я не буду его здесь рассматривать.Просто имейте в виду, что асинхронность по сравнению с потоками имеет несколько преимуществ (вы можете гуглить их).
Теперь у каждого клиента есть свой собственный поток, а основной поток беспокоится только о принятии соединений.Вещи происходят одновременно.Пока все хорошо.
Давайте сосредоточимся на функции client_handler
.То, что вы не понимаете, это как работают сокеты.Это:
data = conn.recv(1024)
не не читает 1024 байта из буфера.На самом деле он читает от до 1024 байта с 0, также возможно.Даже если вы отправите 1024 байта, он все равно может прочитать, скажем, 3. А когда вы получите буфер длины 0, это означает, что клиент отключился.Итак, в первую очередь вам нужно это:
def client_handler(conn):
while True:
data = conn.recv(1024)
if not data:
break
Теперь начинается настоящее веселье.Даже если data
не пусто , это может быть произвольной длины от 1 до 1024. Ваши данные могут быть разделены на части и могут потребовать нескольких вызовов .recv
.И нет, Вы ничего не можете с этим поделать .Чанкинг может происходить из-за некоторых других прокси-серверов или маршрутизаторов, из-за задержки в сети, космического излучения или чего-то еще.Вы должны быть готовы к этому.
Так что для правильной работы с этим вам нужно надлежащий протокол кадрирования.Например, вы должны как-то узнать, насколько велик входящий пакет (чтобы вы могли ответить на вопрос «прочитал ли я все, что мне нужно?»).Один из способов сделать это - поставить перед каждым кадром, скажем, 2 байта, которые объединяются в общую длину кадра.Код может выглядеть так:
def client_handler(conn):
while True:
chunk = conn.recv(1) # read first byte
if not chunk:
break
size = ord(chunk)
chunk = conn.recv(1) # read second byte
if not chunk:
break
size += (ord(chunk) << 8)
Теперь вы знаете, что входящий буфер будет иметь длину size
.При этом вы можете читать все циклы:
def handle_frame(conn, frame):
if frame.find("EVNTTAG") != -1:
pass # do your stuff here now
def client_handler(conn):
while True:
chunk = conn.recv(1)
if not chunk:
break
size = ord(chunk)
chunk = conn.recv(1)
if not chunk:
break
size += (ord(chunk) << 8)
# recv until everything is read
frame = b''
while size > 0:
chunk = conn.recv(size)
if not chunk:
return
frame += chunk
size -= len(chunk)
handle_frame(conn, frame)
ВАЖНО: это всего лишь пример обработки протокола, в котором префикс каждого кадра соответствует его длине.Обратите внимание, что клиент также должен быть настроен .Вы должны либо определить такой протокол, либо, если у вас есть данный, вы должны прочитать спецификацию и попытаться понять, как работает кадрирование.Например, это делается совсем по-другому с HTTP.В HTTP вы читаете, пока не встретите \r\n\r\n
, который сигнализирует об окончании заголовков.Затем вы проверяете заголовки Content-Length
или Transfer-Encoding
(не говоря уже о таких жестких вещах, как переключение протокола), чтобы определить следующее действие.Это становится довольно сложным, хотя.Я просто хочу, чтобы вы знали, что есть другие варианты.Тем не менее кадрирование необходимо.
Также сетевое программирование сложно.Я не собираюсь погружаться в такие вещи, как безопасность (например, от DDOS) и производительность.Приведенный выше код следует рассматривать как крайнее упрощение, а не готовый продукт.Я советую использовать какой-нибудь существующий софт.