通过TcpClient的网络流写入大数据的最佳方法

时间:2016-07-01 08:21:51

标签: c# .net tcp tcpclient networkstream

我们要求将大型固件文件上传到打印机以升级设备的固件。打印机设备与我的服务器位于同一网络中,我们尝试上传的固件大小约为200 - 500 MB。我们选择的方法是将固件(.bin文件)加载到内存流中,然后使用TcpClient通过网络以块的形式写入。

根据网络流的响应,我们正在向客户端显示固件升级的状态。以下是我们用于固件升级的代码段。我想知道它是否是最佳方法,因为错误的方法可能会损坏设备。

修改

class MyClass
{
    int port = 9100;
    string _deviceip;
    byte[] m_ReadBuffer = null;
    TcpClient _tcpclient;
    NetworkStream m_NetworkStream;
    static string CRLF = "\r\n";

    public event EventHandler<DeviceStatus> onReceiveUpdate;

    public async Task<bool> UploadFirmware(Stream _stream)
    {
        bool success = false;
        try
        {
            _tcpclient = new TcpClient();
            _tcpclient.Connect(_deviceip, port);

            _stream.Seek(0, SeekOrigin.Begin);
            m_NetworkStream = _tcpclient.GetStream();
            byte[] buffer = new byte[1024];
            m_ReadBuffer = new byte[1024];
            int readcount = 0;
            m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length,
                                     new AsyncCallback(EndReceive), null);
            await Task.Run(() =>
            {
                while ((readcount = _stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    m_NetworkStream.Write(buffer, 0, readcount);
                    m_NetworkStream.Flush();
                }
            });
            success = true;
        }
        catch (Exception ex)
        {
            upgradeStatus = false;
        }
        return success;
    }

     private void EndReceive(IAsyncResult ar)
     {
         try
         {
             int nBytes;
             nBytes = m_NetworkStream.EndRead(ar);
             if (nBytes > 0)
             {
                 string res = Encoding.UTF8.GetString(m_ReadBuffer, 0, nBytes);
                 DeviceStatus status = new DeviceStatus();

                 string[] readlines = res.Split(new string[] { CRLF }, 
                              StringSplitOptions.RemoveEmptyEntries);
                 foreach (string readline in readlines)
                 {
                     if (readline.StartsWith("CODE"))
                     {
                         //read readline string here
                         break;
                     }
                 }
             }

            if (m_NetworkStream.CanRead)
            {
                do
                {
                    m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new 
                                           AsyncCallback(EndReceive), null);
                } while (m_NetworkStream.DataAvailable);
            }
         }
         catch (ObjectDisposedException ods)
         {
             return;
         }
         catch (System.IO.IOException ex)
         {
         }
     }
}

任何帮助都将非常感激。

2 个答案:

答案 0 :(得分:2)

您的代码基本上没有问题:

  1. m_NetworkStream.Flush(); AFAIK这没有任何作用。如果它做了某些事情会损害吞吐量。所以删除它。
  2. _stream.Seek(0, SeekOrigin.Begin);寻求是来电者的关注,删除它。这是一个分层违规。
  3. 使用更大的缓冲区。通过实验确定合适的尺寸。我通常从64KB开始进行批量转移。这使得IO不那么繁琐了。
  4. 打开唠叨,这有助于批量转移,因为它可以避免虚假的小包。
  5. 您可以使用Stream.Copy替换整个读写循环。
  6. 向呼叫者报告例外的方式隐藏了大量信息。只是让异常泡沫消失。不要归还布尔。
  7. 对所有资源使用using以确保在错误情况下清除它们。
  8. nBytes = m_NetworkStream.EndRead(ar);在这里,您假设单个读取将返回将要发送的所有数据。但是,您可能只收到第一个字节。可能,您应该在循环中使用StreamReader.ReadLine,直到您知道自己已完成为止。
  9. catch (System.IO.IOException ex) { }那是什么意思?如果固件更新是如此重要的事情,抑制错误似乎非常危险。你怎么能找到有关错误的内容?
  10. 我会将阅读代码转换为使用await

答案 1 :(得分:-2)

由于TcpPacket的最大长度为65535(2 ^ 16-1),如果发送任何超出此长度的数据包,它将被截断。如果我是你,我认为发送大数据包的最佳方法是设置每个数据包的Header并枚举它们。例如:
C-> S; [P1] <content>
然后是相同的结构,只加1 [P2] <content>
为此,只需使用少量子串截断数据并发送它们。

干杯!