-->

TcpListener truncating byte array randomly

2019-08-24 01:44发布

问题:

I am writing what is essentially an image backup server to store images. It is a one way service that will not return anything beyond a basic success or failure message to the client.

The issue that I am experienceing is that when I send a byte array through the network stream, it is being cut-off before the end of the stream at random locations. I do not have this issue when I run the server on my development machine and connect locally, but rather it only occurs when the server is deployed on a remote server.

When I send very small arrays ( < 512 bytes) the server recieves the entire stream successfully, but on streams larger than 2000 bytes I experience issues. The code for the client is as follows:

    try
    {
        TcpClient Voice = new System.Net.Sockets.TcpClient();
        //Obviously I use the remote IP when it is deployed - but have altered it for privacy.
        IPEndPoint BackupServer = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 57000);

        Voice.Connect(BackupServer);
        NetworkStream DataStream = Voice.GetStream();

        byte[] buffer = new ASCIIEncoding().GetBytes(ImageData.GetXml());
        DataStream.Write(buffer, 0, buffer.Length);
        DataStream.Flush();
      }
      catch 
      {
      }
      try
      {
        buffer = new byte[4096];
        int read = DataStream.Read(buffer, 0, buffer.Length);
        MessageBox.Show(new ASCIIEncoding().GetString(buffer) + " : " + read.ToString());
      }
      catch
      {

      }

The client code executes without any errors or problems regardless of the size of data I send.

And the code for the server side is as follows:

  private void BackMeUp(object voice)
  {
    TcpClient Voice = (TcpClient)voice;
    Voice.ReceiveTimeout = 30000;
    NetworkStream DataStream = Voice.GetStream();
    try
    {            
      bool ShouldLoop = true;
      //int loops = 0;
      int loops = -1;
      byte[] input = new byte[2048];
      byte[] buffer = new byte[0];

      //while (ShouldLoop)
      while(loops != 0)
      {
        loops = DataStream.Read(input, 0, 2048);

        for (int x = 0; x < loops; x++)
        {                        
          Array.Resize(ref buffer, buffer.Length + 1);
          buffer[buffer.Length - 1] = input[x];                       
        }
        //if (loops < 2048)
        //{
          //ShouldLoop = false;
          //break;
        //}                    
      }

      while (true)
      {   
        StringReader Reader = new StringReader(new ASCIIEncoding().GetString(buffer, 0, buffer.Length));
        DataSet DS = new DataSet();
        DS.ReadXml(Reader);

        if (DS.Tables.Count > 0)
        {
          if (DS.Tables["Images"].Rows.Count > 0)
          {
            foreach (DataRow row in DS.Tables["Images"].Rows)
            {
                  //
            }
          }
        }

        string response = "Got it!";                    

        DataStream.Write(new ASCIIEncoding().GetBytes(response), 0, response.Length);
        DataStream.Flush();

        Voice.Close();
        break;                  
      }
    }
    catch (Exception Ex)
    {
      File.WriteAllText("Elog.txt", Ex.Message + " " + (Ex.InnerException != null ? Ex.InnerException.ToString() : " no Inner"));
      Voice.Close();
    }
  }

The server recieves the data fine, and closes the stream when it reaches the end, however the data is cut-off and I get an error when I try to rebuild the dataset.

I have the impression this has to do with the time it takes to send the stream, and I have played around with the Close and Flush commands but I feel like I'm just shooting in the dark. Any help would be appreciated.

Concise version of question: What factors are involved with a TcpListener that could cause a) the truncation of the stream. or b) premature closing of the stream prior to all bytes being read. When the listener in question is on a remote host rather than a local server.

回答1:

The Read method doesn't have to return the number of bytes that you requested, or the entire stream at once. Especially if the stream is slow, it will be returned in small chunks.

Call the Read method repeatedly, and handle the data for each block that you get. The Read method returns zero when the stream is read to the end:

buffer = new byte[4096];
do {
  int read = DataStream.Read(buffer, 0, buffer.Length);
  if (read != 0) {
    // handle the first "read" bytes of the buffer (index 0 to read-1)
  }
} while (read != 0);

If you know that your buffer is enough for any stream, you can fill up the buffer and handle it afterwards:

buffer = new byte[4096];
int offset = 0;
do {
  int read = DataStream.Read(buffer, offset, buffer.Length - offset);
  offset += read;
} while (read != 0);
// handle the first "offset" bytes of the buffer (index 0 to offset-1)