Utiliser des sockets pour envoyer et recevoir des données sur TCP

Avant de pouvoir utiliser un socket pour communiquer avec des appareils distants, le socket doit être initialisé avec des informations de protocole et d’adresse réseau. Le constructeur de la classe Socket a des paramètres qui spécifient la famille d’adresses, le type de socket et le type de protocole que le socket utilise pour établir des connexions. Lors de la connexion d’un socket client à un socket serveur, le client utilise un objet IPEndPoint pour spécifier l’adresse réseau du serveur.

Créer un point de terminaison IP

Quand vous utilisez System.Net.Sockets, vous représentez un point de terminaison réseau sous forme d’objet IPEndPoint. L’IPEndPoint est construit avec une IPAddress et son numéro de port correspondant. Avant de démarrer une conversation via un Socket, vous créez un canal de données entre votre application et la destination distante.

TCP/IP utilise une adresse réseau et un numéro de port de service pour identifier un service de manière unique. L’adresse réseau identifie un appareil réseau spécifique. Le numéro de port identifie le service spécifique sur cet appareil auquel se connecter. La combinaison de l’adresse réseau et du port de service est appelée point de terminaison, qui est représenté dans .NET par la classe EndPoint. Un descendant du point de terminaison (EndPoint) est défini pour chaque famille d’adresses prise en charge. Pour la famille d’adresses IP, il s’agit de la classe IPEndPoint.

La classe Dns fournit des services de nom de domaine aux applications qui utilisent des services Internet TCP/IP. La méthode GetHostEntryAsync interroge un serveur DNS pour mapper un nom de domaine convivial (par exemple, « host.contoso.com ») à une adresse Internet numérique (par exemple, 192.168.1.1). GetHostEntryAsync retourne un Task<IPHostEntry> qui contient, quand il est attendu, une liste des adresses et alias pour le nom demandé. Dans la plupart des cas, vous pouvez utiliser la première adresse retournée dans le tableau AddressList. Le code suivant obtient une IPAddress contenant l’adresse IP du serveur host.contoso.com.

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

Conseil

Pour les tests et le débogage manuels, vous pouvez généralement utiliser la méthode GetHostEntryAsync avec le nom d’hôte résultant de la valeur Dns.GetHostName() pour résoudre le nom d’hôte local en adresse IP. Prenez l'exemple de l'extrait de code suivant :

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

L’IANA (Internet Assigned Numbers Authority) définit les numéros de port des services courants. Pour plus d’informations, consultez IANA: Service Name and Transport Protocol Port Number Registry). Les autres services peuvent avoir un numéro de port compris dans la plage 1 024 à 65 535. Le code suivant combine l’adresse IP de host.contoso.com avec un numéro de port afin de créer un point de terminaison distant pour une connexion.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

Après avoir déterminé l’adresse de l’appareil distant et choisi le port à utiliser pour la connexion, l’application peut établir une connexion avec l’appareil distant.

Créer un client Socket

Une fois l’objet endPoint créé, créez un socket client pour vous connecter au serveur. Une fois le socket connecté, il peut envoyer et recevoir des données à partir de la connexion au socket serveur.

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);

Le code C# précédent :

  • Instancie un nouvel objet Socket avec une famille d’adresses d’instances endPoint donnée, SocketType.Stream et ProtocolType.Tcp.

  • Appelle la méthode Socket.ConnectAsync avec l’instance endPoint comme argument.

  • Dans une boucle while :

    • Encode et envoie un message au serveur à l’aide de Socket.SendAsync.
    • Écrit le message envoyé dans la console.
    • Initialise une mémoire tampon pour recevoir des données du serveur à l’aide de Socket.ReceiveAsync.
    • Quand la response est un accusé de réception, elle est écrite dans la console et la boucle est fermée.
  • Enfin, le socket client appelle Socket.Shutdown selon SocketShutdown.Both, qui arrête les opérations d’envoi et de réception.

Créer un serveur Socket

Pour créer le socket serveur, l’objet endPoint peut écouter les connexions entrantes sur n’importe quelle adresse IP, mais le numéro de port doit être spécifié. Une fois le socket créé, le serveur peut accepter les connexions entrantes et communiquer avec les clients.

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|>"
}

Le code C# précédent :

  • Instancie un nouvel objet Socket avec une famille d’adresses d’instances endPoint donnée, SocketType.Stream et ProtocolType.Tcp.

  • listener appelle la méthode Socket.Bind avec l’instance endPoint comme argument pour associer le socket à l’adresse réseau.

  • La méthode Socket.Listen() est appelée pour écouter les connexions entrantes.

  • listener appelle la méthode Socket.AcceptAsync pour accepter une connexion entrante sur le socket handler.

  • Dans une boucle while :

    • Appelle Socket.ReceiveAsync pour recevoir des données du client.
    • Lorsque les données sont reçues, elles sont décodées et écrites dans la console.
    • Si le message response se termine par <|EOM|>, un accusé de réception est envoyé au client à l’aide de Socket.SendAsync.

Exécutez l’exemple de client et de serveur

Démarrez d’abord l’application serveur, puis l’application 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...

L’application cliente envoie un message au serveur et le serveur répond avec un accusé de réception.

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...

Voir aussi