Клиент не может получить UDP-сообщение

#python #sockets #udp

#python #сокеты #udp

Вопрос:

Я новичок в программировании сокетов с использованием 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()
  

Ответ №1:

Причина, по которой вы не получаете сообщение, заключается в том, что сервер отправляет его на конечную точку, которая не прослушивает сообщения. Поскольку протокол UDP (никаких гарантий и т. Д.), Сервер успешно отправляет сообщение на не прослушивающую конечную точку, В то время как прослушивающая конечная точка ожидает вечно.

Более подробно, addr поскольку возвращаемый x, addr = s3.recvfrom(1) не (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) , но иметь конечную точку прослушивания на другом конце. Если studentIP и iUDPPortStudent известны вашей «клиентской» программе, что, я полагаю, так и есть, вы можете добавить код, аналогичный тому, что у вас есть в верхней части фрагмента кода серверной программы.

В частности, добавьте вместо 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) before bind .