Introduction
Bonjour et bienvenue sur la page de mon tutoriel sur les bases des sockets en .NET. Les exemples que vous allez voir ici sont en c# mais vous pouvez appliquer les meme bases pour le VB.NET et managed c++. Ce tutoriel a pour but de couvrir la bases des classes utiliser par .NET pour nous permettre de controler les sockets, en utilisant uniquement les methodes synchrones.
Je voudrais premièrement vous inviter a lire un tutoriel super intéressant écrit par xvolks. Il traite de la base des sockets plutot coter technologie. Cela est très intéressant et instructif. Voir cet excellent post ici
Une chose que j'ai appris durant mes longues années d'apprentissage de la programmation: rien n'est plus utile qu'une bonne recherche sur internet! Si vous avez des problemes essayer toujours de trouver par vous meme en premier lieu. En plus de vous rendre plus débrouillard, cela vous aidera beaucoup a devenir un bon programmeur. Vous pouvez commencer cette recherche d'information chaque fois que vous vous poser une question sur la classe socket sur le site de microsoft. Je vous conseille fortement de mettre cette page dans vos favoris si vous voulez commencer à programmer en socket avec .NET! Socket Class (System.Net.Sockets)
Pré-requis
Pour bien profiter de ce tutoriel vous devez etre familier avec le .NET Framework. Une conaissance de c# n'est pas nécessaire mais conseiller. Vous devez également etre familier avec le traitement d'erreurs sur .NET.
La base
Pour commencer, définir l'utilisation des librairies System.Net et System.Net.Sockets:
using System.Net;
using System.Net.Sockets;
Il y a deux type d'opérations que l'on peut performer sur les sockets: Les opérations qui bloquent (Syncronous) et les opérations qui ne bloquent pas (Asyncronous). Les opérations qui bloquent nous empechent de faire quoi que ce doit jusqu'a ce que l'opération sur le socket soit fini. Par opération j'entend les trois operation de bases: Connect, Send, Receive. Les opérations bloquantes sont plus facile (selon moi) a apprendre mais peuvent etre beaucoup moin efficaces (surtout si beaucoup de connections sont nécessaires). Ce premier tutoriel couvrira uniquement les opérations syncrones.
Pour l'exemple, je vais utiliser le deuxieme constructeur pour instancier mon object Socket:
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Donc ici nous avons:
-AddressFamily.InterNetwork: Définit que nous voulons utiliser le protocol IPv4 (Internet Protocol v4).
-SocketType.Stream: Je n`enterai pas dans les details, gardez ce SocketType.
-ProtocolType.Tcp: Définit que nous voulons utiliser le protocol Tcp.
La connection
Maintenant pour les connections. Nous devons definir notre cible (le serveur sur lequel nous voulons nous connecter) avec les classes IPAddress et IPEndPoint. La classe IPAddress définit une addresse IPv4 (elle peut aussi définir IPv6 mais nous n'enterons pas dans ces details), celle IPEndPoint englobe une classe IPAddress ainsi qu'un numéro de port. Pour l'exemple ici je vais prendre l'addresse locale de la machine (127.0.0.1 soit localhost, qui est toujours l'addresse de votre propre ordinateur en local). Nous allons tenter de nous connecter au port 3405 (choisi totalement au hasard).
IPAddress address = IPAddress.Parse("127.0.0.1");
IPEndPoint endPoint = new IPEndPoint(address, 3405);
Pour établir une connection, il faut apeller la méthode Connect. Pour vérifier si la connection établie a été réussie, il faut attraper les exceptions générés par Connect. Si aucune exception n'est générés, la connection réussit. Voici un bout de code qui explique les divers problemes qui peuvent survenir lorsque l'on essaie de connecter un socket:
try
{
socket.Connect(endPoint);
}
catch (SocketException ex)
{
Console.WriteLine("La connection n'a pu etre etablie. Pour connaitre la raison il faut regarder la propriete ErrorCode de l'exception");
Console.WriteLine("Erreur socket: " + ex.ErrorCode);
//References sur les codes erreurs: http://msdn.microsoft.com/en-us/library/ms740668%28VS.85%29.aspx
}
catch (ArgumentNullException)
{
Console.WriteLine("Votre IPEndPoint n'est pas definit.");
}
catch (ObjectDisposedException)
{
Console.WriteLine("Cet erreur arrive si vous essayez de re utiliser un socket");
}
catch (InvalidOperationException)
{
Console.WriteLine("Le socket est en mode ecoute pour accepter des connections. Dans ce cas il ne peut ce connecter");
}
Console.WriteLine("Bravo! vous etes connecter!");
L'envoi de données
Bon c'est bien on est connecter! Mais il faut pouvoir communiquer maintenant. Voyons ici la méthode Send. Commencons par voir comment envoyer des messages. Pour envoyer des messages nous utilisons des array de bytes. Si vous voulez envoyer du text vous devez convertir votre text en bytes et le reconvertir par la suite en text en prenant bien soin d'utiliser le meme encoding. Je n'entrerai pas dans les details de conversion de text car cela sors du contexte de ce tutoriel. Voici un exemple:
byte[] sendBuffer = new byte[] { 0x00, 0x20, 0x44, 0xFF, 0xFF, 0xFF };
int bytesSent = socket.Send(sendBuffer);
Il est important d'attraper les erreurs pouvant sortir de socket.Send, notamment ObjectDisposedException et SocketException. Les raisons de ces erreurs sont les meme que ceux pouvant survenir lors d'un appel a la methode Connect. Il y a une autre chose a verifier en plus. Il faut verifier la valeur de retour de Send. Si cette valeur est de zero, alors la connection a ete fermée:
if (bytesSent == 0)
{
Console.WriteLine("Une erreur est survenue lors de l'envoie de donnees a travers le socket");
throw new ApplicationException("Erreur d'envoie des donnees par le socket");
}
La réception de données
Maintenant pour recevoir des données. Le principle est le meme que pour les envoyés. La difference est que nous déclarons un array de bytes vide (Attention: Elle doit etre asser grande pour contenir les informations recues!) que la methode Receive remplira ensuite avec les bytes recus:
byte[] recvBuffer = new byte[1024];
int bytesReceived = socket.Receive(recvBuffer);
Il faut, comme pour la méthode Send, faire attention de vérifier les exceptions et la valeur de retour de la méthode qui nous dit combien de bytes a été recu. Une valeur de retour de 0 indique toujours que la connection a été fermée.
if (bytesReceived == 0)
{
Console.WriteLine("Une erreur est survenue lors de la reception de donnees a travers le socket");
throw new ApplicationException("Erreur de reception des donnees par le socket");
}
Maintenant pour la réception nous avons une petite étape de plus pour récupérer les données. Nous ne pouvons lire au complet l'array recvBuffer car elle n'est pas completement remplie par les données recues. Nous devons donc copier les données du buffer vers un nouvel array de la bonne taille avant de pouvoir travailler sur les données recues. Nous déclarons un nouvel array de la taille de bytesReceived pour ensuite copier les donnees recues:
byte[] data = new byte[bytesReceived];
Array.Copy(recvBuffer, 0, data, 0, bytesReceived);
Console.Write("Nous avons recu les donnees suivantes du socket: ");
foreach (byte b in data)
Console.Write(b.ToString("X") + " ");
Console.Write(Environment.NewLine);
Et pour conclure..
Voila! J'espere que vous avez apprécier mon premier tutoriel sur ce site (et mon premier tutoriel en Francais d'ailleurs!). Si vous avez des suggestions, commentaires et/ou corrections n'hésitez pas!
Voici le petit program complet
Cliquez pour révéler
Cliquez pour masquer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace MikeDotNet.Examples.SocketTutorial
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Bienvenue sur le program demo du tutoriel sur les sockets et .NET!");
Console.WriteLine("Nous allons debuter dans 5 secondes");
Console.WriteLine();
System.Threading.Thread.Sleep(5000);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse("127.0.0.1");
IPEndPoint endPoint = new IPEndPoint(address, 3405);
try
{
Console.WriteLine("Connection en cours...");
socket.Connect(endPoint);
}
catch (SocketException ex)
{
Console.WriteLine("La connection n'a pu etre etablie. Pour connaitre la raison il faut regarder la propriete ErrorCode de l'exception");
Console.WriteLine("Erreur socket: " + ex.ErrorCode);
//References sur les codes erreurs: http://msdn.microsoft.com/en-us/library ... 85%29.aspx
}
catch (ArgumentNullException)
{
Console.WriteLine("Votre IPEndPoint n'est pas definit.");
}
catch (ObjectDisposedException)
{
Console.WriteLine("Cet erreur arrive si vous essayez de re utiliser un socket");
}
catch (InvalidOperationException)
{
Console.WriteLine("Le socket est en mode ecoute pour accepter des connections. Dans ce cas il ne peut ce connecter");
}
Console.WriteLine("Bravo! vous etes connecter!");
Console.WriteLine();
try
{
Console.WriteLine("Envoie de donnees en cours...");
System.Threading.Thread.Sleep(5000);
byte[] sendBuffer = new byte[] { 0x00, 0x20, 0x44, 0xFF, 0xFF, 0xFF };
int bytesSent = socket.Send(sendBuffer);
if (bytesSent == 0)
{
Console.WriteLine("Une erreur est survenue lors de l'envoie de donnees a travers le socket");
throw new ApplicationException("Erreur d'envoie des donnees par le socket");
}
Console.WriteLine("Nous avons envoyer {0} bytes", bytesSent);
Console.WriteLine();
System.Threading.Thread.Sleep(5000);
Console.WriteLine("Reception de donnees en cours...");
byte[] recvBuffer = new byte[1024];
int bytesReceived = socket.Receive(recvBuffer);
if (bytesReceived == 0)
{
Console.WriteLine("Une erreur est survenue lors de la reception de donnees a travers le socket");
throw new ApplicationException("Erreur de reception des donnees par le socket");
}
byte[] data = new byte[bytesReceived];
Array.Copy(recvBuffer, 0, data, 0, bytesReceived);
Console.Write("Nous avons recu les donnees suivantes du socket: ");
foreach (byte b in data)
Console.Write(b.ToString("X") + " ");
Console.Write(Environment.NewLine);
Console.WriteLine();
Console.WriteLine("Fin du demo de la partie 1 du tutoriel sur les sockets");
}
catch (SocketException ex)
{
Console.WriteLine("Une operation sur le socket n'a pas plus etre completer. Le code d'erreur socket est le " + ex.ErrorCode);
throw;
}
Console.WriteLine("Appuyer sur une touche pour quitter");
Console.ReadKey();
}
}
}
Cliquez pour révéler
Cliquez pour masquer
Voici un petit serveur de test super basic si vous voulez tester. SVP notez que vous ne devriez jamas utiliser ce genre de code (ca marche pour l'exemple mais c'est tres mal fait, ca ma pris 30 secondes). Il est important a noter que si vous voulez utiliser ce petit serveur pour tester vous devez le démarrer en premier et puis autoriser votre pare-feu a accepter les connections vers ce programme.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace MikeDotNet.Examples.SimpleSocketServer
{
class Program
{
static void Main(string[] args)
{
try
{
TcpListener listener = new TcpListener(3405);
listener.Start();
byte[] bytes = new byte[256];
while (true)
{
Console.WriteLine("Waiting for connection...");
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Connected!");
NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
//send back the response
stream.Write(bytes, 0, i);
}
client.Close();
}
}
catch (SocketException ex)
{
Console.WriteLine("Socket exception: " + ex.ErrorCode);
}
Console.Read();
}
}
}