2016-04-13 4 views
4

У меня есть приложения для серверных и клиентских консолей, которые прекрасно обмениваются данными, а также отправляют некоторую строку. Вот код ...Как отправить файл через сокет в C#

Сервера

public static void Main() 
    { 
     try 
     { 
      IPAddress ipAd = IPAddress.Parse("127.0.0.1"); 

      /* Initializes the Listener */ 
      TcpListener myList = new TcpListener(ipAd, 1234); 

      /* Start Listeneting at the specified port */ 
      myList.Start(); 

      Console.WriteLine("The server is running at port 8001..."); 
      Console.WriteLine("The local End point is :" + myList.LocalEndpoint); 
      Console.WriteLine("Waiting for a connection....."); 

      Socket s = myList.AcceptSocket(); 
      Console.WriteLine("Connection accepted from " + s.RemoteEndPoint); 

      byte[] b = new byte[100]; 
      int k = s.Receive(b); 
      Console.WriteLine("Recieved..."); 
      for (int i = 0; i < k; i++) 
       Console.Write(Convert.ToChar(b[i])); 

      ASCIIEncoding asen = new ASCIIEncoding(); 
      s.Send(asen.GetBytes("The string was recieved by the server.")); 
      Console.WriteLine("\nSent Acknowledgement"); 
      /* clean up */ 
      s.Close(); 
      myList.Stop(); 

     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Error..... " + e.StackTrace); 
     } 
    } 

Client

public static void Main() 
    { 
     try 
     { 
      TcpClient tcpclnt = new TcpClient(); 
      Console.WriteLine("Connecting..."); 

      tcpclnt.Connect("127.0.0.1", 1234); 

      Console.WriteLine("Connected"); 
      Console.Write("Enter the string to be transmitted: "); 

      String str = Console.ReadLine(); 
      Stream stm = tcpclnt.GetStream(); 

      ASCIIEncoding asen = new ASCIIEncoding(); 
      byte[] ba = asen.GetBytes(str); 
      Console.WriteLine("Transmitting..."); 

      stm.Write(ba, 0, ba.Length); 

      byte[] bb = new byte[100]; 
      int k = stm.Read(bb, 0, 100); 

      for (int i = 0; i < k; i++) 
       Console.Write(Convert.ToChar(bb[i])); 

      tcpclnt.Close(); 
     } 

     catch (Exception e) 
     { 
      Console.WriteLine("Error... " + e.StackTrace); 
     } 
    } 

Теперь мне нужно добавить код алгоритм, который будет посылать файл через то же приложение. Ключевое значение для реализации - использовать Socket.Send в клиенте. Я не уверен в сериализации и де-сериализации файла.

Любой намек, совет, предложение более приветствуются. Благодарю.

+1

Подсказка: [возможно, связаны + некоторые пояснения] (http://stackoverflow.com/questions/34586733/sending-a-value-from-server-to-client-with-sockets/34669446 # 34669446) – Ian

+0

Вы имеете в виду только для Клиента? Сейчас я не с моим ПК. Доступ только через Mobile. Конечно, если хотите, я бы опубликовал его, возможно, завтра. Я, вероятно, немного обнов его с асинхронной отправкой (вместо отправки синхронизации) – Ian

ответ

2

В соответствии с просьбой, вы можете сделать следующее на стороне клиента - как это было предложено this post, за исключением того, что вы можете рассмотреть, чтобы изменить Синхронизирующую Send часть в асинхронный Send так:

clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async 

и ваш Обратный вызов может выглядеть следующим образом:

private static void endSendCallback(IAsyncResult ar) { 
    try { 
     SocketError errorCode; 
     int result = clientSocket.EndSend(ar, out errorCode); 
     Console.WriteLine(errorCode == SocketError.Success ? 
      "Successful! The size of the message sent was :" + result.ToString() : 
      "Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one 
     ); 
    } catch (Exception e) { //exception 
     Console.WriteLine("Unhandled EndSend Exception! " + e.ToString()); 
     //do something like retry or just report that the sending fails 
     //But since this is an exception, it probably best NOT to retry 
    } 
} 

А как полная сторона клиента коды после указанного изменения:

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.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async 
        //clientSocket.Send(bytes); use this for sync send 
       } 
      } while (result.ToLower().Trim() != "exit"); 
     } 

     private static void endSendCallback(IAsyncResult ar) { 
      try { 
       SocketError errorCode; 
       int result = clientSocket.EndSend(ar, out errorCode); 
       Console.WriteLine(errorCode == SocketError.Success ? 
        "Successful! The size of the message sent was :" + result.ToString() : 
        "Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one 
       ); 
      } catch (Exception e) { //exception 
       Console.WriteLine("Unhandled EndSend Exception! " + e.ToString()); 
       //do something like retry or just report that the sending fails 
       //But since this is an exception, it probably best NOT to retry 
      } 
     } 

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

Полное объяснение того, как вышеуказанные фрагменты кода работы (обратите внимание, что пункт 7 изменяется и точка 8 добавляется для асинхронном Send):

Клиент:

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

    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.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async 
          //clientSocket.Send(bytes); use this for sync send 
         } 
        } while (result.ToLower().Trim() != "exit"); 
    } 
    
  8. И, наконец, в самом самый последний, вам нужно всего лишь объявить функцию обратного вызова асинхронной EndSend и вы сделали!

    private static void endSendCallback(IAsyncResult ar) { 
        try { 
         SocketError errorCode; 
         int result = clientSocket.EndSend(ar, out errorCode); 
         Console.WriteLine(errorCode == SocketError.Success ? 
          "Successful! The size of the message sent was :" + result.ToString() : 
          "Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one 
         ); 
        } catch (Exception e) { //exception 
         Console.WriteLine("Unhandled EndSend Exception! " + e.ToString()); 
         //do something like retry or just report that the sending fails 
         //But since this is an exception, it probably best NOT to retry 
        } 
    } 
    
1

Вам просто нужно прочитать файл в виде массива байтов, а затем отправить этот массив байтов по проводам

var bytes = File.ReadAllBytes(path); 
stm.Write(ba, 0, ba.Length); 
1

File.ReadAllBytes(string path) поможет вам массив байтов файла.
File.WriteAllBytes(string path, byte[] bytes) будет записывать его на диск.
Чтобы помочь различать файл/команду/статус/etc. содержимое сообщения вы могли бы добавить заголовок байта спереди. Enum: байт может пригодиться здесь.

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