2016-11-25 93 views
0

我有一個關於通過TCP類發送和接收消息的順序的簡單問題,我找不到任何100%的答案,我的英文不夠好。TCPClient和TCPListener - NetworkStream - 消息順序

如果我有下面的例子:

服務器:

 IPAddress IP = IPAddress.Parse("127.0.0.1"); 
     int Port = 13000; 

     TcpListener Server = new TcpListener(IP, Port); 
     TcpClient Client = Server.AcceptTcpClient(); 
     NetworkStream Stream = Client.GetStream(); 

     Stream.Write(Buffer1, 0, 4); 
     //random time 
     Stream.Write(Buffer2, 0, 4); 
     //random time 
     Stream.Write(Buffer3, 0, 4); 

和客戶端:

 TCPClient Client = new TcpClient("127.0.0.1", 13000); 
     NetworkStream Stream = Client.GetStream(); 

     Stream.Read(A, 0, 4); 
     //random time 
     Stream.Read(B, 0, 4); 
     //random time 
     Stream.Read(C, 0, 4); 

是不是100%肯定,我將得到A = BUFFER1,B = BUFFER2, C =緩衝區3?

+0

不!您必須檢查'Stream.Read'的返回值以查看實際獲得的字節數。 TCP是*命令的*(意思是說,你可以保證按照你發送的順序接收字節),但也可以*基於數據流*(意思是不保證字節的分組)。爲了更加明確:TCP沒有消息。如果你想要消息,你必須自己構建它們(以長度爲前綴是最常見的方法)。查閱有關網絡代碼的任何教程,瞭解如何編寫正確的接收循環。 –

+0

我已經實現了這個前綴,我的消息的第一個4B是消息的長度,所以在另一方面,我讀取第一個4B,然後將NumberOfBytesToRead設置爲該數字進行另一次讀取。我只是想確認你可能在第一部分中回答的消息的順序:) –

+0

注意:如果你要求4個字節(它們可以跨包傳輸),那麼即使得到4個字節也不能保證,所以即使讀取長度必須在循環中完成,直到你有4個字節。然後你需要再次循環才能得到完整的信息。解決這個問題可能是網絡編程中最常見的錯誤,特別是迂迴,因爲這種代碼在意外或者測試環境中工作得很好,然後在生產中失敗。 –

回答

0

不能保證對於每個NetworkStream.Write操作,它將相應的NetworkStream.Read操作將返回已寫入到流中的確切數據。 這裏是TcpListner和TcpClient的連接的一個簡單的例子:

public class NetworkUtils{ 

    //Client 
    TcpClient client = null; 
    int port = 40555; 
    string serverIpAddress = "127.0.0.1"; 
    public Mutex mut = new Mutex(); 
    int byteToExpecting = 0; 
    int savedBufferOffset = 0; 
    Byte[] saveDataBuffer = new Byte[20000]; 
    NetworkStream stream; 

    public string ServerIpAddress 
    { 
     get { return serverIpAddress; } 
     set { serverIpAddress = value;} 
    } 

    string lastSentMsg = String.Empty; 
    public string LastSentMsg 
    { 
     get { return lastSentMsg; } 
     set { lastSentMsg = value;} 
    } 

    //Server 
    string clientMsg = String.Empty; 
    public string ClientMsg 
    { 
     get { return clientMsg; } 
    } 
    public void ClearClientMsg() 
    { 
     clientMsg = String.Empty; 
    } 

    TcpListener server=null; 

    private string errMsg = String.Empty; 
    public string ErrMsg 
    { 
     get { return errMsg; } 
     set { errMsg = value;} 
    } 

    void ConnectToServer() 
    { 
     client = new TcpClient(serverIpAddress, port); 
    } 

    public bool ClientSendMsg(string message) 
    { 
     try{ 

      ConnectToServer(); 

      Byte[] lengthByteArr = IntToByteArr(message.Length); 
      client.GetStream().Write(lengthByteArr, 0, lengthByteArr.Length); 

      Byte[] data = Encoding.ASCII.GetBytes(message); 
      client.GetStream().Write(data, 0, data.Length); 

      client.GetStream().Close(); 
     } 
     catch (Exception e) 
     { 
      errMsg = e.Message; 
     } 

     return errMsg.Length == 0; 
    } 

    public bool LaunchServer() { 
     try { 
      IPAddress localAddr = IPAddress.Parse("127.0.0.1"); 
      server = new TcpListener(localAddr, port); 
      server.Start(); 
      ListenToClients(); 
     } 
     catch(Exception e) 
     { 
      server.Stop(); 
     } 

     return errMsg.Length == 0; 
    } 

    void ProcessInformation(IAsyncResult result) 
    { 
     try{ 

      TcpClient client; 
      client = server.EndAcceptTcpClient(result); 
      stream = client.GetStream(); 
      stream.BeginRead(saveDataBuffer, 0, sizeof(Int32), new AsyncCallback(callbackGetHeadrer), null); 
      ListenToClients(); 
     } 
     catch(Exception e) 
     { 
      errMsg = e.Message; 
      server.Stop(); 
     } 
    } 

    void callbackGetHeadrer (IAsyncResult asyncResult) { 
     int lenToRead = stream.EndRead(asyncResult); 

     savedBufferOffset = 0; 
     byteToExpecting = ByteArrToInt (saveDataBuffer); 
     saveDataBuffer = new byte[byteToExpecting]; 
     stream.BeginRead (saveDataBuffer, 0, byteToExpecting, callback, null); 
    } 

    void callback (IAsyncResult asyncResult) { 

     int lenToRead = stream.EndRead(asyncResult); 

     byteToExpecting -= lenToRead; 
     savedBufferOffset += lenToRead; 

     /*No one is gurentee that the 'lenToRead' will be correspanding to NetworkStream.Write execution order. 
     We need to keep read from the stream until we will get waht we are expecting accrding 'byteToExpecting' 
     So here we are keep calling 'stream.BeginRead'.*/ 
     if (byteToExpecting > 0) { 
      stream.BeginRead (saveDataBuffer, savedBufferOffset, byteToExpecting, callback, null); 
     } 
     else{ 
      mut.WaitOne(); 
      clientMsg = System.Text.Encoding.ASCII.GetString(saveDataBuffer,0, saveDataBuffer.Length); 
      mut.ReleaseMutex(); 

      savedBufferOffset = 0; 
      stream.Close(); 
      client.Close(); 
     } 
    } 

    bool ListenToClients() 
    { 
     try{ 
      server.BeginAcceptTcpClient(new AsyncCallback(ProcessInformation), server); 
     } 
     catch(Exception e) 
     { 
      errMsg = e.Message; 
      server.Stop(); 
     } 

     return errMsg.Length == 0; 
    } 

    public Byte[] IntToByteArr(Int32 intValue) 
    { 
     byte[] intBytes = BitConverter.GetBytes(intValue); 

     if (BitConverter.IsLittleEndian) 
      Array.Reverse(intBytes); 
     return intBytes; 
    } 

    public Int32 ByteArrToInt(Byte[] intByteArr) 
    { 
     Int32 Int32_NUM_OF_BYTES = 4; 
     Byte[] buffer = new Byte[Int32_NUM_OF_BYTES]; 

     for (int i = 0; i < Int32_NUM_OF_BYTES; ++i) 
      buffer [i] = intByteArr [i]; 

     if (BitConverter.IsLittleEndian) 
      Array.Reverse (buffer); 

     return BitConverter.ToInt32 (buffer, 0); 
    } 
} 

注意「callbackGetHeadrer」負責讓我們將預期要接收的數據大小。之後,我們將繼續使用'stream.BeginRead'讀取流,直到我們將得到我們期待的內容,關於'stream.Write'操作的順序。

+1

是的,你說得對,我用了一些similiard。從.NET 4.5順便說一句,你可以使用異步等待。 NetworkStream有一些新的方法,比如'.ReadAsync'和'.WriteAsync',它們比舊的'.BeginRead' –

相關問題