C# Language
Prise asynchrone
Recherche…
Introduction
En utilisant des sockets asynchrones, un serveur peut écouter les connexions entrantes et faire une autre logique dans le même temps, contrairement au socket synchrone lorsqu'il écoute le thread principal et que l'application ne répond plus.
Remarques
Socket et réseau
Comment accéder à un serveur en dehors de mon propre réseau? C'est une question courante et lorsqu'elle est posée, elle est principalement marquée comme sujet.
Du côté serveur
Sur le réseau de votre serveur, vous devez transférer votre routeur sur votre serveur.
Pour un exemple de PC sur lequel le serveur est exécuté:
IP locale = 192.168.1.115
Le serveur écoute le port 1234.
Transférer les connexions entrantes sur le Port 1234
routeur vers 192.168.1.115
Côté client
La seule chose que vous devez changer est l'IP. Vous ne souhaitez pas vous connecter à votre adresse de bouclage mais à l'adresse IP publique du réseau sur lequel votre serveur est exécuté. Cette adresse IP, vous pouvez obtenir ici .
_connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 1234));
Alors maintenant, vous créez une requête sur ce noeud final: 10.10.10.10:1234
si vous avez fait un 10.10.10.10:1234
propriété de votre routeur, votre serveur et votre client se connecteront sans aucun problème.
Si vous voulez vous connecter à une adresse IP locale, vous n'aurez pas besoin de changer l'adresse de bouclage en 192.168.1.178
ou quelque chose comme ça.
Envoi de données:
Les données sont envoyées dans un tableau d'octets. Vous devez emballer vos données dans un tableau d'octets et les décompresser de l'autre côté.
Si vous êtes familier avec socket, vous pouvez également essayer de chiffrer votre tableau d'octets avant de l'envoyer. Cela empêchera quiconque de voler votre colis.
Exemple de socket asynchrone (client / serveur).
Exemple côté serveur
Créer un écouteur pour le serveur
Commencez par créer un serveur qui gérera les clients qui se connectent et les requêtes qui seront envoyées. Donc, créez une classe d'écoute qui gérera cela.
class Listener
{
public Socket ListenerSocket; //This is the socket that will listen to any incoming connections
public short Port = 1234; // on this port we will listen
public Listener()
{
ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
}
Nous devons d'abord initialiser le socket Listener sur lequel nous pouvons écouter toutes les connexions. Nous allons utiliser un socket TCP, c'est pourquoi nous utilisons SocketType.Stream. Aussi, nous spécifions à quel port le serveur doit écouter
Ensuite, nous commençons à écouter toutes les connexions entrantes.
Les méthodes d'arborescence que nous utilisons ici sont:
Cette méthode lie le socket à un IPEndPoint . Cette classe contient les informations d'hôte et de port local ou distant nécessaires à une application pour se connecter à un service sur un hôte.
Le paramètre backlog spécifie le nombre de connexions entrantes pouvant être mises en file d'attente pour acceptation.
ListenerSocket.BeginAccept ();
Le serveur commence à écouter les connexions entrantes et continue avec une autre logique. Lorsqu'il y a une connexion, le serveur revient à cette méthode et exécute la méthode AcceptCallBack
public void StartListening()
{
try
{
MessageBox.Show($"Listening started port:{Port} protocol type: {ProtocolType.Tcp}");
ListenerSocket.Bind(new IPEndPoint(IPAddress.Any, Port));
ListenerSocket.Listen(10);
ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);
}
catch(Exception ex)
{
throw new Exception("listening error" + ex);
}
}
Ainsi, lorsqu'un client se connecte, nous pouvons les accepter par cette méthode:
Trois méthodes que nous utilisons ici sont:
Nous avons commencé le rappel avec
Listener.BeginAccept()
maintenant nous devons terminer cet appel.The EndAccept()
méthodeThe EndAccept()
accepte un paramètre IAsyncResult, cela stockera l'état de la méthode asynchrone. À partir de cet état, nous pouvons extraire le socket d'où provient la connexion entrante.ClientController.AddClient()
Avec le socket obtenu à partir d'
EndAccept()
nous créons un client avec une méthode propre (code ClientController sous exemple de serveur) .Nous devons recommencer à écouter lorsque le socket est terminé en gérant la nouvelle connexion. Transmettez la méthode qui interceptera ce rappel. Et aussi passer dans le socket Listener afin que nous puissions réutiliser ce socket pour les prochaines connexions.
public void AcceptCallback(IAsyncResult ar)
{
try
{
Console.WriteLine($"Accept CallBack port:{Port} protocol type: {ProtocolType.Tcp}");
Socket acceptedSocket = ListenerSocket.EndAccept(ar);
ClientController.AddClient(acceptedSocket);
ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);
}
catch (Exception ex)
{
throw new Exception("Base Accept error"+ ex);
}
}
Maintenant, nous avons un socket d'écoute, mais comment recevons-nous les données envoyées par le client, comme le montre le code suivant.
Créer un récepteur de serveur pour chaque client
Tout d'abord créer une classe de réception avec un constructeur qui prend en paramètre Socket:
public class ReceivePacket
{
private byte[] _buffer;
private Socket _receiveSocket;
public ReceivePacket(Socket receiveSocket)
{
_receiveSocket = receiveSocket;
}
}
Dans la méthode suivante, nous commençons par donner au tampon une taille de 4 octets (Int32) ou le paquet contient des parties {longueur, données réelles}. Donc, les 4 premiers octets, nous réservons pour la longueur des données le reste pour les données réelles.
Ensuite, nous utilisons la méthode BeginReceive () . Cette méthode est utilisée pour commencer à recevoir des clients connectés et lorsqu'elle recevra des données, elle exécutera la fonction ReceiveCallback
.
public void StartReceiving()
{
try
{
_buffer = new byte[4];
_receiveSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
}
catch {}
}
private void ReceiveCallback(IAsyncResult AR)
{
try
{
// if bytes are less than 1 takes place when a client disconnect from the server.
// So we run the Disconnect function on the current client
if (_receiveSocket.EndReceive(AR) > 1)
{
// Convert the first 4 bytes (int 32) that we received and convert it to an Int32 (this is the size for the coming data).
_buffer = new byte[BitConverter.ToInt32(_buffer, 0)];
// Next receive this data into the buffer with size that we did receive before
_receiveSocket.Receive(_buffer, _buffer.Length, SocketFlags.None);
// When we received everything its onto you to convert it into the data that you've send.
// For example string, int etc... in this example I only use the implementation for sending and receiving a string.
// Convert the bytes to string and output it in a message box
string data = Encoding.Default.GetString(_buffer);
MessageBox.Show(data);
// Now we have to start all over again with waiting for a data to come from the socket.
StartReceiving();
}
else
{
Disconnect();
}
}
catch
{
// if exeption is throw check if socket is connected because than you can startreive again else Dissconect
if (!_receiveSocket.Connected)
{
Disconnect();
}
else
{
StartReceiving();
}
}
}
private void Disconnect()
{
// Close connection
_receiveSocket.Disconnect(true);
// Next line only apply for the server side receive
ClientController.RemoveClient(_clientId);
// Next line only apply on the Client Side receive
Here you want to run the method TryToConnect()
}
Nous avons donc configuré un serveur capable de recevoir et d'écouter les connexions entrantes. Lorsqu'un client se connecte, il sera ajouté à une liste de clients et chaque client a sa propre classe de réception. Pour que le serveur écoute:
Listener listener = new Listener();
listener.StartListening();
Quelques classes que j'utilise dans cet exemple
class Client
{
public Socket _socket { get; set; }
public ReceivePacket Receive { get; set; }
public int Id { get; set; }
public Client(Socket socket, int id)
{
Receive = new ReceivePacket(socket, id);
Receive.StartReceiving();
_socket = socket;
Id = id;
}
}
static class ClientController
{
public static List<Client> Clients = new List<Client>();
public static void AddClient(Socket socket)
{
Clients.Add(new Client(socket,Clients.Count));
}
public static void RemoveClient(int id)
{
Clients.RemoveAt(Clients.FindIndex(x => x.Id == id));
}
}
Exemple côté client
Connexion au serveur
Tout d'abord, nous voulons créer une classe qui se connecte au serveur avec le nom que nous lui donnons: Connecteur:
class Connector
{
private Socket _connectingSocket;
}
La méthode suivante pour cette classe est TryToConnect ()
Cette méthode suscite quelques intérêts:
Créer le socket;
Ensuite je boucle jusqu'à ce que le socket soit connecté
Chaque boucle, il ne fait que tenir le fil pendant 1 seconde, nous ne voulons pas DOS le serveur XD
Avec Connect (), il essaiera de se connecter au serveur. Si cela échoue, une exception sera lancée, mais le serveur gardera le programme connecté au serveur. Vous pouvez utiliser une méthode Connect CallBack pour cela, mais je vais simplement appeler une méthode lorsque le socket est connecté.
Notez que le client essaie maintenant de se connecter à votre PC local sur le port 1234.
public void TryToConnect() { _connectingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); while (!_connectingSocket.Connected) { Thread.Sleep(1000); try { _connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234)); } catch { } } SetupForReceiveing(); } } private void SetupForReceiveing() { // View Client Class bottom of Client Example Client.SetClient(_connectingSocket); Client.StartReceiving(); }
Envoi d'un message au serveur
Nous avons donc maintenant une application presque terminée ou Socket. La seule chose que nous n'avons pas jet est une classe pour envoyer un message au serveur.
public class SendPacket
{
private Socket _sendSocked;
public SendPacket(Socket sendSocket)
{
_sendSocked = sendSocket;
}
public void Send(string data)
{
try
{
/* what hapends here:
1. Create a list of bytes
2. Add the length of the string to the list.
So if this message arrives at the server we can easily read the length of the coming message.
3. Add the message(string) bytes
*/
var fullPacket = new List<byte>();
fullPacket.AddRange(BitConverter.GetBytes(data.Length));
fullPacket.AddRange(Encoding.Default.GetBytes(data));
/* Send the message to the server we are currently connected to.
Or package stucture is {length of data 4 bytes (int32), actual data}*/
_sendSocked.Send(fullPacket.ToArray());
}
catch (Exception ex)
{
throw new Exception();
}
}
Enfin, créez deux boutons l'un pour vous connecter et l'autre pour envoyer un message:
private void ConnectClick(object sender, EventArgs e)
{
Connector tpp = new Connector();
tpp.TryToConnect();
}
private void SendClick(object sender, EventArgs e)
{
Client.SendString("Test data from client");
}
La classe de client que j'ai utilisée dans cet exemple
public static void SetClient(Socket socket)
{
Id = 1;
Socket = socket;
Receive = new ReceivePacket(socket, Id);
SendPacket = new SendPacket(socket);
}
Remarquer
La classe de réception du serveur est identique à la classe de réception du client.
Conclusion
Vous avez maintenant un serveur et un client. Vous pouvez utiliser cet exemple de base. Par exemple, faites en sorte que le serveur puisse également recevoir des fichiers ou d'autres éléments. Ou envoyez un message au client. Dans le serveur, vous avez une liste de clients, donc lorsque vous recevez quelque chose que vous connaissez avec le client, il vient.