#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
.