Подтвердить что ты не робот

Отправка значения с сервера на клиент с сокетами

Я использую следующие проекты, чтобы создать асинхронную связь между сервером и клиент. Когда я запускаю эти проекты, я отправляю сообщение от клиента на сервер, поэтому я получил сообщение:

Данные: запись EOF, отправлено 14 байтам клиенту.

Я хочу добиться отправки логической переменной с сервера клиенту с помощью сокетов. Можно ли это сделать, мне интересно, потому что в коде у меня есть сервер, который ждет и слушает, а клиент, который отправляет данные, могу ли я сделать наоборот? В общем, я хочу отправить логическое несколько клиентов. Зачем мне нужен конец файла для отправки строки? Нужно ли конвертировать все в строку?

EDIT. В общем, я хочу отправить переменную с одного компьютера на два других, чтобы процесс начинался одновременно на всех компьютерах. Фактически для создания переключателя, который дает сигнал для начала процесса на 2-3 компьютерах в одно и то же время.

Я попытался использовать следующий код для сервера:

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();
    }
}

и для клиента:

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();
    }
}

Как раз в том случае, когда я работаю на одной машине. У меня будет всего 4 машины, и я хочу, чтобы один из них дал сингл остальным из них, чтобы начать запись потока rgb. Таким образом, сервер должен отправить сигнал клиентам для начала записи. Что делать, чтобы изменить поведение сервера для передачи данных, а не прослушивания. Возможно ли, чтобы несколько машин прослушивали и ожидали сигнала?

EDIT:

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;
        }
    }

Мой код, как и сейчас, с функцией левого щелчка startServer() с новым потоком, который является основным кодом в реализации @Ians, и после этого я вызываю свой объект. Когда я щелкаю правой кнопкой мыши, я меняю флаг, и захватчик останавливается. Как я могу остановить сервер или сделать паузу, чтобы снова открыть его с помощью левого клика?

4b9b3361

Ответ 1

Сначала ответы на вопросы:

Q: Нужно ли преобразовывать все в строку?... В общем, что я хотите отправить переменную с одного компьютера на два других, чтобы процесс для запуска одновременно на всех компьютерах.

A: Нет, нет необходимости конвертировать все в строку при отправке используя Socket. Вы можете отправить byte[], который вы, скорее всего, захотите.

В: Я хочу добиться отправки логической переменной с сервера клиенту с сокетами

A: Вы имеете в виду boolean или byte? Потому что основной тип переменной, который вы получит от Socket is byte. Вы всегда можете изменить byteна bool со стороны отправителя/получателя, выполнив как

bool val = byteToCheck > 0 ? true : false

A2: И поскольку ваш разорвать Console приложение, я рекомендую принять посмотрите на преобразование hex string в byte[]. Таким образом, вы могли бы напишите что-нибудь в string, но интерпретируйте его как byte[]. Проверьте . Вся эта идея здесь довольно проста. То есть: вы вводите string, но он будет отправлен как byte[]. И поскольку это byte[]вы можете иметь любое значение в нем.

И здесь я представляю свое решение для обработки ваших (1) нескольких клиентов, (2) Async подключиться, принять и получить, но с (3) отправить синхронизацию, а также (4) конвертировать из hex string в byte[] (структура и идея) и последний, но не менее важный (5) рабочий код с пользовательским вводом (для вас, чтобы изменить эту часть) для тестирования!

Я бы решил эту проблему, используя простой класс Socket, так как это решение, с которым я больше всего знаком. Но вы всегда можете сделать это, если используете ваш TcpListener.Server (который является базовой сетью класса Socket). И, как вы пожелаете, я сделал бы это с помощью Async.

Для достижения желаемого уровня как на вашем сервере, так и на вашем клиенте необходимо выполнить несколько шагов:


Сервер

  • Создайте поле Socket как поле класса, а не поле метода, так как вы будете использовать, если повсюду, и вам нужно несколько методов для достижения того, чего вы хотите. И инициализируйте его, как только вы начнете свою основную процедуру.

    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
    }
    
  • Поскольку сервер будет обслуживать множество клиентов, я рекомендую вам использовать Async, а не Sync для процесса. Инициализируйте Socket с помощью BeginAccept вместо использования Accept, поместите acceptCallback в 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
    }
    
  • Определите acceptCallback, где вы будете идти, когда вы принимаете Socket. Положим 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
        }
    }
    
  • Я бы обычно перечислял свои клиентские сокеты и делал что-то по распоряжению клиентами (это не указано в списке), но это зависит от необходимости. В этом случае вам это кажется нужным. И не забудьте создать буферы и т.д. Это для буферизации входящих данных.

  • Начните принимать что-то, полученное от клиента, используя другой Async BeginReceive на клиенте Socket (и теперь вам нужно receiveCallback). Затем очень важно, повторите BeginAccept, чтобы принять других клиентов!

    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());
        }
    }
    
  • Определите свой receiveCallback, то есть когда вы получаете что-то от своего клиента. Эта часть может быть довольно сложной из-за сбоев! Но в принципе, вам сейчас нужно просто EndReceive и снова очень важно, чтобы повторить BeginReceive с того же клиента, чтобы вы могли получить его следующее сообщение!

    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());
        }
    }
    
  • И предположим, что вы хотите ответить отправителю после получения сообщения, просто сделайте это в части 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
    }
    
  • И после того, как вы поместили немного больше вещей в свою основную процедуру, вы закончили (!) - IF, вы не запрашиваете отправки клиенту как byte[]

  • И теперь, если вы хотите отправить что-то всем своим клиентам как byte[], вам просто нужно указать весь ваш клиент (см. шаг 4-5). См. this и преобразуйте result string выше (не забудьте ввести его в формате hex string, если требуется), в byte[], затем отправьте его на все клиенты, использующие список ваших сокетов клиента (здесь, где это необходимо!):

    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");
    }
    

И здесь вы более или менее выполняете свой сервер. Далее ваш клиент


Клиент:

  • Аналогичным образом поместите класс Socket в контекст класса, а не в контекст метода, и инициализируйте его, как только вы запустите свою программу

    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
    }
    
  • Затем подключитесь к Async BeginConnect. Я обычно пошел бы дальше LoopConnect только для обработки ошибок, как это.

    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;
        }
    }
    
  • Похожая концепция того, что вы делаете на сервере BeginAccept, вам нужно определить endConnectCallback для используемого Async BeginConnect. Но здесь в отличие от сервера, который необходимо перезвонить BeginAccept, после того, как вы подключены, вам не нужно делать никаких новых BeginConnect, так как вам нужно только подключиться один раз.

  • Вы можете захотеть объявить buffer и т.д. Затем, после того, как вы подключитесь, не забудьте следующий Async BeginReceive обрабатывать часть извлечения сообщений (аналогично серверу)

    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());
        }
    }
    
  • Естественно, вам нужно определить свой receiveCallback, как и то, что вы сделали для сервера. И да, это, как вы догадались, почти идентично тому, что вы сделали для сервера!

  • Вы можете делать все, что хотите, своими данными. Обратите внимание, что данные, которые вы получаете, фактически находятся в byte[], а не string. Таким образом, вы можете сделать с ней все. Но , например, саке, я просто использую string для отображения.

    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());
        }
    }
    
  • И в самом последнем... Да, опять же, как вы уже догадались, вам просто нужно что-то сделать в своей основной рутине - предположите, что вы хотите использовать его для отправки данных. Поскольку вы используете Console, но хотите, чтобы он отправлял вещи как byte[], вам необходимо выполнить преобразование (см. Пояснение на сервере 9.). И после этого вы полностью закончите!

    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");
    }
    

Результаты:

Здесь вы идете! Я протестировал его, отправив string для отображения, но я уже установил, что нужно, когда вы хотите изменить его на byte[]

введите описание изображения здесь

введите описание изображения здесь


Код для вашего теста:

Сервер

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());
            }
        }
    }
}

Последние комментарии (Изменить)

Поскольку приведенный выше код выполняется с использованием Console Application, он должен выполняться с ключевым словом static main void. И таким образом клиент Socket, определенный выше, имеет тип static. Это может препятствовать тому, чтобы клиент Socket определялся несколько раз, как каждый раз, когда он "определен", поскольку он имеет тот же class с именем Program, он будет ссылаться на тот же Socket (хотя это может не быть всегда, по крайней мере, в соответствии с экспериментом OP: он может успешно запускать несколько клиентов на одном компьютере).

Тем не менее, преодолеть это не так сложно. Просто подключите клиентское приложение к платформе, которая не инициирована как static class (например, WinForms), и все вышеприведенные коды будут работать в обычном режиме. В качестве альтернативы, если он должен быть запущен с помощью Console Applications, и возникает проблема, просто скопируйте клиентское приложение и переопределите его с помощью другого namespace или другого class имени, чтобы избежать определения идентичного Socket из-за идентичных namespace или class.

Но наиболее важной частью этого решения является использование Async и Sync мудро для решения данной проблемы.


Продолжение этой темы можно найти здесь

Ответ 2

Почему бы вам не сделать вашу жизнь проще и использовать SignalR?
Ниже вы можете увидеть простой пример, где сервер и клиенты - консольные приложения.

Клиент (запустите этот .exe много раз)

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();
        }
    }
}

Сервер (запустите этот .exe перед запуском клиентов)

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(); }
    }
}

Для того, чтобы выше работали, вам понадобятся следующие пакеты NuGet:

Клиент:
        Microsoft.AspNet.SignalR.Client
Сервер
        Microsoft.AspNet.SignalR
        Microsoft.AspNet.SignalR.Client
        Microsoft.AspNet.SignalR.SelfHost
        Microsoft.Owin.Host.HttpListener

Если вы хотите, чтобы сервер/клиенты находились на разных компьютерах, все, что вам нужно сделать, это изменить свойства ServeURI в обоих проектах:

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

В WinForms можно найти другой похожий пример:
https://code.msdn.microsoft.com/windowsdesktop/Using-SignalR-in-WinForms-f1ec847b

Ответ 3

Вы можете отправлять данные клиенту, и это может быть достигнуто аналогично тому, как вы делаете это клиент → сервер (если только ваши сокеты не принимаются, если это так, чтобы их отправлять и получать). Для отправки Boolean требуется преобразование в байт, вы можете достичь этого с помощью класса BitConverter.

Ответ 4

На странице https://msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx см. метод AcceptCallback (он называется при подключении клиента). Есть строка Socket handler = listener.EndAccept(ar);. Это происходит для каждого клиента, когда он подключается, поэтому держите эти экземпляры Socket в каком-то списке. Когда вы хотите отправить данные клиентам, используйте метод Send из того же примера с каждым сокетом из списка (или выборочно, если вы хотите отправлять только некоторым клиентам).

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);
}