简体   繁体   English

是否可以有一个套接字同时侦听两个UDP IP,一个是127.0.0.1(同一台机器),而另一台计算机呢?

[英]Is it possible to have a socket listening to two UDP IPs, one that is 127.0.0.1 (same machine) and a different computer at the same time?

An eye-tracking application I use utilizes UDP to send packets of data. 我使用的眼动追踪应用程序利用UDP发送数据包。 I made a python socket on the same computer to listen and dump the data into a .txt file. 我在同一台计算机上制作了一个python套接字,以侦听数据并将其转储为.txt文件。 I already have this much working. 我已经做了很多工作。

A separate application also written in python (what the eye-tracked subject is seeing) is running on a separate computer. 另一个用python编写的单独应用程序(眼睛跟踪的对象正在查看的内容)在单独的计算机上运行。 Because the eye-tracking application is continuous and sends unnecessary data, so far I've had to manually parse out the instances when the subject is looking at desired stimuli. 因为眼动追踪应用程序是连续的并且会发送不必要的数据,所以到目前为止,当对象正在查看所需的刺激时,我不得不手动解析出实例。 I did this based on a manually synchronized start of both the stimuli and eye-tracking applications and then digging through the log file. 我是基于手动同步启动刺激和眼动跟踪应用程序,然后再挖掘日志文件来完成此操作的。

What I want to do is have the second computer act as a second UDP client, sending a packet of data to the socket on the eye-tracking computer everytime the subject is looking at stimuli (where a marker is inserted into the .txt file previously mentioned). 我想做的是让第二台计算机充当第二个UDP客户端,每次受试者观察刺激时都会向眼动计算机上的套接字发送一个数据包(之前在.txt文件中插入了标记)提到)。 Is it possible to have a socket listening to two IP addresses at one time? 是否可以让一个套接字同时监听两个IP地址?

Here's my socket script: 这是我的套接字脚本:

#GT Pocket client program

import datetime
import socket
now = datetime.datetime.now()
filename = 'C:\gazelog_' + now.strftime("%Y_%m_%d_%H_%M") + '.txt'

UDP_IP = '127.0.0.1' # The remote host (in this case our local computer)
UDP_PORT = 6666 # The same port as used by the GT server by default

sock = socket.socket(socket.AF_INET, #internet
                                socket.SOCK_DGRAM) #UDP 

sock.bind( (UDP_IP, UDP_PORT) )

while True:
    data, addr = sock.recvfrom( 1024) #I assume buffer size is 1024 bytes.
    print "Received Message:", data
    with open(filename, "a") as myfile:
    myfile.write(str(data + "\n"))

sock.close()
myfile.close()

EDIT: 编辑:

@abarnert I was able to bind to the host address on the Ethernet interface and send a message from computer B to computer A, but computer A was no long able to receive packets from itself. @abarnert我能够绑定到以太网接口上的主机地址,并从计算机B向计算机A发送消息,但是计算机A不再能够从其自身接收数据包。 When I specified UDP_IP = '0.0.0.0' computer B was no longer able to send data across the Ethernet. 当我指定UDP_IP = '0.0.0.0'计算机B不再能够通过以太网发送数据。 When I specified UDP_IP = '' I received the `error: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted 当我指定UDP_IP = ''我收到`error: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted

This have to do with the script I used on the Computer B to send the data: 这与我在计算机B上用于发送数据的脚本有关:

import socket

UDP_IP = "169.254.35.231" # this was the host address I was able to send through.
UDP_PORT = 6666
MESSAGE = "Start"

print ("UDP target IP:"), UDP_IP
print ("UDP target port:"), UDP_PORT
print ("message:"), MESSAGE

sock = socket.socket(socket.AF_INET,
                 socket.SOCK_DGRAM) 
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT) )

I didn't know where (or if at all) I needed to specify INADDR_ANY, so I didn't. 我不知道需要在哪里(或者根本不需要)指定INADDR_ANY,所以我没有。 But I did try once where import socket.INADDR_ANY but got ImportError: No module named INADDR_ANY 但是我确实尝试过一次在哪里import socket.INADDR_ANY但出现ImportError: No module named INADDR_ANY

Seems like a simple issue based on your response, so I'm not sure where I'm messing up. 根据您的回复,这似乎是一个简单的问题,所以我不确定我在哪里搞砸。

EDIT2: I just reread your answer again and understand why socket.INADDR_ANY doesn't work. EDIT2:我只是重新阅读了您的答案,并理解了为什么socket.INADDR_ANY不起作用。 Please disregard that part of my previous edit 请忽略我先前编辑的那部分

EDIT3: Okay so the reason that I wasn't picking up data when specifying the host IP was that the application I was collecting data from on Computer A was still specified to send to 127.0.0.1. EDIT3:好的,所以我在指定主机IP时未拾取数据的原因是,我仍在计算机A上从中收集数据的应用程序仍指定发送给127.0.0.1。 So I figured it out. 所以我想通了。 I am still curious why 0.0.0.0 didn't work though! 我仍然很好奇为什么0.0.0.0不能正常工作!

No. A socket can only be bound to a single address at a time.* 否。套接字一次只能绑定到一个地址。*

If there happens to be a single address that handles both things you want, you can use a single socket to listen to it. 如果碰巧有一个地址可以同时满足您的所有需求,则可以使用一个套接字来监听它。 In this case, the INADDR_ANY host ( 0.0.0.0 ) may be exactly what you're looking for—that will handle any (IPv4) connections on all interfaces, both loopback and otherwise. 在这种情况下, INADDR_ANY主机( 0.0.0.0 )可能正是您要寻找的主机-它将处理所有接口上的任何(IPv4)连接,包括环回和其他方式。 And even if there is no pre-existing address that does what you want, you may be able to set one up via, eg, an ipfilter-type interface. 并且即使没有预先存在的地址可以满足您的要求,您也可以通过ipfilter-type接口进行设置。

But otherwise, you have to create two sockets. 但是否则,您必须创建两个套接字。 Which means you need to either multiplex with something like select , or create two threads. 这意味着您需要与select类的对象多路复用,或者创建两个线程。


In your case, you want to specify a host that can listen to both the local machine, and another machine on the same Ethernet network. 在您的情况下,您想指定一个既可以侦听本地计算机又可以侦听同一以太网网络上另一台计算机的主机。 You could get your host address on the Ethernet interface and bind that. 可以在以太网接口上获取主机地址并将其绑定。 (Your machine can talk to itself on any of its interfaces.) Usually, getting your address on "whatever interface is the default" works for this too—you'll see code that binds to socket.gethostname() in some places, like the Python Socket Programming HOWTO . (您的机器可以在其任何接口上与自己通信。)通常,在“默认接口是任何接口”上获取地址也适用于此操作—您会在某些地方看到绑定到socket.gethostname()代码,例如Python套接字编程HOWTO But binding to INADDR_ANY is a lot simpler. 但是绑定到INADDR_ANY要简单得多。 Unless you want to make sure that machines on certain interfaces can't reach you (which is usually only a problem if you're, eg, building a server intended to live on a firewall's DMZ), you'll usually want to use INADDR_ANY . 除非您要确保某些接口上的计算机无法连接到您(这通常仅是一个问题,例如,您要构建用于驻留在防火墙的DMZ上的服务器),否则通常需要使用INADDR_ANY


Finally, how do you bind to INADDR_ANY ? 最后,您如何绑定到INADDR_ANY The short answer is: just use UDP_IP = '' , or UDP_IP = '0.0.0.0' if you want to be more explicit. 简短的答案是:如果您想更明确地使用UDP_IP = ''UDP_IP = '0.0.0.0' Anyone who understands sockets, even if they don't know any Python, will understand what '0.0.0.0' means in server code.(You may wonder why Python doesn't have a constant for this in the socket module, especially when even lower-level languages like C do. The answer is that it does, but it's not really usable.**) 任何了解套接字的人,即使他们不了解任何Python,也会理解服务器代码中'0.0.0.0'含义。(您可能想知道为什么Python在socket模块中对此没有常量,尤其是当像C这样的低级语言。答案是可以的,但实际上并不实用。**)


* Note that being bound to a single address doesn't mean you can only receive packets from a single address; *请注意,绑定到单个地址并不意味着您只能从单个地址接收数据包。 it means you can receive packets from all networks where that single address is reachable. 这意味着您可以从单个地址可以到达的所有网络接收数据包。 For example, if your machine has a LAN connection, where your address is 10.0.0.100, and a WAN connection, where your address is 8.9.10.11, if you bind 10.0.0.100, you can receive packets from other LAN clients like 10.0.0.201 and 10.0.0.202. 例如,如果您的计算机具有LAN连接(地址为10.0.0.100)和WAN连接(地址为8.9.10.11),则如果绑定10.0.0.100,则可以从其他LAN客户端(如10.0)接收数据包。 0.201和10.0.0.202。 But you can't receive packets from WAN clients like 9.10.11.12 as 10.0.0.100. 但是您不能从WAN客户端(如9.10.11.12,即10.0.0.100)接收数据包。

** In the low-level sockets API, dotted-string addresses like '0.0.0.0' are converted to 32-bit integers like 0 . **在低级套接字API中,点字符串地址(例如'0.0.0.0'将转换为32位整数(例如0 Python sometimes represents those integers as int s, and sometimes as 4-byte buffers like b'\\0\\0\\0\\0' . Python有时将这些整数表示为int ,有时还表示为4字节的缓冲区,例如b'\\0\\0\\0\\0' Depending on your platform and version, the socket.INADDR_ANY constant can be either 0 or b'\\0\\0\\0\\0' . 根据您的平台和版本, socket.INADDR_ANY常数可以为0b'\\0\\0\\0\\0' The bind method will not take 0 , and may not take b'\\0\\0\\0\\0' . bind方法不会采用0 ,也可能不会采用b'\\0\\0\\0\\0' And you can't convert to '0.0.0.0' without first checking which form you have, then calling the right functions on it. 而且,如果不先检查您具有哪种格式,然后在其上调用正确的函数,就无法转换为'0.0.0.0' This is ugly. 这很丑。 That's why it's easier to just use '0.0.0.0' . 这就是为什么只使用'0.0.0.0'更容易的原因。

I believe you can bind a raw socket to an entire interface, but you appear to be using two different interfaces. 我相信您可以将原始套接字绑定到整个接口,但是您似乎正在使用两个不同的接口。

It's probably best to use two sockets with select(). 最好将两个套接字与select()一起使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM