Сокеты в Python не являются потокобезопасными.
Вы пытаетесь решить сразу несколько проблем:
- Розетки не являются поточно-ориентированными.
- recv блокирует и блокирует основной поток.
- sendall используется из другого потока.
Вы можете решить их, используя asyncio или решив способ, которым asyncio решает его внутренне: используя select.select
вместе с socketpair
и используя очередь для входящих данных.
import select
import socket
import queue
# Any data received by this queue will be sent
send_queue = queue.Queue()
# Any data sent to ssock shows up on rsock
rsock, ssock = socket.socketpair()
main_socket = socket.socket()
# Create the connection with main_socket, fill this up with your code
# Your callback thread
def different_thread():
# Put the data to send inside the queue
send_queue.put(data)
# Trigger the main thread by sending data to ssock which goes to rsock
ssock.send(b"\x00")
# Run the callback thread
while True:
# When either main_socket has data or rsock has data, select.select will return
rlist, _, _ = select.select([main_socket, rsock], [], [])
for ready_socket in rlist:
if ready_socket is main_socket:
data = main_socket.recv(1024)
# Do stuff with data, fill this up with your code
else:
# Ready_socket is rsock
rsock.recv(1) # Dump the ready mark
# Send the data.
main_socket.sendall(send_queue.get())
Мы используем несколько конструкций здесь. Вам нужно будет заполнить пустые места с вашим кодом выбора. Что касается объяснения:
Сначала мы создаем send_queue
, который представляет собой очередь данных для отправки. Затем мы создаем пару соединенных сокетов (socketpair()
). Это понадобится нам позже, чтобы разбудить основной поток, так как мы не хотим, чтобы recv()
блокировал и предотвращал запись в сокет.
Затем мы подключаем main_socket
и запускаем поток обратного вызова. Теперь вот магия:
В основном потоке мы используем select.select
, чтобы узнать, есть ли у rsock
или main_socket
какие-либо данные. Если у одного из них есть данные, основной поток просыпается.
После добавления данных в очередь мы активируем основной поток, сигнализируя ssock
, который просыпается rsock
и, таким образом, возвращается из select.select
.
Чтобы полностью понять это, вам нужно прочитать select.select()
, socketpair()
и queue.Queue()
.