【问题标题】:Client cannot receive UDP message客户端无法接收 UDP 消息
【发布时间】:2020-10-28 07:25:46
【问题描述】:

我是使用 python 进行套接字编程的初学者。我正在做我的课程项目。我的部分项目需要使用不同的端口发送和接收 UDP 消息。提供了名为robot 的服务器程序,我需要编写名为student 的客户端程序,它可以与机器人交互。因此,我无法在服务器程序中显示所有源代码。

这是服务端程序中与UDP套接字相关的部分

############################################################################# phase 3
# Create a UDP socket to send and receive data
print ("Preparing to receive x...")
addr = (localhost, iUDPPortRobot)
s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s3.bind(addr)

x, addr = s3.recvfrom(1)
print ("Get x = %d" % (int(x)))
############################################################################# phase 3

time.sleep(1)
print ("Sending UDP packets:")

messageToTransmit = ""
for i in range(0,int(x) * 2):
    messageToTransmit += str(random.randint(0,9999)).zfill(5)
print ("Message to transmit: " + messageToTransmit)

for i in range(0,5):
    s3.sendto(messageToTransmit.encode(),(studentIP,iUDPPortStudent))
    time.sleep(1)
    print ("UDP packet %d sent" %(i+1))
    
############################################################################# phase 4

这是我的客户程序。 s3 是 UDP 套接字。我可以成功地向服务器程序发送消息,但我无法从它接收消息。这是因为端口的不同吗?如果是,我应该怎么做才能修复它?

import os
import subprocess
import socket
import random
import time

sendPort = 3310
localhost = '127.0.0.1'
socket.setdefaulttimeout(10)

command = "python robot_for_python_version_3.py"
subprocess.Popen(command)
print("ROBOT IS STARTED")

sendSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sendSocket.connect((localhost, sendPort))
studentId = '1155127379'
sendSocket.send(studentId.encode())

s_2Port = sendSocket.recv(5)
sendSocket.close()

s_2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s_2.bind((localhost, int(s_2Port)))
s_2.listen(5)
s2, address = s_2.accept()
s_2.close()

step4Port = s2.recv(12)
iUDPPortRobot, dummy1 = step4Port.decode().split(",")
iUDPPortStudent, dummy2 = dummy1.split(".")

s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
num = random.randint(5,10)
time.sleep(3)
s3.sendto(str(num).encode(), (localhost, int(iUDPPortRobot)))
print("Test1")

charStr = s3.recvfrom(1024)
print("Test2")
print(charStr)



exit()

【问题讨论】:

    标签: python sockets udp


    【解决方案1】:

    您未收到消息的原因是服务器将其发送到未侦听消息的端点。由于协议是 UDP(无保证等),服务器将消息成功发送到非监听端点,而监听端点则一直等待。

    更详细地说,x, addr = s3.recvfrom(1) 返回的addr不是 (studentIP, iUDPPortStudent)。试试下面看看有什么区别(注意你省略了iUDPPortRobot被定义和共享的部分,我把它设置为50000为了便于说明):

    # in one interactive session 1 (terminal), let's call it session 1 
    >>> import socket
    >>> import random 
    >>> import time
    >>> 
    >>> iUDPPortRobot = 50000
    >>> addr = ('localhost', iUDPPortRobot)
    >>> s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    >>> s3.bind(addr)
    >>> x, addr = s3.recvfrom(1) # <= this will block
    
    # in another interactive session (terminal), let's call it session 2
    >>> import socket
    >>> import random 
    >>> import time
    >>> 
    >>> iUDPPortRobot = 50000
    >>> s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    >>> num = random.randint(5,10)
    >>> s3.sendto(str(num).encode(), ('localhost', int(iUDPPortRobot))) # <= this will unblock recvfrom in session 1, i.e., the message got received
    1
    
    # back to session 1
    >>> addr <= check address, this is the main issue you are facing
    ('127.0.0.1', 60911)
    >>> messageToTransmit = ""
    >>> for i in range(0,int(x) * 2):
    ...     messageToTransmit += str(random.randint(0,9999)).zfill(5)
    ... 
    >>> print ("Message to transmit: " + messageToTransmit)
    Message to transmit: 06729020860821106419048530205105224040360495103025
    
    # back to session 2, let's prepare for receiving the message
    >>> charStr = s3.recvfrom(1024) # <= this will block 
    
    # back to session 1 to send a message
    # you do not share what (studentIP,iUDPPortStudent), but from 
    # what you describe it is not ('127.0.0.1', 60911), let's say
    # studentIP = 'localhost' and iUDPPortStudent = 50001
    >>> studentIP = 'localhost'
    >>> iUDPPortStudent = 50001
    # now let send a message that will be sent successfully but not received, i.e.,
    # it will not unblock recvfrom in session 2
    >>> s3.sendto(messageToTransmit.encode(),(studentIP,iUDPPortStudent))
    50
    # ... but if try to send to the listening endpoint it will get received
    >>> s3.sendto(messageToTransmit.encode(), addr)
    50
    
    # back to session 2, to check things
    >>> charStr
    (b'06729020860821106419048530205105224040360495103025', ('127.0.0.1', 50000)) # <= SUCCESS
    

    有两种方法可以解决此问题。上面显示的涉及更改服务器代码,这基本上涉及上面显示的内容,即通过修改传递给s3.sendto的地址将消息发送到侦听端点。如果我理解正确,那么当您尝试编写客户端代码时,这不是一个选项。第二种方式是将消息发送到(studentIP, iUDPPortStudent),但在另一端有一个监听端点。如果您的“客户端”程序知道studentIPiUDPPortStudent,我假设是这种情况,您可以添加类似于您在服务器程序代码sn-p 顶部的代码。

    具体来说,在charStr = s3.recvfrom(1024) 的位置添加类似:

    addr = (studentIP, iUDPPortStudent)
    s4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s4.bind(addr)
    
    charStr = s4.recvfrom(1024) # <= this will block and unblock when you send the message using s3.sendto(messageToTransmit.encode(),(studentIP,iUDPPortStudent))
    

    为了完整起见,您需要将localhost 更改为'localhost',如果在您的实验中遇到OSError: [Errno 98] Address already in use,您将不得不等待TIME-WAIT 期间通过或设置SO_REUSEADDR 标志添加s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 之前 bind

    【讨论】: