我認爲這是可以做到的。但我不確定保證是什麼。
的基本思路是:
- 創建服務器插座,在其上設置
SO_REUSEADDR
,將其綁定到 服務器端口,並等待接收上的數據。
recvfrom
爲您提供下一個收到的數據報和它發送的 的地址。
- 將初始數據報和地址傳遞給新線程。
- 在新線程中,創建一個新的套接字(再次與
SO_REUSEADDR
綁定)。現在把connect
的新套接字給對方地址。
- 回到步驟2
這裏的想法是,你始終保持一個「未連接」插座。當你得到一個新的對等體時,你創建一個連接到該對等體的新套接字。當套接字專門連接到遠程對等體時,內核會將來自該對等體的傳入數據報傳遞給該套接字。否則,它會將數據報傳送到未連接的套接字。
我不是100%確定的事情是:操作系統保證只將後續數據報傳遞給連接的套接字而不是未連接的套接字?它是有道理的,它會這樣工作,它似乎在我的Linux機器上工作 - 請參閱下面的python3代碼(可以使用nc -u localhost 9999
作爲客戶端)。
#!/usr/bin/env python3
from threading import Thread
import socket
def client_thread(addr, msg):
# Create a new socket and connect it to the peer.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 9999)) # Maybe omit -- see below
sock.connect(addr)
# Maybe set SO_RCVTIMEO to cause timeout on receive and
# cause client thread to bail?
while True:
sock.send(msg)
msg = sock.recv(1024)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 9999))
threads = []
while True:
# Receive datagram on unconnected socket
msg, addr = sock.recvfrom(1024)
# Send connected socket and datagram
th = Thread(target=client_thread, args=(addr, msg))
th.start()
threads.append(th)
你大概會有一些殺死子線程的方式,這樣他們就不會永遠掛在你的客戶端消失了。
或者,您可以省略綁定client_thread
中的新套接字,這將導致內核爲新套接字中的本地地址選擇一個未使用的端口,但是客戶端需要注意它的地址收到最初的迴應並將未來的數據報導向該地址。這將完成同樣的事情,並在很多方面變得更清潔。
的客戶端可以是這樣的(再次使用python演示):
#!/usr/bin/env python3
import socket
# Initial request sent to well-known port
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ('localhost', 9999)
for n in range(5):
print('Send to {}'.format(addr))
sock.sendto(b'Hello', addr)
response, addr = sock.recvfrom(1024)
# Subsequent requests sent to whoever responded to last request
print('Got response {} from {}'.format(response, addr))
你能嗎?爲什麼不?你可以產生新的線程來做你想做的事情。 –
你可以;但是,目前還不清楚這樣做是否有好處。單線程可以使用單個套接字處理任意數量的UDP客戶端。 –
@JeremyFriesner當你將套接字包裝在BIO和SSL對象中時,你可以在OpenSSL中...所以我想知道是否可以在這裏完成相同的工作。 – user2584587