2016-02-20 2 views
2

У меня есть 2 приложения, работающие на одной машине, которым нужно общаться. Один из них сделан с Unreal Engine, а другой - с настольным приложением C#.Повторное использование сокетов или создание нового каждый раз?

Принимая this (Unreal Engine) и this (C#) в качестве справочника, мне удалось успешно отправить сообщение с C# на UE.

Проблема в том, что я могу отправить только одно сообщение; во второй раз я не получаю ошибок, но сообщение не получено.

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

Если возможны оба варианта, существует ли какое-либо существенное преимущество/недостаток?

Как ссылка, в моем конкретном сценарии связь - это только один способ (C# - UE), а отправленные сообщения - всего несколько байтов каждые 10-60 секунд.

Любая помощь или комментарии оцениваются.

ответ

0

В code example on C# показано, что сокет должен использоваться для соединения, отправки и приема только один раз (и все выполняется синхронно, то есть вы не можете перейти к следующей строке, если вызов метода не завершен) перед закрытием:

sender.Connect(remoteEP); 

Console.WriteLine("Socket connected to {0}", 
    sender.RemoteEndPoint.ToString()); 

// Encode the data string into a byte array. 
byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>"); 

// Send the data through the socket. 
int bytesSent = sender.Send(msg); 

// Receive the response from the remote device. 
int bytesRec = sender.Receive(bytes); 
Console.WriteLine("Echoed test = {0}", 
    Encoding.ASCII.GetString(bytes,0,bytesRec)); 

// Release the socket. 
sender.Shutdown(SocketShutdown.Both); 
sender.Close(); 

Таким образом, если ваш TCP сокет слушателем в нереальном двигателя не закрывает текущее соединение, вы потеряете определенную сокет-соединение, которое было связано с ним, и, таким образом, не в силах идти дальше.

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

Итак, не просто используйте код примера, вам необходимо его значительно изменить.

Я рекомендую вам взглянуть на this post, содержащий код для создания как сервера, так и клиента для связи по протоколу TCP/IP с использованием класса Socket. В сообщении уже есть рабочее решение для тестирования с использованием приложения C# Console. И вы также можете увидеть, как Async (вместо Sync) используется для Connect и Receive.

Вот отрывок кода (просто изменить SERVER_IP и PORT_NO, чтобы соответствовать вашим, и, вероятно, вам нужно будет использовать только клиент, как Unreal Engine является сервером в вашем случае):

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

Сервер

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 http://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 http://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()); 
      } 
     } 
    } 
} 
+0

я изменил [код клиента] (https://msdn.microsoft.com/en-us/library/kb5kfec7 (v = vs.110) .aspx) для передачи и приема в цикле (синхронно), но я каждый раз создавал и уничтожал сокет.Слушатель Socket не закрывал соединения в Unreal, поэтому я терял связь, поэтому прошло только первое сообщение. Спасибо за ваш ответ. – jmcorallo

+1

@jmcorallo Я вижу ... рад, что мой ответ может быть полезен. И удачи для вашего проекта! ;) – Ian