TCPClient 和 TCPListener - NetworkStream - 消息顺序

TCPClient and TCPListener - NetworkStream - order of messages

我有一个关于通过 TCP 发送和接收消息的顺序的简单问题 类,我找不到任何 100% 的答案,我的英语也不够好。

如果我有以下例子:

服务器:

        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 = Buffer3?

无法保证每个 NetworkStream.Write 操作都会对应 NetworkStream.Read 操作,这些操作将 return 写入流的确切数据。 下面是一个简单的 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' 操作的顺序。