最近,生活使我与 UDP 的联系比对UDP
/ TCP
/区别的一般理解多一点HTTP
,我不得不在一个独立的 Python 玩具中开始编写服务器和客户端同步,这是我有生以来的第一次。为了开展这项业务,我使用了socket
. 当然,我一直都知道,在赤裸裸的UDP
情况下是没有这种东西的connection
,一开始我尝试发送一个数据包,并立即期待结果
sock.sendto(b'hi', target)
resp = sock.recv(1028)
那一刻,我已经意识到我什么都不懂。代码似乎可以工作,但问题隐藏在我身上,服务器将如何回答我?纯粹出于逻辑原因
_, addr = self.sock.recvfrom(1024)
sock.sendto(b'accepted', addr)
令我惊讶的是,它当然可以在 Internet 上而不是在本地网络上运行,我看到每秒 0 到 20 个数据包的速度非常糟糕,这对于这项任务来说是不可接受的。我的头脑风暴的整个过程我就不描述了,但是我独立得出结论,需要将逻辑分成两个线程,并将结果扔到一个受保护的列表中,第三个线程会从中获取它需要的数据. 一切都开始了,欢呼,那是在早上,我上床睡觉,闭上眼睛看到这个:
# client.py
while self.running:
resp = sock.recv(1028)
当我意识到这是一个死业务时,我继续研究,因为我意识到我没有专用IP,我养了一个HTTP服务器,没有人会从互联网上找到它,它没有socket.bind
成功Windows 以一种愚蠢的方式向我们抛出异常OSError: Требуемый адрес для своего контекста неверен
。有了这一切,在我发送到服务器的唯一数据包之后,服务器可以获得我的 IP 和端口,然后可以使用它来发送数据。
# client.py
target = ('x.x.x.x', 8080)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.settimeout(1.0)
# `Connection` for send ip:port
sock.sendto(b'hi', target)
while True:
try:
req = sock.recv(1028)
print(req)
except socket.timeout:
continue
# server.py
target = ('x.x.x.x', 8080)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind(target)
i = 0
last = time()
print("Waiting message...")
_, addr = sock.recvfrom(1028)
while True:
sleep(0.1);
sock.sendto(b"connect", addr)
i += 1
if time() - last > 1:
print("> Send {} packages per second to {}:{}".format(i, addr[0], addr[1]))
i = 0
last = time()
最后,问题本身
如果我没有专用 IP,服务器如何向我发送数据包?为什么服务器TCP
/HTTP
没有专用服务器从 Internet 上看不到,但裸服务器UDP
以某种方式从 Internet 接收数据包到其 IP 及其某个随机端口?如果我想在特定端口上接收数据包,为什么socket.bind
它不起作用?
正如@andreymal 已经告诉你的那样,NAT适用于 provider 。提供者有一个专用地址。NAT执行
inner_host:inner_port
->outer_port
映射,其中inner_host和inner_port是 ISP 本地网络上主机的地址和端口(读取,您的本地地址),而outer_port是公开给互联网的端口。在提供商的本地网络中,您的计算机具有某个地址A(例如,
192.168.0.103
)。当您发送一个数据包时,您必须从某个端口P发送它。数据包的目的地也是某种 ip 和端口。NAT看到从本地地址A和端口P发送了一个请求,选择了一些伸出到 Internet 的空闲端口Q并记住外部端口Q后面是内部地址A和端口P (
A:P
->Q
)。那些。实际上,远程主机接收到一个数据包,其中
source_ip
包含提供程序的外部地址,并且在source_port
外部端口Q中,提供程序从该端口发送您的数据包。将来,当提供者在端口Q上接收到数据包时(即远程主机会响应您,因为他们从该端口收到了数据包),NAT将知道该数据包是发给您的主机(A: P)的,并将从端口Q向您发送数据包。
有端口转发之类的东西。这与到达端口Q的数据包总是以内部主机A:P为目的地的硬编码相同,但正如您再次被告知的那样,ISP 可能不想为此烦恼。