C# 通过TcpClient的网络流写入大数据的最佳方法
我们需要将大型固件文件上载到打印机,以升级设备的固件。打印机设备与我的服务器位于同一网络中,我们尝试上载的固件大小约为200-500 MB。我们选择的方法是将固件(.bin文件)加载到内存流中,并使用C# 通过TcpClient的网络流写入大数据的最佳方法,c#,.net,tcp,tcpclient,networkstream,C#,.net,Tcp,Tcpclient,Networkstream,我们需要将大型固件文件上载到打印机,以升级设备的固件。打印机设备与我的服务器位于同一网络中,我们尝试上载的固件大小约为200-500 MB。我们选择的方法是将固件(.bin文件)加载到内存流中,并使用TcpClient通过网络将其分块写入 根据来自网络流的响应,我们将向客户端显示固件升级的状态。下面是我们用于固件升级的代码片段。我想知道这是否是最好的方法,因为错误的方法可能会损坏设备 编辑: class MyClass { int port = 9100; string _dev
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)
{
}
}
}
class-MyClass
{
int端口=9100;
字符串设备IP;
字节[]m_ReadBuffer=null;
TCP客户_TCP客户;
NetworkStream m_NetworkStream;
静态字符串CRLF=“\r\n”;
公共事件处理程序onReceiveUpdate;
公共异步任务上载固件(流\u流)
{
布尔成功=假;
尝试
{
_tcpclient=新的tcpclient();
_tcpclient.Connect(_deviceip,端口);
_stream.Seek(0,SeekOrigin.Begin);
m_NetworkStream=_tcpclient.GetStream();
字节[]缓冲区=新字节[1024];
m_ReadBuffer=新字节[1024];
int readcount=0;
m_NetworkStream.BeginRead(m_ReadBuffer,0,m_ReadBuffer.Length,
新的异步回调(EndReceive),null);
等待任务。运行(()=>
{
而((readcount=_stream.Read(buffer,0,buffer.Length))>0)
{
m_NetworkStream.Write(缓冲区,0,读取计数);
m_NetworkStream.Flush();
}
});
成功=真实;
}
捕获(例外情况除外)
{
upgradeStatus=false;
}
回归成功;
}
专用void EndReceive(IAsyncResult ar)
{
尝试
{
整数字节;
n字节=m_NetworkStream.EndRead(ar);
如果(n字节>0)
{
stringres=Encoding.UTF8.GetString(m_ReadBuffer,0,n字节);
DeviceStatus状态=新的DeviceStatus();
string[]readlines=res.Split(新字符串[]{CRLF},
StringSplitOptions.RemoveEmptyEntries);
foreach(读取行中的字符串读取行)
{
if(readline.StartsWith(“代码”))
{
//在此处读取读取行字符串
打破
}
}
}
if(m_NetworkStream.CanRead)
{
做
{
m_NetworkStream.BeginRead(m_ReadBuffer,0,m_ReadBuffer.Length,新
AsyncCallback(EndReceive),null);
}while(m_NetworkStream.DataAvailable);
}
}
捕获(ObjectDisposedException ods)
{
返回;
}
捕获(System.IO.IOException-ex)
{
}
}
}
非常感谢您的帮助。因为TcpPacket的最大长度是65535(2^16-1),如果您发送的任何数据包超过此长度,它将被截断。如果我是你,我认为发送大数据包的最佳方式是为每个数据包设置一个报头并对其进行枚举。例如:
C->S<代码>[P1]
然后是相同的结构,加上1
[P2]
为此,只需使用几个子字符串来截断数据并发送它们
干杯 您的代码基本上没有问题,但有几个问题:
m_NetworkStream.Flush()代码>AFAIK这没有任何作用。如果它做了什么,它将损害吞吐量。所以删掉这个
\u stream.Seek(0,SeekOrigin.Begin)代码>查找是调用方关心的问题,请删除该问题。这是一种分层违规行为
Stream.Copy
替换整个读写循环Use
,以确保在错误情况下清除它们nBytes=m_NetworkStream.EndRead(ar)这里,您假设一次读取将返回所有即将到来的数据。不过,您可能只收到第一个字节。也许,您应该在循环中使用StreamReader.ReadLine
,直到您知道您已经完成了
catch(System.IO.IOException ex){}
这是怎么回事?如果固件更新是如此关键,那么抑制错误就显得非常危险。你还可以通过什么途径找到bugwait
我不会马上寄出去的。也许您没有注意到,我在while loop.TCP中以块的形式发送数据,但不公开数据包语义。这是一个连续的字节流,应用程序发送某些数据包不会出错,因为应用程序根本不发送数据包。非常感谢您提供如此详细的分析。然而,我对实施第八点感到困惑。你的意思是我应该在EndReceive方法中使用循环吗?如果您能详细说明这一点,我将不胜感激。我的想法更像这样:
Task.Run(()=>{while(true)var line=myStreamReader.ReadLine();if(IsEnd(line))break;else ProcessLine(line);})代码>。这就是我的想法,请看看你是否能为你做这件事。您可以删除所有t