如何使用相同的本地端口打开两个 udp 客户端套接字

Posted

技术标签:

【中文标题】如何使用相同的本地端口打开两个 udp 客户端套接字【英文标题】:How to open the two udp client socket with same local port 【发布时间】:2015-10-21 11:17:39 【问题描述】:

如何打开两个具有相同本地端口的客户端套接字,如 java。 在 Java 中,我们可以在创建 DatagramPacket 时提及源端口。 我正在尝试进行 UDP 打孔。如果我做错了什么,请纠正我。

我在这里添加了我的代码。

void UDPClientConnect ( string IP , string Port ) 
 
    WSADATA wsa;


if ( WSAStartup(MAKEWORD(2,2),&wsa) != 0 )

    printf ( "startup failed %d\n" , WSAGetLastError() ) ;
    return ;

int reuse = 1 ; 
SOCKET s = socket ( AF_INET , SOCK_DGRAM , IPPROTO_UDP ) ;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR,(char*) &reuse, sizeof(int)); 

struct sockaddr_in si_other;
int slen=sizeof(si_other);
char buf[BUFLEN];
char message[BUFLEN];

int p = atoi ( Port.c_str() ) ;

memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;

si_other.sin_port = htons(p);
si_other.sin_addr.S_un.S_addr = inet_addr( IP.c_str() );

strcpy_s ( message , "Hello" ) ;


//send the message
if ( sendto ( s , message , strlen(message) , 0 , (struct sockaddr *) &si_other , slen ) == SOCKET_ERROR)

    printf ( "sendto() failed with error code : %d" , WSAGetLastError() ) ;
   return ;


//receive a reply and print it
memset ( buf , '\0' , BUFLEN ) ;
//try to receive some data, this is a blocking call
if ( recvfrom ( s , buf , BUFLEN , 0 , (struct sockaddr *) &si_other , &slen ) == SOCKET_ERROR )

    printf("recvfrom() failed with error code : %d" , WSAGetLastError());
    return ;


struct sockaddr_in localAddress;
int addrlen = sizeof(localAddress) ;

getsockname ( s , (struct sockaddr*)&localAddress , &addrlen ) ;

USHORT LocalPort = localAddress.sin_port ;
IN_ADDR localAddr = localAddress.sin_addr ;

printf("local address: %s\n", inet_ntoa( localAddress.sin_addr));
printf("local port: %d\n", (int) ntohs(localAddress.sin_port));

printf ( "\n******************************************\n\n" ) ;

int pos = 1 ;
CStringA CBuff = CStringA ( buf ) ;

string RemortPort = CBuff.Tokenize ( "-" , pos ) ;
string RemortIp = CBuff.Tokenize ( "-" , pos ) ;


reuse = 1 ;

SOCKET Sock_Reuse = socket ( AF_INET , SOCK_DGRAM , IPPROTO_UDP ) ;

setsockopt( Sock_Reuse , SOL_SOCKET, SO_REUSEADDR,(char*) &reuse, sizeof(int)); // optional, but recommended

struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(LocalPort);
sin.sin_addr.s_addr=INADDR_ANY;

if ( SOCKET_ERROR == connect(Sock_Reuse,(struct sockaddr *)&sin,sizeof(struct sockaddr_in)))

    printf ( "bind failed %d" , WSAGetLastError() ) ;


closesocket ( Sock_Reuse ) ;
closesocket ( s ) ;
WSACleanup ( ) ;

我尝试用相同的方法打开具有相同端口的新套接字。

检查这个 Java 代码我想在 C/C++ 中这样做。不可能吗??

import java.io.* ;
import java.net.* ;

public class UDPHolePunchingClient 

public static void main(String[] args) throws Exception 
    // prepare Socket
    DatagramSocket clientSocket = new DatagramSocket();

    // prepare Data
    byte[] sendData = "Hello".getBytes();

    // send Data to Server with fix IP (X.X.X.X)
    // Client1 uses port 7070, Client2 uses port 7071
    DatagramPacket sendPacket = new DatagramPacket(sendData,
            sendData.length, InetAddress.getByName("172.24.8.110"), 7070);
    clientSocket.send(sendPacket);

    // receive Data ==> Format:"<IP of other Client>-<Port of other Client>"
    DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
    clientSocket.receive(receivePacket);

    // Convert Response to IP and Port
    String response = new String(receivePacket.getData());
    String[] splitResponse = response.split("-");
    InetAddress ip = InetAddress.getByName(splitResponse[0].substring(1));

    int port = Integer.parseInt(splitResponse[1]);

    // output converted Data for check
    System.out.println("IP: " + ip + " PORT: " + port);

    // close socket and open new socket with SAME localport
    int localPort = clientSocket.getLocalPort();
    clientSocket.close();
    clientSocket = new DatagramSocket(localPort);

    // set Timeout for receiving Data
    clientSocket.setSoTimeout(1000);

    // send 5000 Messages for testing
    for (int i = 0; i < 5000; i++) 

        // send Message to other client
        sendData = ("Datapacket(" + i + ")").getBytes();
        sendPacket = new DatagramPacket(sendData, sendData.length, ip, port);
        clientSocket.send(sendPacket);

        // receive Message from other client
        try 
            receivePacket.setData(new byte[1024]);
            clientSocket.receive(receivePacket);
            System.out.println("REC: "
                    + new String(receivePacket.getData()));

         catch (Exception e) 
            System.out.println("SERVER TIMED OUT");
        
    

    // close connection
    clientSocket.close();



【问题讨论】:

C 和 C++ 都不知道“套接字”是什么。哪个操作系统和编译器? 在 UDP 中,每个端口使用一个套接字。一个 UDP 套接字可以处理任意数量的连接。 在同一个本地端口上打开多个套接字在任何情况下都是没有意义的。 @CoffeeandCode 我们可以将源端口设置为java中其他套接字的本地端口端口。 关闭是荒谬的。这个问题很清楚。 【参考方案1】:

与所有评论相反,您所要做的就是在bind() 之前设置套接字选项SO_REUSEPORT

【讨论】:

我已经做到了。但是我可以在哪里设置当前套接字的本地端口。 错误,在bind() 通话中? 如果我使用绑定调用窗口抛出一些警报。 @bala 这完全没有说明问题。 什么警报?诸如此类的含糊陈述是不可接受的。在任何情况下,如果你使用bind()调用,你就不能使用任何特定端口,更不用说重新使用它了。 在同一个端口上打开两个或多个 UDP 套接字的主要问题是当一个数据包进来时,不确定哪个套接字将接收数据包。它可能只是一个(任何一个),也可能是全部。这是假设单播。另一方面,如果套接字正在侦听多播流量,则传入的多播数据包将被传递到已加入该数据包的多播组的任何套接字。【参考方案2】:

看起来Java代码实际上并没有在同一个端口上打开两个套接字,而是关闭第一个套接字然后在与关闭的端口相同的端口上打开一个新的套接字。对于未处于连接状态的 UDP 套接字,根本不需要这样做。

如果您不更改端口,您的 C++ 代码应该能够继续使用同一个套接字,而不是创建一个新的。假设您想反映 Java 代码正在执行的操作,则不需要调用 connect。只需使用sendtorecvfrom 来回发送数据。

【讨论】:

连接状态下甚至不需要。只需将其连接到null:这就是断开连接。

以上是关于如何使用相同的本地端口打开两个 udp 客户端套接字的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 unix Sockets API 查找本地可用的 UDP 端口

如何在同一本地/src 地址上创建多个 UDP 数据报通道/流

如何打开一个UDP端口? [关闭]

Linux学习_UDP编程

如何在c中设置UDP套接字中的源端口?

关于socket通信UDP协议的问题,在客户端client下创建两个套接字s、s1分别用于recvfrom()和sendto()。