Compartir a través de


Uso de sockets para enviar y recibir datos mediante TCP

Para poder usar un socket para comunicarse con dispositivos remotos, el socket debe inicializarse con información de protocolo y dirección de red. El constructor de la Socket clase tiene parámetros que especifican la familia de direcciones, el tipo de socket y el tipo de protocolo que usa el socket para realizar conexiones. Al conectar un socket de cliente a un socket de servidor, el cliente usará un IPEndPoint objeto para especificar la dirección de red del servidor.

Creación de un punto de conexión IP

Al trabajar con System.Net.Sockets, representa un punto de conexión de red como un IPEndPoint objeto . IPEndPoint se crea con un objeto IPAddress y con su correspondiente número de puerto. Para poder iniciar una conversación a través de Socket, primero crea una tubería de datos entre tu aplicación y el destino remoto.

TCP/IP usa una dirección de red y un número de puerto de servicio para identificar de forma única un servicio. La dirección de red identifica un destino de red específico; el número de puerto identifica el servicio específico en ese dispositivo al que conectarse. La combinación de dirección de red y puerto de servicio se denomina punto de conexión, que se representa en .NET por la EndPoint clase . Se define un descendiente de EndPoint para cada familia de direcciones admitidas; para la familia de direcciones IP, la clase es IPEndPoint.

La Dns clase proporciona servicios de nombre de dominio a las aplicaciones que usan servicios de Internet TCP/IP. El GetHostEntryAsync método consulta un servidor DNS para asignar un nombre de dominio descriptivo (como "host.contoso.com") a una dirección numérica de Internet (como 192.168.1.1). GetHostEntryAsync devuelve un objeto Task<IPHostEntry> que, cuando se le espera, contiene una lista de direcciones y alias del nombre solicitado. En la mayoría de los casos, puede usar la primera dirección devuelta en la matriz AddressList. El código siguiente obtiene un IPAddress que contiene la dirección IP del servidor host.contoso.com.

IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];

Sugerencia

Con fines de prueba y depuración manuales, normalmente puede usar el método GetHostEntryAsync con el nombre de host resultante del valor de Dns.GetHostName() para resolver el nombre de localhost en una dirección IP. Tenga en cuenta el fragmento de código siguiente:

var hostName = Dns.GetHostName();
IPHostEntry localhost = await Dns.GetHostEntryAsync(hostName);
// This is the IP address of the local machine
IPAddress localIpAddress = localhost.AddressList[0];

La Autoridad de Números Asignados en Internet (IANA) define números de puerto para servicios comunes. Para obtener más información, consulte IANA: Nombre del servicio y Registro de número de puerto del protocolo de transporte). Otros servicios pueden tener números de puerto registrados en el intervalo de 1024 a 65 535. El código siguiente combina la dirección IP para host.contoso.com con un número de puerto para crear un punto de conexión remoto para una conexión.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

Después de determinar la dirección del dispositivo remoto y elegir un puerto que se va a usar para la conexión, la aplicación puede establecer una conexión con el dispositivo remoto.

Creación de un Socket cliente

Con el endPoint objeto creado, cree un socket de cliente para conectarse al servidor. Una vez conectado el socket, puede enviar y recibir datos de la conexión de socket del servidor.

using Socket client = new(
    ipEndPoint.AddressFamily, 
    SocketType.Stream, 
    ProtocolType.Tcp);

await client.ConnectAsync(ipEndPoint);
while (true)
{
    // Send message.
    var message = "Hi friends 👋!<|EOM|>";
    var messageBytes = Encoding.UTF8.GetBytes(message);
    _ = await client.SendAsync(messageBytes, SocketFlags.None);
    Console.WriteLine($"Socket client sent message: \"{message}\"");

    // Receive ack.
    var buffer = new byte[1_024];
    var received = await client.ReceiveAsync(buffer, SocketFlags.None);
    var response = Encoding.UTF8.GetString(buffer, 0, received);
    if (response == "<|ACK|>")
    {
        Console.WriteLine(
            $"Socket client received acknowledgment: \"{response}\"");
        break;
    }
    // Sample output:
    //     Socket client sent message: "Hi friends 👋!<|EOM|>"
    //     Socket client received acknowledgment: "<|ACK|>"
}

client.Shutdown(SocketShutdown.Both);

El código de C# anterior:

  • Crea una instancia de un nuevo objeto Socket con una determinada familia de direcciones de instancias de endPoint, SocketType.Stream y ProtocolType.Tcp.

  • Llama al Socket.ConnectAsync método con la endPoint instancia como argumento.

  • En un bucle while:

    • Codifica y envía un mensaje al servidor mediante Socket.SendAsync.
    • Escribe el mensaje enviado en la consola.
    • Inicializa un búfer para recibir datos del servidor mediante Socket.ReceiveAsync.
    • Cuando response es una confirmación, se escribe en la consola y se sale del bucle.
  • Por último, el socket client llama a Socket.Shutdown según SocketShutdown.Both, lo que finaliza las operaciones tanto de envío como de recepción.

Creación de un Socket servidor

Para crear el socket de servidor, el endPoint objeto puede escuchar las conexiones entrantes en cualquier dirección IP, pero se debe especificar el número de puerto. Una vez creado el socket, el servidor puede aceptar conexiones entrantes y comunicarse con los clientes.

using Socket listener = new(
    ipEndPoint.AddressFamily,
    SocketType.Stream,
    ProtocolType.Tcp);

listener.Bind(ipEndPoint);
listener.Listen(100);

var handler = await listener.AcceptAsync();
while (true)
{
    // Receive message.
    var buffer = new byte[1_024];
    var received = await handler.ReceiveAsync(buffer, SocketFlags.None);
    var response = Encoding.UTF8.GetString(buffer, 0, received);
    
    var eom = "<|EOM|>";
    if (response.IndexOf(eom) > -1 /* is end of message */)
    {
        Console.WriteLine(
            $"Socket server received message: \"{response.Replace(eom, "")}\"");

        var ackMessage = "<|ACK|>";
        var echoBytes = Encoding.UTF8.GetBytes(ackMessage);
        await handler.SendAsync(echoBytes, 0);
        Console.WriteLine(
            $"Socket server sent acknowledgment: \"{ackMessage}\"");

        break;
    }
    // Sample output:
    //    Socket server received message: "Hi friends 👋!"
    //    Socket server sent acknowledgment: "<|ACK|>"
}

El código de C# anterior:

  • Crea una instancia de un nuevo objeto Socket con una determinada familia de direcciones de instancias de endPoint, SocketType.Stream y ProtocolType.Tcp.

  • listener llama al Socket.Bind método con la endPoint instancia como argumento para asociar el socket a la dirección de red.

  • Se llama al método Socket.Listen() para escuchar las conexiones entrantes.

  • listener Llama al Socket.AcceptAsync método para aceptar una conexión entrante en el handler socket.

  • En un bucle while:

    • Llama a Socket.ReceiveAsync para recibir datos del cliente.
    • Cuando se reciben los datos, se descodifica y se escriben en la consola.
    • Si el response mensaje finaliza con <|EOM|>, se envía una confirmación al cliente mediante .Socket.SendAsync

Ejecución del cliente y el servidor de ejemplo

Inicie primero la aplicación de servidor y, a continuación, inicie la aplicación cliente.

dotnet run --project socket-server
Socket server starting...
Found: 172.23.64.1 available on port 9000.
Socket server received message: "Hi friends 👋!"
Socket server sent acknowledgment: "<|ACK|>"
Press ENTER to continue...

La aplicación cliente enviará un mensaje al servidor y el servidor responderá con una confirmación.

dotnet run --project socket-client
Socket client starting...
Found: 172.23.64.1 available on port 9000.
Socket client sent message: "Hi friends 👋!<|EOM|>"
Socket client received acknowledgment: "<|ACK|>"
Press ENTER to continue...

Consulte también