系统中两个不同的UDP套接字可以绑定同一个端口吗?

时间:2010-05-05 10:24:44

标签: java sockets

我有一个使用UDP连接的应用程序,现在当我尝试不止一次地运行应用程序时,它会抛出异常

java.net.BindException: Address already in use: Cannot bind

但在我的另一个使用tcp连接的应用程序中,我可以打开同一个应用程序的两个实例,并且其工作正常。为什么这个错误只与UDP连接?

编辑:

TCP socket:

Socket clientSocket= new Socket(ipAddress, 8000);
Socket clientSocket1= new Socket(ipAddress, 8000);

如果我像上面一样创建TCP套接字,使用相同的端口,它不会抛出任何错误。但是,如果我使用UDP Socket执行此操作,它会抛出异常,为什么?

3 个答案:

答案 0 :(得分:13)

它与TCP和UDP之间的区别有关。当您创建TCP套接字时,您正在创建与另一台计算机上的端口的同步客户端连接,并且当您连接到地址时,您实际上也会在套接字上获得本地端口。因此,在您的示例代码中,创建的两个套接字可能是

clientSocket = localhost:2649 <-> ipAddress:8000
clientSocket1 = localhost:2650 <-> ipAddress:8000

请注意,虽然它们的远程地址相同,但本地地址具有不同的端口,这就是允许这样做的原因。因此,本地远程计算机可以使用已建立的端口可靠地来回发送数据。

对于UDP,情况并非如此(我假设您正在使用DatagramSocket)。由于UDP是异步的(与TCP之类的同步相反),为了接收数据,您不会创建与另一台特定机器的绑定,例如,如果您要尝试

DatagramSocket udp1 = new DatagramSocket(8000); // = localhost:8000 <-> ?
DatagramSocket udp2 = new DatagramSocket(8000); // = localhost:8000 <-> ?

udp套接字不知道数据的来源,因此不能有像TCP那样的唯一映射,而且与TCP不同,您指定的端口是您机器的端口,而不是远程机器端口

创建UDP套接字时想到它的另一种方法就像创建TCP服务器套接字一样。当您创建TCP服务器套接字时,它正在等待来自某台计算机的连接但该计算机未知,并且当您创建TCP服务器套接字时,您指定的端口是本地端口:

ServerSocket ss1 = new ServerSocket(8000); // = localhost:8000 <-> ?
ServerSocket ss2 = new ServerSocket(8000); // = localhost:8000 <-> ?

再次像UDP一样,这将创建一个绑定异常,因为该端口用于本地计算机,并且映射不再是唯一的。但是,当您接受服务器套接字上的连接时,远程计算机会发挥作用以使套接字唯一,就像您将Socket创建到远程计算机时一样:

Socket s1 = ss1.accept();// localhost:8000 <-> remoteIp1:12345
Socket s2 = ss1.accept();// localhost:8000 <-> remoteIp2:54321

请注意,虽然本地地址相同,但套接字的远程地址不同,因此总映射(localip:port&lt; - &gt; remoteip:port)现在是唯一的。

因此,在某种程度上,您可以将UDP套接字视为类似TCP服务器套接字,这就是您必须将其绑定到唯一端口的原因。

答案 1 :(得分:1)

您确定TCP应用程序是否真的使用非零端口号进行绑定?它也不适用于TCP。端口号的目的是识别将流量路由到哪个正在运行的应用程序实例;如果两个应用程序分别将两个套接字绑定到同一个端口号,则该路由变得不可能,这就是为什么它是一个错误。

答案 2 :(得分:0)

您必须将套接字设置为可共享的,或称为SO_REUSEPORT。

在C或C ++中,这是通过setsockopt函数完成的。当您绑定到端口,然后设置SO_REUSEPORT时,另一个进程也可以将套接字绑定到相同的端口号。一个例子是多进程应用程序,它使用多个CPU内核来尽可能快地读取传入的数据。完成此操作后,流量将在各个进程之间实现负载平衡,或者更有可能的情况是,第一个读取数据的人(使用UDP)获取了数据。

对于TCP,这仅与新连接有关。当新连接进入时,第一个accept()连接的进程拥有该连接,但是绑定到该端口的所有进程仍共享端口号随后将其和新的连接移交给第一个使用accept()进行连接的人。共享来自TCP套接字的READ毫无意义(尽管多线程应用程序可以做到这一点),因为数据是以没有记录定界符的流的形式进入的,因此,让多个读者读取它只是一个类比,多人阅读同一本书,并在阅读时撕下页面。您将被每个阅读器混乱得一团糟。