example - Socket C#UDP: Obtener la dirección del receptor




udp socket c++ example (4)

Acabo de tener el mismo problema. No veo una forma, utilizando ReceiveFrom o sus variantes asíncronas, para recuperar la dirección de destino de un paquete recibido.

Sin embargo ... Si usa ReceiveMessageFrom o sus variantes, obtendrá una IPPacketInformation (por referencia para ReceiveMessageFrom y EndReceiveMessageFrom , o como una propiedad de SocketAsyncEventArgs transferida a su devolución de llamada en ReceiveMessageFromAsync ). Ese objeto contendrá la dirección IP y el número de interfaz donde se recibió el paquete.

(Tenga en cuenta que este código no se ha probado, ya que usé ReceiveMessageFromAsync lugar de las llamadas de inicio / finalización falsas).

private void ReceiveCallback(IAsyncResult iar)
{
    IPPacketInformation packetInfo;
    EndPoint remoteEnd = new IPEndPoint(IPAddress.Any, 0);
    SocketFlags flags = SocketFlags.None;
    Socket sock = (Socket) iar.AsyncState;

    int received = sock.EndReceiveMessageFrom(iar, ref flags, ref remoteEnd, out packetInfo);
    Console.WriteLine(
        "{0} bytes received from {1} to {2}",
        received,
        remoteEnd,
        packetInfo.Address
    );
}

Tenga en cuenta que, aparentemente, debería llamar a SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true) como parte de la configuración del socket, antes de Bind . Los métodos ... ReceiveMessageFrom ... lo establecerán por usted, pero probablemente solo verá información válida en cualquier paquete que Windows haya visto después de que se configuró la opción. (En la práctica, esto no es un gran problema, pero cuando / si alguna vez sucedió, la razón sería un misterio. Es mejor prevenirlo por completo).

Tengo una clase de servidor UDP asíncrono con un socket vinculado a IPAddress.Any, y me gustaría saber a qué dirección IP se envió el paquete recibido (... o recibido el). Parece que no puedo usar la propiedad Socket.LocalEndPoint, ya que siempre devuelve 0.0.0.0 (lo que tiene sentido, ya que está vinculado a eso ...).

Aquí están las partes interesantes del código que estoy usando actualmente:

private Socket udpSock;
private byte[] buffer;
public void Starter(){
    //Setup the socket and message buffer
    udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    udpSock.Bind(new IPEndPoint(IPAddress.Any, 12345));
    buffer = new byte[1024];

    //Start listening for a new message.
    EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
    udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);
}

private void DoReceiveFrom(IAsyncResult iar){
    //Get the received message.
    Socket recvSock = (Socket)iar.AsyncState;
    EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
    int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP);
    byte[] localMsg = new byte[msgLen];
    Array.Copy(buffer, localMsg, msgLen);

    //Start listening for a new message.
    EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
    udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);

    //Handle the received message
    Console.WriteLine("Recieved {0} bytes from {1}:{2} to {3}:{4}",
                      msgLen,
                      ((IPEndPoint)clientEP).Address,
                      ((IPEndPoint)clientEP).Port,
                      ((IPEndPoint)recvSock.LocalEndPoint).Address,
                      ((IPEndPoint)recvSock.LocalEndPoint).Port);
    //Do other, more interesting, things with the received message.
}

Como se mencionó anteriormente, esto siempre imprime una línea como:

Recibió 32 bytes de 127.0.0.1:1678 a 0.0.0.0:12345

Y me gustaría que fuera algo como:

Recibió 32 bytes de 127.0.0.1:1678 a 127.0.0.1:12345

Gracias, de antemano, por cualquier idea sobre esto! --Adán

ACTUALIZAR

Bueno, encontré una solución, aunque no me gusta ... Básicamente, en lugar de abrir un solo socket udp vinculado a IPAddress. En cualquier caso, creo un socket único para cada dirección IP disponible. Por lo tanto, la nueva función de inicio se ve como:

public void Starter(){
    buffer = new byte[1024];

    //create a new socket and start listening on the loopback address.
    Socket lSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    lSock.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);

    EndPoint ncEP = new IPEndPoint(IPAddress.Any, 0);
    lSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref ncEP, DoReceiveFrom, lSock);

    //create a new socket and start listening on each IPAddress in the Dns host.
    foreach(IPAddress addr in Dns.GetHostEntry(Dns.GetHostName()).AddressList){
        if(addr.AddressFamily != AddressFamily.InterNetwork) continue; //Skip all but IPv4 addresses.

        Socket s = new Socket(addr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
        s.Bind(new IPEndPoint(addr, 12345));

        EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
        s.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, s);
    }
}

Esto es solo para ilustrar el concepto, el mayor problema con este código tal como está, es que cada socket está tratando de usar el mismo búfer ... lo que generalmente es una mala idea ...

Tiene que haber una mejor solución a esto; Quiero decir, ¡la fuente y el destino son parte del encabezado del paquete UDP! Oh bueno, supongo que seguiré con esto hasta que haya algo mejor.


Adán

Esto no está probado ... intentemos

private void DoReceiveFrom(IAsyncResult iar){
//Get the received message.
Socket recvSock = (Socket)iar.AsyncState;
//EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
Socket clientEP = recvSock.EndAccept(iar);
int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP);
byte[] localMsg = new byte[msgLen];
Array.Copy(buffer, localMsg, msgLen);

//Start listening for a new message.
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);

//Handle the received message
/*
Console.WriteLine("Recieved {0} bytes from {1}:{2} to {3}:{4}",
                  msgLen,
                  ((IPEndPoint)recvSock.RemoteEndPoint).Address,
                  ((IPEndPoint)recvSock.RemoteEndPoint).Port,
                  ((IPEndPoint)recvSock.LocalEndPoint).Address,
                  ((IPEndPoint)recvSock.LocalEndPoint).Port);
//Do other, more interesting, things with the received message.
*/
Console.WriteLine("Recieved {0} bytes from {1}:{2} to {3}",
                  msgLen,
                  ((IPEndPoint)recvSock.RemoteEndPoint).Address,
                  ((IPEndPoint)recvSock.RemoteEndPoint).Port,
                  clientEP.RemoteEP.ToString();
 }

Creo que si se enlaza a 127.0.0.1 en lugar de a IPAddress. Cualquiera obtendrá el comportamiento que desea.

0.0.0.0 significa deliberadamente "cada dirección IP disponible" y se toma muy literalmente como consecuencia de su declaración de enlace.


Una forma de obtener una dirección de ese socket sería conectarlo al remitente. Una vez que haga eso, podrá obtener la dirección local (o al menos, una enrutable al remitente), sin embargo, solo podrá recibir mensajes del punto extremo conectado.

Para desconectarte, deberás usar conectar de nuevo, esta vez especificando una dirección con una familia de AF_UNSPEC . Desafortunadamente, no sé cómo se lograría esto en C #.

(Descargo de responsabilidad: nunca he escrito una línea de C #, esto se aplica a Winsock en general)







asyncsocket