2016-04-13 79 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); 
     } 
    } 

客户

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

你的意思是只对客户端?我现在不用我的电脑。只能通过手机访问。当然,如果你愿意,我可能会在明天发布。我可能会更新一些异步发送(而不是发送同步) – 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. 类似的概念,给你做的,你需要定义endConnectCallbackASyncBeginConnect使用服务器BeginAccept什么。但是在这里,不像服务器需要重新调用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[],因此您需要执行转换(请参阅linked post的服务器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.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);