Programmation asynchrone avec les sockets
L'approche asynchrone permet d'exécuter des opérations réseau sans bloquer le thread principal. Les méthodes retournent immédiatement et notifietn l'achèvement via des rappels, optimisant l'utilisation des ressources.
Serveur TCP asynchrone
Processus principal :
- Instancier le socket
- Associer l'adresse IP et le port via Bind()
- Activer l'écoute avec Listen()
- Accepter les connexions via BeginAccept/EndAccept
- Échanger des données avec BeginSend/EndSend et BeginReceive/EndReceive
- Fermer la connexion
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ServeurAsynchrone
{
public class ServeurTcpAsync
{
public void Demarrer()
{
var endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
using var socketEcoute = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
socketEcoute.Bind(endpoint);
socketEcoute.Listen(10);
AccepterConnexion(socketEcoute);
}
private void AccepterConnexion(Socket socketEcoute)
{
socketEcoute.BeginAccept(resultat =>
{
var socketClient = socketEcoute.EndAccept(resultat);
Console.WriteLine($"Connexion cliente: {socketClient.RemoteEndPoint}");
EnvoyerMessage(socketClient, "Serveur: Connexion établie");
RecevoirMessage(socketClient);
}, null);
}
private void RecevoirMessage(Socket socketClient)
{
var buffer = new byte[1024];
try
{
socketClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None,
resultat =>
{
int octetsLus = socketClient.EndReceive(resultat);
Console.WriteLine($"Message reçu: {Encoding.UTF8.GetString(buffer)}");
}, null);
}
catch (Exception ex)
{
Console.WriteLine($"Erreur: {ex.Message}");
}
}
private void EnvoyerMessage(Socket socketClient, string message)
{
if (socketClient == null || string.IsNullOrEmpty(message)) return;
byte[] donnees = Encoding.UTF8.GetBytes(message);
socketClient.BeginSend(donnees, 0, donnees.Length, SocketFlags.None,
resultat =>
{
int octetsEnvoyes = socketClient.EndSend(resultat);
Console.WriteLine($"Message envoyé: {message}");
}, null);
}
}
}
Client TCP asynchrone
Processus principle :
- Initialiser le socket client
- Établir la connexion via BeginConnect/EndConnect
- Utiliser BeginSend/EndSend et BeginReceive/EndReceive
- Terminer la session
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ClientAsynchrone
{
public class ClientTcpAsync
{
public void Connecter()
{
var endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
var socketClient = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
socketClient.BeginConnect(endpoint, resultat =>
{
socketClient.EndConnect(resultat);
EnvoyerMessage(socketClient, "Client: Bonjour serveur");
RecevoirMessage(socketClient);
}, null);
}
private void RecevoirMessage(Socket socketClient)
{
var buffer = new byte[1024];
socketClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None,
resultat =>
{
int octetsLus = socketClient.EndReceive(resultat);
Console.WriteLine($"Message serveur: {Encoding.UTF8.GetString(buffer)}");
RecevoirMessage(socketClient);
}, null);
}
private void EnvoyerMessage(Socket socketClient, string message)
{
byte[] donnees = Encoding.UTF8.GetBytes(message);
socketClient.BeginSend(donnees, 0, donnees.Length, SocketFlags.None,
resultat => socketClient.EndSend(resultat), null);
}
}
}
Modèles de rappels asynchrones
Deux approches pour gérer les rappels :
// Méthode dédiée
socket.BeginConnect(endpoint, ConnexionCompletee, socket);
// Expression lambda
socket.BeginConnect(endpoint, resultat =>
{
// Traitement
}, null);
L'approche lambda smiplifie le partage de contexte sans paramètres supplémentaires.