Introduction
Bonjour et bienvenue sur la deuxième partie de mon tutoriel sur les sockets. Dans ce tutoriel, nous allons voir comment faire un client en socket qui peut gérer les connections, déconnections, envoie et réception de données.
La classe, que j'ai décider d'apeller 'CustomSocket', contiendra les fonctions suivantes:
-Une méthode Connect.
-Une méthode Disconnect.
-Une méthode Send.
-Un 'event' DataReceived (Données recues).
Cette classe a pour but de faciliter la gestion des sockets et de fournir une facon réutilisable pour toutes vos connections sockets. Je resterai toujours pour ce tutoriel dans le synchrone: J'utiliserai un thread en boucle pour lire les données en provenance du socket. Dans le prochain tutoriel je vous montrerai comment faire la meme classe asynchrone.
Prérequis
-Le tutoriel [Socket][.NET] Partie 1 - Les bases. Ne vous avancez pas ici si vous n'avez pas compris le premier tutoriel!
La classe CustomSocket
Voyons premierement les Propriétés requises de la classe:
//public
public IPEndPoint EndPoint { get; set; }
public event EventHandler<DataReceivedEventArgs> DataReceived;
//private
private Socket socket;
private bool connected = false;
Donc nous avons ici une variable pour notre EndPoint de connection, une pour le socket comme tel. Les deux autres nous allons y arriver.
En premier lieu nous prenons un IPEndPoint comme parametre pour notre constructeur. Nous pouvons ici initializer le socket.
public CustomSocket(IPEndPoint endPoint)
{
this.EndPoint = endPoint;
this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
Ensuite les méthodes Disconnect et Send sont ce que nous avons fait dans le dernier tutoriel mais regrouper dans ces méthodes:
public void Disconnect()
{
try
{
this.connected = false;
this.socket.Close();
}
catch { }
}
public bool Send(byte[] data)
{
try
{
int bytesSent = socket.Send(data);
return bytesSent == data.Length;
}
catch (SocketException ex)
{
return false;
}
catch (ArgumentNullException)
{
return false;
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException)
{
return false;
}
return true;
}
Pour la méthode connect maintenant. Nous allons ajouter un petit extra; nous allons partir un thread qui sera responsable de l'écoute sur le socket:
public bool Connect()
{
try
{
socket.Connect(this.EndPoint);
new Thread(new ThreadStart(this.ListenThreadMethod)).Start();
}
catch (SocketException)
{
return false;
}
catch (ArgumentNullException)
{
return false;
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException)
{
return false;
}
this.connected = true;
return true;
}
Portez une attention particuliere a la ligne suivante:
new Thread(new ThreadStart(this.ListenThreadMethod)).Start();
Quand nous allons recevoir des données, nous pourrons alors lancé un event qui pourra etre écouter par quiconque est interesser a recevoir les données, j'aime bien faire une méthode protected pour mes events, car il est ensuite possible de les lancés a partir d'une dérivation de la classe. Ca enleve de la pesanteur du code a ne pas avoir a vérifier a chaque fois si l'event est null:
protected void onDataReceived(object sender, DataReceivedEventArgs e)
{
if (DataReceived != null)
{
DataReceived(sender, e);
}
}
Finalement pour notre petite classe CustomSocket, voici le thread qui s'occupe d'écouter sur le socket:
private void ListenThreadMethod()
{
byte[] recvBuffer = new byte[1024];
int bytesReceived = 0;
while (connected)
{
try
{
bytesReceived = socket.Receive(recvBuffer);
if (bytesReceived == 0)
{
return;
}
byte[] data = new byte[bytesReceived];
Array.Copy(recvBuffer, 0, data, 0, bytesReceived);
onDataReceived(this, new DataReceivedEventArgs(data));
}
catch (SocketException)
{
return;
}
catch (ArgumentNullException)
{
return;
}
catch (ObjectDisposedException)
{
return;
}
catch (InvalidOperationException)
{
return;
}
}
}
Vous noterez ici que c'est le meme code que avant, je l'ai seulement mis dans une boucle. La variable connected est définie a true lors de l'appel de Connect() et false lors de l'appel de Disconnect(). Idéalement il faudrait aussi prendre en considération les déconnections a partir du serveur pour mettre cette variable false. Nous ajoutons également un appel a la méthode onDataReceived une fois les données lues.
L'utilisation de la classe CustomSocket
Vous pouvez toujours utiliser le petit serveur fourni dans le précédent tutoriel. L'utilisation de cette classe est tres simple. Je vais seulement vous montrer le code avec quelque descriptions a l'intérieur, je crois que cela devrait suffire pour comprendre.
static void Main(string[] args)
{
CustomSocket cSocket = new CustomSocket(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3405));
cSocket.DataReceived += new EventHandler<DataReceivedEventArgs>(cSocket_DataReceived);
Console.WriteLine("Connection en cours");
if (!cSocket.Connect())
{
Console.WriteLine("Echec de connection");
Console.ReadKey();
return;
}
Console.WriteLine("Connection reussie");
Thread.Sleep(1000);
Console.WriteLine("Envoie des premiere donnees");
byte[] b1 = new byte[] { 0x00, 0x00, 0x00, 0x00, };
if (!cSocket.Send(b1))
{
Console.WriteLine("Echec d'envoi de donnees");
Console.ReadKey();
return;
}
Thread.Sleep(1000);
Console.WriteLine("Envoir des dernieres donnees");
byte[] b2 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if (!cSocket.Send(b2))
{
Console.WriteLine("Echec d'envoi de donnees");
Console.ReadKey();
return;
}
Thread.Sleep(1000);
Console.WriteLine("Envoir des donnees reussi!");
Console.ReadKey();
}
Et puis l'attaché a l'événement DataReceived:
static void cSocket_DataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Nous avons recu {0} bytes", e.Data.Length);
}
Conclusion
Alors j'espere que ce tutoriel sera utile a au moin une personne! Cette méthode d'utilisation de socket est tres correct. Cependant si vous avez dans l'intention d'utiliser plusieurs connections en simultaner, il est préférable d'utiliser des méthodes asynchrones. La création d'un nouveau thread par connection peut bien vite devenir lourd pour votre systeme!
Alors je vous dis merci d'avoir lu mon tutoriel, et voici le code en entier du CustomSocket:
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;
using System.Threading;
namespace MikeDotNet.Examples.SocketTutorialPart2
{
public class CustomSocket
{
//public
public IPEndPoint EndPoint { get; set; }
public event EventHandler<DataReceivedEventArgs> DataReceived;
//private
private Socket socket;
private bool connected = false;
//ctor
public CustomSocket(IPEndPoint endPoint)
{
this.EndPoint = endPoint;
this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
//methods
public bool Connect()
{
try
{
socket.Connect(this.EndPoint);
new Thread(new ThreadStart(this.ListenThreadMethod)).Start();
}
catch (SocketException)
{
return false;
}
catch (ArgumentNullException)
{
return false;
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException)
{
return false;
}
this.connected = true;
return true;
}
public void Disconnect()
{
try
{
this.connected = false;
this.socket.Close();
}
catch { }
}
public bool Send(byte[] data)
{
try
{
int bytesSent = socket.Send(data);
return bytesSent == data.Length;
}
catch (SocketException ex)
{
return false;
}
catch (ArgumentNullException)
{
return false;
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException)
{
return false;
}
return true;
}
protected void onDataReceived(object sender, DataReceivedEventArgs e)
{
if (DataReceived != null)
{
DataReceived(sender, e);
}
}
private void ListenThreadMethod()
{
byte[] recvBuffer = new byte[1024];
int bytesReceived = 0;
while (connected)
{
try
{
bytesReceived = socket.Receive(recvBuffer);
if (bytesReceived == 0)
{
return;
}
byte[] data = new byte[bytesReceived];
Array.Copy(recvBuffer, 0, data, 0, bytesReceived);
onDataReceived(this, new DataReceivedEventArgs(data));
}
catch (SocketException)
{
return;
}
catch (ArgumentNullException)
{
return;
}
catch (ObjectDisposedException)
{
return;
}
catch (InvalidOperationException)
{
return;
}
}
}
}
}