Как открыть два клиентских сокета udp с одним и тем же локальным портом

Как открыть два клиентских сокета с одним и тем же локальным портом, например 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();
}

}

person bala    schedule 30.07.2015    source источник
comment
И C, и C++ понятия не имеют, что такое сокет. Какая операционная система и компилятор?   -  person RamblingMad    schedule 30.07.2015
comment
В UDP вы используете один сокет на порт. Сокет UDP может обрабатывать любое количество соединений.   -  person ElderBug    schedule 30.07.2015
comment
Открытие нескольких сокетов на одном и том же локальном порту не имеет смысла ни в каком контексте.   -  person RamblingMad    schedule 30.07.2015
comment
@CoffeeandCode Мы можем установить исходный порт как локальный порт какого-либо другого сокета в java.   -  person bala    schedule 30.07.2015
comment
Закрытие смешное. Вопрос совершенно ясен.   -  person user207421    schedule 30.07.2015
comment
может кто-нибудь снять удержание? В противном случае я не могу предложения и ответить.   -  person bala    schedule 31.07.2015


Ответы (2)


Вопреки всем комментариям, все, что вам нужно сделать, это установить параметр сокета SO_REUSEPORT перед параметром bind().

person user207421    schedule 30.07.2015
comment
Я сделал это. но где я могу установить локальный порт текущего сокета. - person bala; 30.07.2015
comment
Эээ, в звонке bind()? - person user207421; 30.07.2015
comment
Если я использую окна вызова привязки, выдающие предупреждение. - person bala; 30.07.2015
comment
@bala Это точно ничего не говорит. Что оповещение? Подобные расплывчатые заявления неприемлемы. В любом случае, если вы не используете вызов bind(), вы не можете использовать какой-либо конкретный порт, не говоря уже о его повторном использовании. - person user207421; 30.07.2015
comment
Основная проблема с открытием двух или более сокетов UDP на одном и том же порту заключается в том, что когда приходит пакет, неизвестно, какой сокет получит пакет. Это может быть только один (любой), а может быть и все. Это предполагает одноадресную передачу. С другой стороны, если сокеты прослушивают многоадресный трафик, входящие многоадресные пакеты будут доставлены в любой сокет, который присоединился к группе многоадресной рассылки для этого пакета. - person dbush; 30.07.2015
comment
@Ejp Когда я использую bind () вызов, брандмауэр Windows выдает какое-то предупреждение, как это обойти? - person bala; 30.07.2015
comment
@dbush Ваш комментарий должен быть адресован ОП. - person user207421; 30.07.2015
comment
@bala Либо попросите пользователя принять это, либо настройте правило в брандмауэре. - person user207421; 30.07.2015
comment
@bala Пожалуйста, опубликуйте свой код вместе с точным текстом любого сообщения об ошибке, которое вы получаете при его запуске, чтобы мы могли лучше вам помочь. - person dbush; 30.07.2015
comment
@bala Это означает тебя, оригинальный постер. - person user207421; 31.07.2015
comment
@bala А в чем твоя проблема сейчас? - person user207421; 31.07.2015
comment
Мне нужно открыть второй сокет UDP с локальным портом первого сокета. - person bala; 31.07.2015
comment
Это работает нормально. Когда я пытаюсь привязать новый порт, он выдает предупреждение, теперь я пытаюсь привязать уже существующий порт. - person bala; 07.08.2015
comment
@bala Вы должны установить эту опцию на обоих сокетах. Но я не понимаю, почему вы закрываете сокет, а затем создаете новый на том же порту в своем Java-коде. Это бессмысленно. Если вы пытаетесь отразить это поведение в своем коде C, не делайте этого. Просто оставьте розетку открытой. - person user207421; 07.08.2015

Кажется, что код Java на самом деле не открывает два сокета на одном и том же порту, а закрывает первый сокет, а затем открывает новый на том же порту, что и тот, который был закрыт. Для сокета UDP, не находящегося в подключенном состоянии, в этом вообще нет необходимости.

Если вы не меняете порт, ваш код C++ должен иметь возможность продолжать использовать тот же сокет, а не создавать новый. Предполагая, что вы хотите отразить то, что делает код Java, вам не нужно вызывать connect. Просто используйте sendto и recvfrom для отправки данных туда и обратно.

person dbush    schedule 31.07.2015
comment
Это даже не нужно в подключенном состоянии. Просто подключите его к null: это разрыв соединения. - person user207421; 07.08.2015