2016-01-04 6 views
17

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

Данные: запись 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 и afterthat Звоню объект. Когда я щелкаю правой кнопкой мыши, я меняю флаг, и захватчик останавливается. Как я могу остановить сервер или сделать паузу, чтобы открыть его снова щелкнув левой кнопкой мыши?

+2

к моему знанию, вы можете, но метод, который я не знаю, посмотрите на это. постскриптум то, что я могу вспомнить, - это преобразовать данные, которые вы хотите отправить в байт [] - http://www.codeproject.com/Articles/2477/Multi-threaded-Client-Server-Socket-Class – KGCybeX

+1

извинения, только что понял, что это находится на C++, просто найдите сайт codeproject для передачи сетевого сервера клиента для C#, я использовал его с этого сайта, надеюсь, что это поможет – KGCybeX

+0

@ Спасибо за ваш супераналитический ответ. Я собираюсь попробовать все твои шаги. : P –

ответ

18

Ответы на вопросы первых:

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

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

Q: То, что я хочу добиться того, чтобы послать логическую переменную с сервера к клиенту с гнездами

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

bool val = byteToCheck > 0 ? true : false

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

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

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

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


сервера

  1. сделать ваш 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 
    } 
    
  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. Старт, чтобы принять что-то получил от клиента, с помощью другого ASyncBeginReceive на клиенте 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()); 
        } 
    } 
    
  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). См this и преобразовать resultstring выше (не забудьте ввести его в шестнадцатеричном формате 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. Аналогично, поставить 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 
    } 
    
  2. Тогда подключите к ASyncBeginConnect. Я бы обычно пошел дальше 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 для ASyncBeginConnect вы используете. Но здесь, , в отличие от сервера, которому необходимо перезвонить BeginAccept, после того, как вы подключены, вам не нужно делать никаких новых BeginConnect, так как вы должны только подключить один раз.

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

    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[]

enter image description here

enter image description here


Код для теста:

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

Последние замечания (Edit)

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

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

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


Продолжение этой темы можно найти here

+1

@JoseRamon Да! Я проверил его для вас. Это действительно из-за класса 'static'. Когда я создаю другой проект копирования, точно так же, но я изменил «пространство имен» на что-то еще, он отлично работает ... – Ian

+0

В клиентском коде, как я могу просто слушать сервер? Я попытался удалить данные, которые фактически отправляют данные. Однако я не могу распечатать данные, которые я только что передал со стороны сервера. –

+0

На самом деле я пытаюсь найти то, что я должен добавить в основную функцию сервера, чтобы отправить данные клиенту. В случае клиента сокет создается после этого loopConect, а затем отправляет, который фактически отправляет данные на сервер. Что из них следует добавить в основной функции на стороне сервера? Как отправляется работа? Он отправляет все порты, которые прослушивают данные?Возможно ли, чтобы клиент мог слушать и ждать данные? Должен ли я добавить привязку и прослушать код на стороне клиента? –

2

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

+0

Могу ли я сделать это для несколько клиентов? –

+1

Да. Каждый клиент должен иметь сокет. Если вы храните каждый клиент в какой-либо коллекции, всякий раз, когда вам нужно отправлять одни и те же данные всем клиентам (или только конкретным клиентам), вы можете перебирать эту коллекцию и отправлять необходимые данные. – Jaxter

+0

В настоящее время я сохраняю строку, которая является содержимым = \t "false " \t Как удалить символ EOF, чтобы разделить строку на логическую переменную? –

2

На странице 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); 
} 
+0

Я должен держать всех слушателей Socket в списке? Я немного смущен. –

+0

В общем, я был смущен для функций клиента и слушателя. Могу ли я иметь сервер, который просто отправляет данные и клиенты, которые прослушивают с сервера? –

+0

Не прослушиватели сокетов, но сокеты (экземпляры класса 'Socket'). Но, да, они вам нужны, они представляют ваших клиентов, поэтому отправка в сокет отправляется клиенту. Разница между слушателем и клиентом заключается в том, что у вас есть один слушатель, который ждет подключения от клиентов, но у вас много клиентов, каждый из которых представлен экземпляром Socket. – Shadowed

3

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

Client (Запустить .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(); 
     } 
    } 
} 

Server (Выполнить это.ехе перед запуском клиентов)

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

Client:
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

+0

Я попытался создать форму с самого начала. Это действительно сработало правильно, однако, когда я попытался добавить мою библиотеку, у меня возникли проблемы: исключение типа «System.IO.FileLoadException» произошло в mscorlib.dll, но не было обработано в коде пользователя. Дополнительная информация: не удалось загрузить файл или сборка 'FileHelpers, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = 3e0c08d59cc3d657' или одна из его зависимостей. Операция не поддерживается. (Исключение из HRESULT: 0x80131515) –

+0

Какой может быть конфликт в отношении библиотеки, которую я пытаюсь добавить? –

+0

Какую библиотеку вы пытаетесь добавить? Вы проверили эти: http://stackoverflow.com/questions/1869239/error-when-using-filehelpers-on-iis-sometimes-s-policyexception-required-p http://stackoverflow.com/questions/14483550/replacing-dll-to-a-newer-version http://serverfault.com/questions/93342/error-when-using-filehelpers-on-iis-sometimes-policyexception-required- perm –

Смежные вопросы