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

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

Данные: запись 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 машины, и я хочу, чтобы один из них дал сингл остальным из них, чтобы начать запись streamа rgb. Таким образом, сервер должен отправить сигнал клиентам для начала записи. Что делать, чтобы изменить поведение сервера для передачи данных, а не прослушивания. Возможно ли, чтобы несколько машин слушали и ожидали сигнала?

РЕДАКТИРОВАТЬ:

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

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

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

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

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

В: То, что я хочу достичь, – отправить логическую переменную с сервера клиенту с помощью сокетов

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

bool val = byteToCheck > 0;

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

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

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

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


сервер

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

     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. Поскольку сервер будет обслуживать множество клиентов, я рекомендую вам использовать 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 } 
  3. Определите 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 } } 
  4. Я, как правило, перечисляю свои клиентские сокеты и делаю что-то по утилизации клиентов (это не входит в список), но это зависит от необходимости. В этом случае вам это кажется нужным. И не забудьте создать буферы и т. Д. Это для буферизации входящих данных.

  5. Начните принимать что-то, полученное от клиента, используя другой 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 clientSockets = new List(); //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. Определите свой 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()); } } 
  7. И предположим, что вы хотите ответить отправителю после получения сообщения, просто сделайте это в части 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. И после того, как вы поместили немного больше вещей в свою основную рутину, вы закончите (!) - ЕСЛИ вы не просите отправить клиенту в качестве byte[]

  9. И теперь, если вы хотите отправить что-то всем своим клиентам в качестве byte[] вам просто нужно перечислить весь ваш клиент (см. Шаг 4-5). См. Это и преобразуйте приведенную выше string result (не забудьте ввести ее в формате шестнадцатеричной 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"); } 

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


Клиент:

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

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

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

  6. Вы можете делать все, что хотите, своими данными. Обратите внимание, что полученные вами данные фактически находятся в 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()); } } 
  7. И в самом последнем ... Да, опять же, как вы уже догадались, вам просто нужно что-то сделать в своей основной рутине - предположите, что вы хотите использовать его для отправки данных. Поскольку вы используете 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()); } } } } 

клиент

 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 ключевым словом static main void . И, таким образом, клиент Socket определенный выше, имеет static тип. Это может помешать определению Socket клиента несколько раз, поскольку каждый раз, когда он «определен», поскольку он имеет тот же class именем Program , он будет ссылаться на тот же Socket (хотя это может быть не всегда так, по крайней мере, согласно эксперимент OP: он может успешно запускать несколько клиентов на одном компьютере).

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

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


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

Почему бы вам не сделать вашу жизнь проще и использовать 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("SendMessage", (name, message) => Console.WriteLine(name + ":" + message)); Connection.Start().Wait(); Console.WriteLine("Press Enter to stop client"); Console.ReadLine(); } } } 

Server (запустите этот .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("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

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

На странице https://msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx см. Метод AcceptCallback (он 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); } 
Interesting Posts

Что такое хорошая встроенная firebase database для использования с C #?

Несколько версий Java, работающих одновременно под Windows

Что означает звездочка после имени файла при вводе `ls -l`?

Как я могу обрабатывать R CMD для проверки заметок «нет видимой привязки для глобальной переменной», когда разумный синтаксис ggplot2?

Отображение файлов (например, изображений), хранящихся на Google Диске, на веб-сайте

Как запустить базу данных H2 в режиме сервера?

Что именно делает маленькая зеленая кнопка «+» в Mac OS X?

Могу ли я сделать синхронный запрос с волейболом?

Найти записи разделов на физических дисках с помощью wmic

Как удалить оставшиеся папки Windows Update?

DSIM не работает. Работал один раз, но SFC терпел неудачу. Теперь DSIM не работает при восстановлении

Триггеры MySQL не могут обновлять строки в той же таблице, которой назначен триггер. Предлагаемое решение?

Windows 7 установит ошибку «Load Driver», ноутбук Lenovo

Почему .NET / C # не оптимизируется для рекурсии хвоста?

Невозможно загрузиться с диска, вставьте системный диск и нажмите ввод

Давайте будем гением компьютера.