web-dev-qa-db-fra.com

Envoi d'une valeur du serveur au client avec des sockets

J'utilise les projets suivants afin de créer une communication asynchrone entre les sockets serveur et client . Lorsque j'exécute ces projets, j'envoie un message du client au serveur, j'ai donc reçu le message:

Données: enregistrement EOF, envoyé 14 octets au client.

Ce que je veux réaliser, c'est envoyer une variable booléenne du serveur au client avec les sockets. Est-ce faisable, je me demande puisque dans le code j'ai le serveur qui attend et écoute et le client qui envoie des données, puis-je faire le contraire? En général, ce que je veux, c'est envoyer un booléen à plusieurs clients. Pourquoi ai-je besoin de la fin du fichier pour envoyer une chaîne? Est-il nécessaire de tout convertir en chaîne?

[~ # ~] edit [~ # ~] : En général, ce que je veux, c'est envoyer une variable d'un ordinateur à deux autres pour un processus pour commencer simultanément sur tous les ordinateurs. En fait, pour créer un commutateur qui donne un signal pour commencer un processus dans 2-3 machines en même temps.

J'ai essayé d'utiliser le code suivant pour serveur :

class Program
{
    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static void Main(string[] args)
    {
        //---listen at the specified IP and port no.---
        IPAddress localAdd = IPAddress.Parse(SERVER_IP);
        TcpListener listener = new TcpListener(localAdd, PORT_NO);
        Console.WriteLine("Listening...");
        listener.Start();
        //---incoming client connected---
        TcpClient client = listener.AcceptTcpClient();
        //---get the incoming data through a network stream---
        NetworkStream nwStream = client.GetStream();
        byte[] buffer = new byte[client.ReceiveBufferSize];
        //---read incoming stream---
        int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
        //---convert the data received into a string---
        string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
        Console.WriteLine("Received : " + dataReceived);
        //---write back the text to the client---
        Console.WriteLine("Sending back : " + dataReceived);
        nwStream.Write(buffer, 0, bytesRead);
        client.Close();
        listener.Stop();
        Console.ReadLine();
    }
}

et pour client :

class Program
{
    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static void Main(string[] args)
    {
        //---data to send to the server---
        string textToSend = DateTime.Now.ToString();
        //---create a TCPClient object at the IP and port no.---
        TcpClient client = new TcpClient(SERVER_IP, PORT_NO);
        NetworkStream nwStream = client.GetStream();
        byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(textToSend);
        //---send the text---
        Console.WriteLine("Sending : " + textToSend);
        nwStream.Write(bytesToSend, 0, bytesToSend.Length);
        //---read back the text---
        byte[] bytesToRead = new byte[client.ReceiveBufferSize];
        int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
        Console.WriteLine("Received : " + Encoding.ASCII.GetString(bytesToRead, 0, bytesRead));
        Console.ReadLine();
        client.Close();
    }
}

Juste au cas où je travaille dans la même machine. J'aurai au total 4 machines et je veux que l'une d'elles donne un chant aux autres pour commencer l'enregistrement du flux rgb. Ainsi, le serveur doit envoyer un signal aux clients pour commencer l'enregistrement. Que dois-je faire pour changer le comportement du serveur pour envoyer des données et ne pas écouter. Est-il possible que plusieurs machines écoutent et attendent qu'un signal soit donné?

MODIFIER:

private void mouseClick1(object sender, MouseEventArgs e)
    {

        Thread thread = new Thread(() => StartServer());
        thread.Start();

        if (e.Button == MouseButtons.Left)
        {
            button5.Enabled = false;
            button3.Enabled = true;

            try
            {
                obj = new Capturer();
            }
            catch (Exception e1)
            {
                Console.WriteLine("The process failed: {0}", e1.ToString());
            }
        }
    }

    private void mouseClick2(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Right)
        {
            obj.flag2 = true;
        }
    }

Mon code tel qu'il est maintenant avec un clic gauche appelle la fonction startServer () avec un nouveau thread qui est le code principal dans l'implémentation @Ians et ensuite j'appelle mon objet. Lorsque je clique avec le bouton droit de la souris, je change un indicateur et le capturant s'arrête. Comment puis-je également arrêter le serveur ou faire une pause afin de l'ouvrir à nouveau avec le clic gauche?

17
Jose Ramon

Réponses aux questions d'abord:

Q: Est-il nécessaire de tout convertir en chaîne? ... En général, ce que je veux, c'est envoyer une variable d'un ordinateur à deux autres afin qu'un processus démarre simultanément sur tous les ordinateurs.

R: Non, il n'est pas nécessaire de tout convertir en chaîne lors de l'envoi à l'aide de Socket. Vous pouvez envoyer byte[] Que vous voulez très probablement.

Q: Ce que je veux réaliser, c'est envoyer une variable booléenne du serveur au client avec les sockets

R: Voulez-vous dire boolean ou byte? Parce que le type de variable de base que vous obtiendrez du Socket est byte. Vous pouvez toujours changer byte en bool du côté émetteur/récepteur en faisant comme

bool val = byteToCheck > 0;

A2: Et comme votre serveur est une application Console, je recommande de jeter un œil à la conversion hexadécimale string en byte[]. De cette façon, vous pourriez écrire quelque chose dans string mais l'interpréter comme byte[]. Vérifiez ceci . L'idée ici est assez simple. C'est-à-dire: vous tapez string, mais il sera envoyé en tant que byte[]. Et comme il s'agit de byte[], Vous pouvez y ajouter n'importe quelle valeur.

Et ici, je présente ma solution pour gérer vos (1) clients multiples, (2) Async connecter et accepter et recevoir, mais ayant (3) envoyer la synchronisation, ainsi que (4) la conversion à partir de hex string à byte[] (la structure et l'idée), et le dernier mais non le moindre (5) code de travail avec entrée utilisateur (pour que vous puissiez changer cette partie) pour le test!

Je résoudrais un tel problème en utilisant une simple classe Socket, car c'est la solution que je connais le mieux. Mais vous pourriez toujours faire de même si vous utilisez votre TcpListener.Server (Qui est le réseau sous-jacent de la classe Socket). Et, comme vous le souhaitez, je le ferais avec ASync.

Il y a plusieurs étapes nécessaires pour réaliser ce que vous voulez à la fois sur votre serveur et votre client:


Serveur

  1. Créez votre Socket comme champ de classe plutôt que comme champ de méthode, car vous l'utiliserez partout et vous aurez besoin de plusieurs méthodes pour obtenir ce que vous voulez. Et initialisez-le dès que vous avez commencé votre routine principale.

    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static Socket serverSocket; //put here as static
    static void Main(string[] args) {
        //---listen at the specified IP and port no.---
        Console.WriteLine("Listening...");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
        serverSocket.Listen(4); //the maximum pending client, define as you wish
        //your next main routine
    }
    
  2. Étant donné que le serveur servira de nombreux clients, je vous recommanderai d'utiliser ASync plutôt que Sync pour le processus. Initialisez votre Socket en utilisant BeginAccept plutôt qu'en utilisant Accept, mettez acceptCallback dans votre BeginAccept

    static void Main(string[] args) {
        //---listen at the specified IP and port no.---
        Console.WriteLine("Listening...");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
        serverSocket.Listen(4); //the maximum pending client, define as you wish
        serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);      
        //other stuffs
    }
    
  3. Définissez acceptCallback, où vous irez lorsque vous accepterez un Socket. Mettez-y EndAccept.

    private void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
        System.Net.Sockets.Socket socket = null;
        try {
            socket = serverSocket.EndAccept(result); // To get your client socket
            //do something later
        } catch (Exception e) { // this exception will happen when "this" is be disposed...        
            //do something later
        }
    }
    
  4. Je répertorierais généralement mes sockets client et ferais quelque chose à la disposition du client (qui n'est pas répertorié) - mais cela dépend du besoin. Dans ce cas, vous semblez en avoir besoin. Et n'oubliez pas de créer des tampons, etc ... C'est pour tamponner les données entrantes.

  5. Commencez à accepter quelque chose reçu du client, en utilisant un autre ASyncBeginReceive sur le client Socket (et maintenant vous avez besoin de receiveCallback). Ensuite, très important , répétez votre BeginAccept pour accepter d'autres clients!

    private const int BUFFER_SIZE = 4096;
    private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
    private static List<Socket> clientSockets = new List<Socket>(); //may be needed by you
    private static void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
        Socket socket = null;
        try {
            socket = serverSocket.EndAccept(result); // The objectDisposedException will come here... thus, it is to be expected!
            //Do something as you see it needs on client acceptance such as listing
            clientSockets.Add(socket); //may be needed later
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
            serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
        } catch (Exception e) { // this exception will happen when "this" is be disposed...        
            //Do something here
            Console.WriteLine(e.ToString());
        }
    }
    
  6. Définissez votre receiveCallback, c'est-à-dire lorsque vous recevez quelque chose de votre client. Cette partie pourrait être assez délicate à cause des échecs! Mais fondamentalement, ce dont vous avez besoin pour l'instant est simplement EndReceive et encore, très important , pour répéter le BeginReceive de le même client pour que vous puissiez recevoir son prochain message!

    const int MAX_RECEIVE_ATTEMPT = 10;
    static int receiveAttempt = 0; //this is not fool proof, obviously, since actually you must have multiple of this for multiple clients, but for the sake of simplicity I put this
    private static void receiveCallback(IAsyncResult result) {
        Socket socket = null;
        try {
            socket = (Socket)result.AsyncState; //this is to get the sender
            if (socket.Connected) { //simple checking
                int received = socket.EndReceive(result);
                if (received > 0) {
                    byte[] data = new byte[received]; //the data is in the byte[] format, not string!
                    Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                    //DO SOMETHING ON THE DATA IN byte[] data!! Yihaa!!
                    Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else
                    receiveAttempt = 0; //reset receive attempt
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //fail but not exceeding max attempt, repeats
                    ++receiveAttempt; //increase receive attempt;
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                } else { //completely fails!
                    Console.WriteLine("receiveCallback fails!"); //don't repeat beginReceive
                    receiveAttempt = 0; //reset this for the next connection
                }
            }
        } catch (Exception e) { // this exception will happen when "this" is be disposed...
            Console.WriteLine("receiveCallback fails with exception! " + e.ToString());
        }
    }
    
  7. Et supposons que vous souhaitiez répondre à votre expéditeur après avoir reçu le message, faites-le simplement dans la partie if (received > 0):

    if (received > 0) {
        byte[] data = new byte[received]; //the data is in the byte[] format, not string!
        //DO SOMETHING ON THE DATA int byte[]!! Yihaa!!
        Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else                     
    
        //Message retrieval part
        //Suppose you only want to declare that you receive data from a client to that client
        string msg = "I receive your message on: " + DateTime.Now;
        socket.Send(Encoding.ASCII.GetBytes(msg)); //Note that you actually send data in byte[]
        Console.WriteLine("I sent this message to the client: " + msg);
    
        receiveAttempt = 0; //reset receive attempt
        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
    }
    
  8. Et après avoir mis un peu plus de choses dans votre routine principale, vous avez terminé (!) - [~ # ~] si [~ # ~] vous le faites ne pas demander l'envoi au client en tant que byte[]

  9. Et maintenant, si vous voulez envoyer quelque chose à tous vos clients en tant que byte[], Il vous suffit de lister tous vos clients (voir étape 4 -5). Voir this et convertir le resultstring ci-dessus (n'oubliez pas de le taper au format hex string comme requis) en byte[] Puis envoyez-le à tous les clients en utilisant votre liste de socket client (voici où cela est nécessaire!):

    static void Main(string[] args) {
        //---listen at the specified IP and port no.---
        Console.WriteLine("Listening...");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
        serverSocket.Listen(4); //the maximum pending client, define as you wish
        serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);      
    
        //normally, there isn't anything else needed here
        string result = "";
        do {
            result = Console.ReadLine();
            if (result.ToLower().Trim() != "exit") {
                byte[] bytes = null;
                //you can use `result` and change it to `bytes` by any mechanism which you want
                //the mechanism which suits you is probably the hex string to byte[]
                //this is the reason why you may want to list the client sockets
                foreach(Socket socket in clientSockets)
                    socket.Send(bytes); //send everything to all clients as bytes
            }
        } while (result.ToLower().Trim() != "exit");
    }
    

Et ici, vous avez plus ou moins fini avec votre serveur. Vient ensuite votre client


Client:

  1. De même, placez la classe Socket dans le contexte de la classe plutôt que dans le contexte de la méthode et initialisez-la dès que vous démarrez votre programme

    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static Socket clientSocket; //put here
    static void Main(string[] args) {
        //Similarly, start defining your client socket as soon as you start. 
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
        //your other main routines
    }
    
  2. Commencez ensuite à vous connecter par ASyncBeginConnect. J'irais normalement plus loin en LoopConnect juste pour une gestion des pannes comme celle-ci.

    static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
        int attempts = 0;
        while (!clientSocket.Connected && attempts < noOfRetry) {
            try {
                ++attempts;
                IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnect, null);
                result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
                System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
            } catch (Exception e) {
                Console.WriteLine("Error: " + e.ToString());
            }
        }
        if (!clientSocket.Connected) {
            Console.WriteLine("Connection attempt is unsuccessful!");
            return;
        }
    }
    
  3. Concept similaire à ce que vous faites au serveur BeginAccept, vous devez définir endConnectCallback pour le ASyncBeginConnect que vous utilisez. Mais ici, contrairement au serveur qui doit rappeler BeginAccept, une fois que vous êtes connecté, vous n'avez pas besoin de faire de nouvelles BeginConnect puisque vous n'avez besoin d'être connecté qu'une seule fois .

  4. Vous voudrez peut-être déclarer buffer etc. Ensuite, après vous être connecté, n'oubliez pas le prochain ASyncBeginReceive pour gérer la partie de récupération des messages (similaire avec le serveur)

    private const int BUFFER_SIZE = 4096;
    private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
    private static void endConnectCallback(IAsyncResult ar) {
        try {
            clientSocket.EndConnect(ar);
            if (clientSocket.Connected) {
                clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
            } else {
                Console.WriteLine("End of connection attempt, fail to connect...");
            }
        } catch (Exception e) {
            Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
        }
    }
    
  5. Naturellement, vous devez définir votre receiveCallback, tout comme ce que vous avez fait pour le serveur. Et oui, c'est comme vous l'avez deviné, c'est presque identique à ce que vous avez fait pour le serveur!

  6. Vous pouvez faire tout ce que vous voulez avec vos données. Notez que les données que vous recevez se trouvent en fait dans byte[], Pas dans string. Vous pouvez donc tout faire avec. Mais par exemple , je vais simplement utiliser string pour afficher.

    const int MAX_RECEIVE_ATTEMPT = 10;
    static int receiveAttempt = 0;
    private static void receiveCallback(IAsyncResult result) {
        System.Net.Sockets.Socket socket = null;
        try {
            socket = (System.Net.Sockets.Socket)result.AsyncState;
            if (socket.Connected) {
                int received = socket.EndReceive(result);
                if (received > 0) {
                    receiveAttempt = 0;
                    byte[] data = new byte[received];
                    Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //copy the data from your buffer
                    //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
                    //Notice that your data is not string! It is actually byte[]
                    //For now I will just print it out
                    Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
                    ++receiveAttempt;
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                } else { //completely fails!
                    Console.WriteLine("receiveCallback is failed!");
                    receiveAttempt = 0;
                    clientSocket.Close();
                }
            }
        } catch (Exception e) { // this exception will happen when "this" is be disposed...
            Console.WriteLine("receiveCallback is failed! " + e.ToString());
        }
    }
    
  7. Et à la toute dernière ... Oui, encore une fois, comme vous l'avez déjà deviné, il vous suffit de faire quelque chose sur votre routine principale - supposons que vous vouliez l'utiliser pour envoyer des données. Étant donné que vous utilisez Console mais que vous souhaitez qu'il envoie des éléments sous la forme byte[], Vous devez effectuer la conversion (voir l'explication dans le serveur 9.). Et après, vous avez complètement terminé !!

    static void Main(string[] args) {
        //Similarly, start defining your client socket as soon as you start. 
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        loopConnect(3, 3); //for failure handling
        string result = "";
        do {
            result = Console.ReadLine(); //you need to change this part
            if (result.ToLower().Trim() != "exit") {
                byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
                //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
                clientSocket.Send(bytes);
            }
        } while (result.ToLower().Trim() != "exit");
    }
    

Résultats:

Voici! Je l'ai testé en envoyant string pour l'affichage, mais j'ai déjà mis en place ce qui est nécessaire lorsque vous voulez le changer en byte[]

enter image description here

enter image description here


Code pour votre test:

Serveur

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpListenerConsoleApplication {
    class Program {
        const int PORT_NO = 2201;
        const string SERVER_IP = "127.0.0.1";
        static Socket serverSocket;
        static void Main(string[] args) {
            //---listen at the specified IP and port no.---
            Console.WriteLine("Listening...");
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
            serverSocket.Listen(4); //the maximum pending client, define as you wish
            serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);      
            string result = "";
            do {
                result = Console.ReadLine();
            } while (result.ToLower().Trim() != "exit");
        }

        private const int BUFFER_SIZE = 4096;
        private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
        private static void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
            Socket socket = null;
            try {
                socket = serverSocket.EndAccept(result); // The objectDisposedException will come here... thus, it is to be expected!
                //Do something as you see it needs on client acceptance
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
            } catch (Exception e) { // this exception will happen when "this" is be disposed...        
                //Do something here             
                Console.WriteLine(e.ToString());
            }
        }

        const int MAX_RECEIVE_ATTEMPT = 10;
        static int receiveAttempt = 0; //this is not fool proof, obviously, since actually you must have multiple of this for multiple clients, but for the sake of simplicity I put this
        private static void receiveCallback(IAsyncResult result) {
            Socket socket = null;
            try {
                socket = (Socket)result.AsyncState; //this is to get the sender
                if (socket.Connected) { //simple checking
                    int received = socket.EndReceive(result);
                    if (received > 0) {
                        byte[] data = new byte[received]; //the data is in the byte[] format, not string!
                        Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                        //DO SOMETHING ON THE DATA int byte[]!! Yihaa!!
                        Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else                     

                        //Message retrieval part
                        //Suppose you only want to declare that you receive data from a client to that client
                        string msg = "I receive your message on: " + DateTime.Now;                      
                        socket.Send(Encoding.ASCII.GetBytes(msg)); //Note that you actually send data in byte[]
                        Console.WriteLine("I sent this message to the client: " + msg);

                        receiveAttempt = 0; //reset receive attempt
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                    } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //fail but not exceeding max attempt, repeats
                        ++receiveAttempt; //increase receive attempt;
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                    } else { //completely fails!
                        Console.WriteLine("receiveCallback fails!"); //don't repeat beginReceive
                        receiveAttempt = 0; //reset this for the next connection
                    }
                }
            } catch (Exception e) { // this exception will happen when "this" is be disposed...
                Console.WriteLine("receiveCallback fails with exception! " + e.ToString());
            }
        }

    }
}

Client

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpClientConsoleApplication {
    class Program {
        const int PORT_NO = 2201;
        const string SERVER_IP = "127.0.0.1";
        static Socket clientSocket; //put here
        static void Main(string[] args) {
            //Similarly, start defining your client socket as soon as you start. 
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            loopConnect(3, 3); //for failure handling
            string result = "";
            do {
                result = Console.ReadLine(); //you need to change this part
                if (result.ToLower().Trim() != "exit") {
                    byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
                    //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
                    clientSocket.Send(bytes);
                }
            } while (result.ToLower().Trim() != "exit");
        }

        static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
            int attempts = 0;
            while (!clientSocket.Connected && attempts < noOfRetry) {
                try {
                    ++attempts;
                    IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null);
                    result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
                    System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
                } catch (Exception e) {
                    Console.WriteLine("Error: " + e.ToString());
                }
            }
            if (!clientSocket.Connected) {
                Console.WriteLine("Connection attempt is unsuccessful!");
                return;
            }
        }

        private const int BUFFER_SIZE = 4096;
        private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
        private static void endConnectCallback(IAsyncResult ar) {
            try {
                clientSocket.EndConnect(ar);
                if (clientSocket.Connected) {
                    clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
                } else {
                    Console.WriteLine("End of connection attempt, fail to connect...");
                }
            } catch (Exception e) {
                Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
            }
        }

        const int MAX_RECEIVE_ATTEMPT = 10;
        static int receiveAttempt = 0;
        private static void receiveCallback(IAsyncResult result) {
            System.Net.Sockets.Socket socket = null;
            try {
                socket = (System.Net.Sockets.Socket)result.AsyncState;
                if (socket.Connected) {
                    int received = socket.EndReceive(result);
                    if (received > 0) {
                        receiveAttempt = 0;
                        byte[] data = new byte[received];
                        Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                        //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
                        //Notice that your data is not string! It is actually byte[]
                        //For now I will just print it out
                        Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                    } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
                        ++receiveAttempt;
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                    } else { //completely fails!
                        Console.WriteLine("receiveCallback is failed!");
                        receiveAttempt = 0;
                        clientSocket.Close();
                    }
                }
            } catch (Exception e) { // this exception will happen when "this" is be disposed...
                Console.WriteLine("receiveCallback is failed! " + e.ToString());
            }
        }
    }
}

Dernières remarques (Modifier) ​​

Étant donné que le code ci-dessus est exécuté à l'aide de Console Application, Il doit être exécuté avec le mot clé static main void. Et donc le client Socket défini ci-dessus est de type static. Cela peut empêcher le client Socket d'être défini plusieurs fois comme à chaque fois qu'il est "défini", car il est du même class nommé Program, il fera référence à la même Socket (bien que cela ne soit pas toujours le cas, du moins selon l'expérience de l'OP: il peut exécuter plusieurs clients avec succès sur le même ordinateur).

Néanmoins, surmonter cela n'est pas si difficile. Portez simplement l'application client sur la plate-forme qui n'est pas initiée en tant que classe static (telle que WinForms) et tout le code ci-dessus fonctionnerait toujours normalement. Sinon, si elle doit être exécutée à l'aide du Console Applications Et que le problème se produit, copiez simplement l'application cliente et redéfinissez-la en utilisant un nom différent namespace ou différent class pour éviter définir Socket identique en raison de namespace ou class identiques.

Mais la partie la plus importante de cette résolution de problèmes est l'utilisation judicieuse de Async et Sync pour résoudre le problème donné.


La suite de ce sujet peut être trouvée ici

27
Ian

Pourquoi ne vous simplifiez-vous pas la vie et n'utilisez-vous pas SignalR?
Ci-dessous, vous pouvez voir un exemple simple où le serveur et les clients sont des applications de console

Client (exécutez ce fichier .exe plusieurs fois)

using System;
using Microsoft.AspNet.SignalR.Client;

namespace SignalRClient
{
    class Program
    {
        private static IHubProxy HubProxy { get; set; }
        const string ServerURI = "http://localhost:1234/signalr";
        private static HubConnection Connection { get; set; }

        static void Main(string[] args)
        {
            Connection = new HubConnection(ServerURI);
            HubProxy = Connection.CreateHubProxy("MyHub");

            HubProxy.On<string, string>("SendMessage", (name, message) => Console.WriteLine(name + ":" + message));
            Connection.Start().Wait();

            Console.WriteLine("Press Enter to stop client");
            Console.ReadLine();
        }
    }
}

Serveur (exécutez ce .exe avant d'exécuter les clients)

using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.Owin.Hosting;
using Owin;

namespace SignalRServer
{
    class Program
    {
        static private IDisposable SignalR { get; set; }
        const string ServerURI = "http://localhost:1234";
        private static IHubProxy HubProxy { get; set; }
        private static HubConnection Connection { get; set; }
        static void Main(string[] args)
        {
            SignalR = WebApp.Start(ServerURI);
            Console.WriteLine("Server running at " + ServerURI);

            Connection = new HubConnection(ServerURI);
            HubProxy = Connection.CreateHubProxy("MyHub");
            HubProxy.On<string, string>("SendMessage", (name, message) => Console.WriteLine(name + ":" + message));
            Connection.Start().Wait();

            string messageToSentToClients;
            do
            {
                Console.WriteLine("Type someting to send to clients and press enter");
                messageToSentToClients = Console.ReadLine();
               HubProxy.Invoke("Send", "Server", messageToSentToClients);
           } while (messageToSentToClients != "exit");

       }
    }

    public class MyHub : Hub
    {
        public void Send(string name, string message) { Clients.All.sendMessage(name, message); }
    }

    class Startup
    {
        public void Configuration(IAppBuilder app) { app.MapSignalR(); }
    }
}

Pour que ce qui précède fonctionne, vous avez besoin des packages NuGet suivants:

Client:
Microsoft.AspNet.SignalR.Client
Serveur
Microsoft.AspNet.SignalR
Microsoft.AspNet.SignalR.Client
Microsoft.AspNet.SignalR.SelfHost
Microsoft.Owin.Host.HttpListener

Si vous souhaitez que le serveur/les clients soient sur des machines différentes, tout ce que vous avez à faire est de modifier les propriétés ServeURI dans les deux projets:

//Clients
const string ServerURI = "http://SERVER_IP:PORT/signalr";
//Server
const string ServerURI = "http://SERVER_IP:PORT"; 

Vous pouvez trouver un autre exemple similaire dans WinForms ici:
https://code.msdn.Microsoft.com/windowsdesktop/Using-SignalR-in-WinForms-f1ec847b

5
George Vovos

Vous pouvez envoyer des données à un client, et cela peut être réalisé de la même manière que vous le faites client -> serveur (à moins que vos sockets ne soient reçus que, si c'est le cas, passez-les pour envoyer et recevoir). L'envoi d'un booléen nécessite une conversion en octet, vous pouvez y parvenir via la classe BitConverter.

2
Jaxter

Sur la page https://msdn.Microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx voir la méthode AcceptCallback (elle est appelée lorsque le client se connecte ). Il y a la ligne Socket handler = listener.EndAccept(ar);. Cela se produit pour chaque client lorsqu'il se connecte, alors conservez ces instances de socket dans une liste. Lorsque vous souhaitez envoyer des données aux clients, utilisez la méthode Send du même exemple avec chaque socket de la liste (ou de manière sélective si vous souhaitez envoyer uniquement à certains clients).

private static void Send(Socket handler, String data) {
    // Convert the string data to byte data using ASCII encoding.
    byte[] byteData = Encoding.ASCII.GetBytes(data);

    // Begin sending the data to the remote device.
    handler.BeginSend(byteData, 0, byteData.Length, 0,
        new AsyncCallback(SendCallback), handler);
}
2
Shadowed