Crear un servidor UDP en C#

Saad Aslam 12 octubre 2023
Crear un servidor UDP en C#

Este artículo le mostrará cómo puede crear un servidor UDP simple en C#.

Crear un Servidor UDP en C#

Para una breve introducción, el protocolo UDP no necesita construir una conexión con el cliente. Los datos simplemente se transmiten sin autenticar si el cliente los recibió o no.

Este tipo de protocolo se suele utilizar para transmitir los datos. Debido a que UDP no tiene conexión, un oyente no necesita estar en línea para recibir el mensaje.

Para transmitir un datagrama con UDP, deberá conocer la dirección de red del dispositivo de red que aloja el servicio y el número de puerto UDP del servicio. Los números de puerto para servicios populares están definidos por la Autoridad de Números Asignados de Internet (IANA); el rango de números de puerto es de 1024 a 65 535 y se puede asignar a servicios que no están en la lista de IANA.

En las redes basadas en IP, se utilizan direcciones de red especiales para gestionar los mensajes de difusión UDP. La siguiente explicación utiliza la familia de direcciones IP versión 4 de Internet como ejemplo.

Al configurar todos los bits de la identificación del host, las transmisiones pueden dirigirse a partes específicas de una red. Utilice la dirección 192.168.1.255 para transmitir a todos los hosts de la red con direcciones IP que comiencen con 192.168.1.

Ahora estamos listos para construir o crear un socket, configurar nuestro protocolo UDP e iniciar la comunicación de inmediato.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

Estas serían las bibliotecas utilizadas para configurar el servidor. Estas bibliotecas admiten todas las funciones básicas relacionadas con las redes, como la creación de sockets, el manejo de las direcciones IPV4 y muchas más.

public class SimpleUdpSrvr {
  public static void Main() {
    int recv;
    byte[] data = new byte[1024];
    IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
    Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  }
}

La línea int recv crea una variable recv de tipo entero que contiene el número de bytes del mensaje de cadena que recibió el cliente.

El byte[] data = new byte[1024]; La línea se usa para crear una matriz llamada data de tamaño de byte para cada celda con 1024 celdas, también llamada tamaño de matriz. Esta matriz se crea dinámicamente en un montón.

El IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050); line le dice que cree una variable ipep para el socket. La información que pasa le dice qué tipo de socket debe usar.

La clase IPEndPoint representa un punto final de red con una dirección IP y un número de puerto; el cliente necesita esta información de configuración para conectarse con nuestro servicio. El argumento IPAddress.Any que pasa indica que el servidor puede conectarse con cualquier dirección IP al número de puerto 9050; la variable ipep también se crea dinámicamente.

Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

Esta línea crea una variable newsock, un socket para nosotros a través del cual nos comunicaremos. Los argumentos que pasan indican qué tipo de socket queremos crear.

El AddressFamily.InterNetwork nos dice que queremos las direcciones IP locales. El argumento SocketType.Dgram establece que los datos deben fluir en datagramas en lugar de paquetes.

Y por último, el argumento ProtocolType.Udp indica el tipo de protocolo del socket que usaremos. Ahora, nuestro socket está creado.

newsock.Bind(ipep);
Console.WriteLine("Waiting for a client...");

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);

Hasta ahora, habíamos creado nuestro único punto final en la red y un socket a través del cual nos comunicaremos, newsock.Bind(ipep); line ahora nos ayudará a vincular nuestro punto final al socket.

ipep es la variable que tiene la información sobre nuestro punto final y se pasa en la función bind() de nuestro objeto newsock.

Console.WriteLine("Waiting for a client...");

Esta línea imprime en pantalla que el servidor está esperando que el cliente envíe algún mensaje. Necesitamos crear otro punto final para el remitente que intentará comunicarse con el servidor.

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);

Esta línea crea otro punto final, y la variable se llama remitente. El argumento pasado en su IPAddress.Any dice que estamos esperando cualquier dirección IP, y 0 significa que es un comodín para el número de puerto, y el sistema debería ver y encontrar cualquier puerto adecuado que nos lo asigne.

La línea EndPoint Remote = (EndPoint)(sender); se utiliza para encontrar la dirección IP del remitente y almacenarla en la variable remota.

recv = newsock.ReceiveFrom(data, ref Remote);

Console.WriteLine("Message received from {0}:", Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));

La variable recv que creamos al comienzo de nuestro programa ahora se usa en recv = newsock.ReceiveFrom(data, ref Remote); línea. Usamos la función Receive() del socket que creamos anteriormente, newsock, y actualmente recibimos todos los datos que tenemos en el socket en ese momento.

Los argumentos que pasan la función Receive() indican dónde almacenar los datos y quién debe almacenar o esperar los datos. El argumento data es la matriz de tamaño de bytes que se pasa, y los datos se escribirán como matriz de datos.

El argumento ref Remote dice que esa dirección IP envió los datos, y la función devolverá la cantidad de bytes obtenidos de ese socket y los almacenará en la variable recv.

Console.WriteLine("Message received from {0}:", Remote.ToString());

Esta línea escribe en pantalla la dirección IP del cliente que envió los datos a través del socket. El argumento pasado Remote.ToString() convierte los números decimales en caracteres ASCII, y la variable remote era el segundo punto final que se refería al cliente.

Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));

Esta línea escribe los datos reales en la pantalla. Los datos enviados a través del socket están en formato sin procesar y deben convertirse en caracteres ASCII para leer.

La línea Encoding.ASCII.GetString(data, 0, recv) toma los datos de la variable data que se pasa como argumento, y el argumento recv (contiene el número de bytes que envió el cliente) dice que para escribir estos muchos bytes de la variable data en la pantalla.

string welcome = "Welcome to my test server";
data = Encoding.ASCII.GetBytes(welcome);
newsock.SendTo(data, data.Length, SocketFlags.None, Remote);

La línea string welcome = "Welcome to my test server"; inicializa la variable de cadena welcome con el texto Welcome to my test server.

La línea data = Encoding.ASCII.GetBytes(welcome); toma la variable welcome como argumento en la función Encoding.ASCII.GetBytes() y la convierte en forma cruda, para que esté lista para enviar a través del socket en el otro extremo.

newsock.SendTo(data, data.Length, SocketFlags.None, Remote);

Esta línea envía los datos a través de nuestro socket newsock creado previamente y utiliza la función Send(). Los argumentos que toma son: la variable data que contiene la forma sin procesar de los datos que se enviarán, el argumento data.Length que indica cuántos bytes escribir en el socket, ya que la longitud de los datos es equivalente a la cantidad de bytes , el argumento SocketFlags.None nos dice que mantengamos todas las banderas en cero, las banderas son indicadores y cada bandera tiene su significado.

El argumento de la variable Remoto nos informa sobre el cliente al que queremos enviar los datos, ya que la variable Remoto almacena la dirección IP y el número de puerto del cliente.

while (true) {
  data = new byte[1024];
  recv = newsock.ReceiveFrom(data, ref Remote);

  Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
  newsock.SendTo(data, recv, SocketFlags.None, Remote);
}

La línea while(true) dice que es un ciclo infinito y que todas sus declaraciones se ejecutarán infinitamente. La línea data = new byte[1024]; crea un nuevo array de tamaño byte de longitud 1024 cada vez que se ejecuta.

El recv = newsock.ReceiveFrom(data, ref Remote); line recibe los datos del cliente y los guarda en la matriz de datos.

La Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); escribe los datos en la pantalla.

El newsock.SendTo(data, recv, SocketFlags.None, Remote); línea devuelve los mismos datos al cliente que el servidor ha recibido de él.

Después de eso, el servidor se detendrá nuevamente y esperará al cliente, y cualquier cosa que el cliente envíe al servidor responderá con el mismo mensaje. Este servidor nunca terminará a menos que falle o el usuario lo detenga manualmente.

Ahora, como hemos aprendido cómo crear un servidor UDP en C# y sus funcionalidades básicas, podemos agregar más funcionalidades y hacer un servidor de acuerdo con nuestros requisitos.

Código fuente completo:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class SimpleUdpSrvr {
  public static void Main() {
    int recv;
    byte[] data = new byte[1024];
    IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);

    Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

    newsock.Bind(ipep);
    Console.WriteLine("Waiting for a client...");

    IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
    EndPoint Remote = (EndPoint)(sender);

    recv = newsock.ReceiveFrom(data, ref Remote);

    Console.WriteLine("Message received from {0}:", Remote.ToString());
    Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));

    string welcome = "Welcome to my test server";
    data = Encoding.ASCII.GetBytes(welcome);
    newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
    while (true) {
      data = new byte[1024];
      recv = newsock.ReceiveFrom(data, ref Remote);

      Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
      newsock.SendTo(data, recv, SocketFlags.None, Remote);
    }
  }
}
Autor: Saad Aslam
Saad Aslam avatar Saad Aslam avatar

I'm a Flutter application developer with 1 year of professional experience in the field. I've created applications for both, android and iOS using AWS and Firebase, as the backend. I've written articles relating to the theoretical and problem-solving aspects of C, C++, and C#. I'm currently enrolled in an undergraduate program for Information Technology.

LinkedIn

Artículo relacionado - Csharp Server