Python 中的套接字编程
Socket Programming in Python
我正在使用 python 进行概念验证,它模拟使用套接字发送 UDP 数据包的 server/client 通信。我可以很容易地做一个简单的客户端到服务器并返回到客户端的通信,但我试图在该通信中引入 "middle-man" 。从概念上讲,问题可以描述为,如果 "Joe" 是主要客户端,他将向 "Steve" 发送一条消息,中间人将在将消息发送到 [=35= 之前对该消息进行处理。 ] 作为服务器处理新消息并将响应发送回中间人 "Steve"。最终,中间人会将该消息发送到其他地方,但目前我并不担心。
我当前的代码如下:
"Joe"(原客户端)长得像
# create dgram udp socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
except socket.error:
print ('Failed to create socket')
sys.exit()
host = 'localhost'
port = 8888
print("start comms")
while 1:
arr = ['Dog', 'cat', 'treE', 'Paul']
num = random.randrange(0,4)
#Send the string
s.sendto(arr[num].encode(), (host, port))
"Steve"(中间人)长得像
host = ''
hostRT = 'localhost'
portVM = 8888
portRT = 8752
# socket to receive from "Joe"
s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s1.bind((host, portVM))
# socket to send to "Carol"
s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print("start comms")
while 1:
# receive from "Joe"
data = s1.recvfrom(1024)
num = data[0].decode()
addrVM = data[1]
# print data from Joe
print(num)
# add some excitement to Joe's message
num += '!!!'
# show received message address + port number
print ("message[" + addrVM[0] + ":" + str(addrVM[1]) + ']')
# Send to "Carol"
s2.sendto(num.encode(), (hostRT, portRT))
# receive from "Carol"
d = s2.recvfrom(1024)
reply = d[0].decode()
addrRT = d[1]
# show received message address + port number
print ("message[" + addrRT[0] + ":" + str(addrRT[1]) + ']')
# show Carol's response
print ('Server reply : ' + reply)
s1.close()
s2.close()
"Carol"(服务器)看起来像
host = ''
port = 8752
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print ("socket created")
s.bind((host, port))
print ("Socket bind complete")
while 1:
d = s.recvfrom(1024)
data = d[0].decode()
addr = d[1]
print(data)
reply = "Upper case client data = " + data.upper()
print(reply)
s.sendto(reply.encode(), addr)
print ("message[" + addr[0] + ":" + str(addr[1]) + '] - ' + data.strip())
s.close()
目前我可以收到来自 Joe 的消息,但它在发送到服务器 Carol 时挂起。我对套接字编程完全陌生,因此非常感谢任何帮助。
编辑澄清
使用Python3.4
Joe 正在不停地发送数据包,以模拟此概念验证所针对的真实应用程序。 Joe 将以大约 1 个数据包/4 毫秒的速率发送数据包,但我只关心最近的数据包。然而,由于从 Steve 到 Carol 的往返平均周转时间约为 10 毫秒,我最初想将 Joe 的最新数据包缓存在本地内存位置并覆盖该位置,直到 Steve 准备好向 Carol 发送数据包一次她已经回应了最后一个数据包。但是,对于这个简单的概念证明,我还没有尝试实现它。对此的任何建议也会有所帮助。
有多个故障导致了整体故障,其中一些故障并不明显(即它在其他地方崩溃之前一直在工作)。
首先,目前以最快的速度发送数据包。仅此一项就可能导致其他地方出现严重的数据包丢失(这可能是一件好事,因为您现在必须确保您的代码在数据包丢失后仍然存在)。除非你真的想对网络施加压力,否则 time.sleep(0.1)
之类的东西在发送循环中是合适的。
更重要的是,史蒂夫的套接字设置一团糟。他最多需要两个插座,而不是三个。目前的设置方式是,carol 向 steve 回答她从中获取数据包的 IP 地址和端口(这是非常明智的),但 steve 在一个从未收到数据发送到的不同套接字上读取。
更糟糕的是,steve 的 s3
监听的端口实际上是 carol 使用的同一个端口!既然你是 not using multicast。您可以简单地从代码中删除对 s3
的所有引用并专门使用 s2
。
另一个问题是你不处理数据包丢失。例如,如果数据包在 steve 和 carol 之间丢失,代码
# Send to "Carol"
s2.sendto(num.encode(), (hostRT, portRT))
# receive from "Carol"
d = s2.recvfrom(1024) # s3 in the original
将永远挂起,因为 Carol 在丢失的数据包之后没有发送任何新数据包。如前所述,数据包丢失的可能性更大,因为 joe 正在尽可能快地发送数据包。
要检测数据包丢失,有几个选项:
- 使用多个 threads or processes 发送和接收。这将使您的程序更加复杂。
- 切换到异步/非阻塞IO,在Python与高层asyncore or the more low-level select。
- 设置并正确处理socket timeouts。这可能是目前最简单的选择,但非常有限。
- 如果您确实需要可靠的通信,请切换到 TCP。
除了前面提到的网络问题,还有一些潜在的问题或不准确:
- 如果您使用 Python 2.x,
decode
和 encode
对字符串的行为取决于系统配置。与许多其他潜在问题一样,这已在 Python 3.x 中通过强制使用 UTF-8 得到修复(在 2.x 中你必须明确要求)。对于您的情况,只要您只发送 ASCII 字符就可以了。
while(1) :
在 Python 中看起来真的很奇怪 - 为什么 在 参数之后的空格,以及为什么括号。为什么不是 while 1:
或 while True:
?
您可以使用 tuple unpacking 来达到很好的效果。而不是
数据=s1.recvfrom(1024)
num = 数据[0].decode()
addrVM = 数据[1]
怎么样:
data, addrVM = s1.recvfrom(1024)
num = data.decode('utf-8')
我正在使用 python 进行概念验证,它模拟使用套接字发送 UDP 数据包的 server/client 通信。我可以很容易地做一个简单的客户端到服务器并返回到客户端的通信,但我试图在该通信中引入 "middle-man" 。从概念上讲,问题可以描述为,如果 "Joe" 是主要客户端,他将向 "Steve" 发送一条消息,中间人将在将消息发送到 [=35= 之前对该消息进行处理。 ] 作为服务器处理新消息并将响应发送回中间人 "Steve"。最终,中间人会将该消息发送到其他地方,但目前我并不担心。
我当前的代码如下:
"Joe"(原客户端)长得像
# create dgram udp socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
except socket.error:
print ('Failed to create socket')
sys.exit()
host = 'localhost'
port = 8888
print("start comms")
while 1:
arr = ['Dog', 'cat', 'treE', 'Paul']
num = random.randrange(0,4)
#Send the string
s.sendto(arr[num].encode(), (host, port))
"Steve"(中间人)长得像
host = ''
hostRT = 'localhost'
portVM = 8888
portRT = 8752
# socket to receive from "Joe"
s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s1.bind((host, portVM))
# socket to send to "Carol"
s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print("start comms")
while 1:
# receive from "Joe"
data = s1.recvfrom(1024)
num = data[0].decode()
addrVM = data[1]
# print data from Joe
print(num)
# add some excitement to Joe's message
num += '!!!'
# show received message address + port number
print ("message[" + addrVM[0] + ":" + str(addrVM[1]) + ']')
# Send to "Carol"
s2.sendto(num.encode(), (hostRT, portRT))
# receive from "Carol"
d = s2.recvfrom(1024)
reply = d[0].decode()
addrRT = d[1]
# show received message address + port number
print ("message[" + addrRT[0] + ":" + str(addrRT[1]) + ']')
# show Carol's response
print ('Server reply : ' + reply)
s1.close()
s2.close()
"Carol"(服务器)看起来像
host = ''
port = 8752
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print ("socket created")
s.bind((host, port))
print ("Socket bind complete")
while 1:
d = s.recvfrom(1024)
data = d[0].decode()
addr = d[1]
print(data)
reply = "Upper case client data = " + data.upper()
print(reply)
s.sendto(reply.encode(), addr)
print ("message[" + addr[0] + ":" + str(addr[1]) + '] - ' + data.strip())
s.close()
目前我可以收到来自 Joe 的消息,但它在发送到服务器 Carol 时挂起。我对套接字编程完全陌生,因此非常感谢任何帮助。
编辑澄清
使用Python3.4
Joe 正在不停地发送数据包,以模拟此概念验证所针对的真实应用程序。 Joe 将以大约 1 个数据包/4 毫秒的速率发送数据包,但我只关心最近的数据包。然而,由于从 Steve 到 Carol 的往返平均周转时间约为 10 毫秒,我最初想将 Joe 的最新数据包缓存在本地内存位置并覆盖该位置,直到 Steve 准备好向 Carol 发送数据包一次她已经回应了最后一个数据包。但是,对于这个简单的概念证明,我还没有尝试实现它。对此的任何建议也会有所帮助。
有多个故障导致了整体故障,其中一些故障并不明显(即它在其他地方崩溃之前一直在工作)。
首先,目前以最快的速度发送数据包。仅此一项就可能导致其他地方出现严重的数据包丢失(这可能是一件好事,因为您现在必须确保您的代码在数据包丢失后仍然存在)。除非你真的想对网络施加压力,否则 time.sleep(0.1)
之类的东西在发送循环中是合适的。
更重要的是,史蒂夫的套接字设置一团糟。他最多需要两个插座,而不是三个。目前的设置方式是,carol 向 steve 回答她从中获取数据包的 IP 地址和端口(这是非常明智的),但 steve 在一个从未收到数据发送到的不同套接字上读取。
更糟糕的是,steve 的 s3
监听的端口实际上是 carol 使用的同一个端口!既然你是 not using multicast。您可以简单地从代码中删除对 s3
的所有引用并专门使用 s2
。
另一个问题是你不处理数据包丢失。例如,如果数据包在 steve 和 carol 之间丢失,代码
# Send to "Carol"
s2.sendto(num.encode(), (hostRT, portRT))
# receive from "Carol"
d = s2.recvfrom(1024) # s3 in the original
将永远挂起,因为 Carol 在丢失的数据包之后没有发送任何新数据包。如前所述,数据包丢失的可能性更大,因为 joe 正在尽可能快地发送数据包。
要检测数据包丢失,有几个选项:
- 使用多个 threads or processes 发送和接收。这将使您的程序更加复杂。
- 切换到异步/非阻塞IO,在Python与高层asyncore or the more low-level select。
- 设置并正确处理socket timeouts。这可能是目前最简单的选择,但非常有限。
- 如果您确实需要可靠的通信,请切换到 TCP。
除了前面提到的网络问题,还有一些潜在的问题或不准确:
- 如果您使用 Python 2.x,
decode
和encode
对字符串的行为取决于系统配置。与许多其他潜在问题一样,这已在 Python 3.x 中通过强制使用 UTF-8 得到修复(在 2.x 中你必须明确要求)。对于您的情况,只要您只发送 ASCII 字符就可以了。 while(1) :
在 Python 中看起来真的很奇怪 - 为什么 在 参数之后的空格,以及为什么括号。为什么不是while 1:
或while True:
?您可以使用 tuple unpacking 来达到很好的效果。而不是
数据=s1.recvfrom(1024) num = 数据[0].decode() addrVM = 数据[1]
怎么样:
data, addrVM = s1.recvfrom(1024)
num = data.decode('utf-8')