.NET 6 WinForm发送32KB字节数组至REST API失败问题排查
在.NET 6 WinForm中发送32KB固件分块数据包失败的解决方法
问题描述
我尝试将固件文件以字节数组形式分块发送至REST API,JavaScript中可正常发送32KB数据包,但在.NET 6 WinForm中仅能发送2KB数据包,超过该大小就会失败。已查看Wireshark日志未发现异常,不清楚数据包去向。以下是报错信息及相关代码,求问如何在.NET中将数据包大小调整为32KB?
报错信息:
System.Net.WebException HResult=0x80131620 Message=An error occurred while sending the request. Source=System.Net.Requests Inner Exception 1: HttpRequestException: An error occurred while sending the request. Inner Exception 2: IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.. Inner Exception 3: SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
相关代码:
public void fnUpdateFirmware() { bool blnUpdateDone = false; #region Initialize FW Update Call // filePath = "D:\\Work\\BitBucket_Projects\\LMS_2.1.0\\Working\\lion-management-suite\\solutions\\LioN Management Suite V2\\src\\bin\\Debug\\firmwares\\935708001\\V11.1.4.16\\dig_light_8dido_mp.fwu"; Data = File.ReadAllBytes(filePath); data = "size=" + Data.Length; array1 = Encoding.ASCII.GetBytes(data); uri = new Uri(http + DeviceIp + fwupdate);//"http://192.168.1.10/fwup_install" httpSend.uri = uri; httpSend.DigestAuth = true; httpSend.method = "POST"; httpSend.username = UName;// "admin"; httpSend.password = Pwd;// "private"; httpSend.senddata = array1; httpSend.contentType = "text/plain"; //Start = false; fnUpdateCallAuthentication(ref httpSend); // textBox1.Text = "Device Initialize Successfully !"; #endregion #region Firmware Update //filePath = "D:\\Work\\BitBucket_Projects\\LMS_2.1.0\\Working\\lion-management-suite\\solutions\\LioN Management Suite V2\\src\\bin\\Debug\\firmwares\\935708001\\V11.1.4.16\\dig_light_8dido_mp.fwu"; Data = File.ReadAllBytes(filePath); int sendbytes = 0; int Var = 1024*2; int Len = Convert.ToInt32(Data.Length / Var); int Counter = 0; //HttpSendDataReq httpSend = new HttpSendDataReq(); while (Data.Length > sendbytes) { if (Counter < Len) { array1 = new byte[Var]; for (int i = 0; i < array1.Length; i++) { if (sendbytes < Data.Length) { array1[i] = Data[sendbytes]; sendbytes++; } else { break; } } Counter++; // textBox1.Text = Counter.ToString(); } else { array1 = new byte[Data.Length - (Var * Len)]; //textBox1.Text += "Remaining Array Length = " + array1.Length.ToString(); for (int i = 0; i < array1.Length; i++) { array1[i] = Data[sendbytes]; sendbytes++; } } uri = new Uri(http + DeviceIp + fwupdate); httpSend.uri = uri; httpSend.DigestAuth = true; httpSend.method = "POST"; httpSend.username = UName;// "admin"; httpSend.password = Pwd;// "private"; httpSend.senddata = array1; httpSend.contentType = "application/octet-stream"; // Start = false; fnUpdateCallAuthentication(ref httpSend); } //textBox1.Text += "\n Firmware file sent successfully !"; #endregion //return blnUpdateDone; } private string fnUpdateCallAuthentication(ref HttpSendDataReq sendData) { try { // AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false); // HttpWebRequest webRequest = (HttpWebRequest)base.GetWebRequest(uri); // HttpClient httpClient = Htt WebRequest myWebRequest = HttpWebRequest.Create(sendData.uri); HttpWebRequest myHttpWebRequest = (HttpWebRequest)myWebRequest; NetworkCredential myNetworkCredential = new NetworkCredential(sendData.username, sendData.password); CredentialCache myCredentialCache = new CredentialCache(); myCredentialCache.Add(sendData.uri, "Digest", myNetworkCredential); myHttpWebRequest.PreAuthenticate = true; myHttpWebRequest.Credentials = myCredentialCache; if (sendData.method.ToLower() == "post") { myHttpWebRequest.Method = sendData.method; myHttpWebRequest.KeepAlive = false; myHttpWebRequest.ContentType = sendData.contentType; if (null == sendData.senddata) { myHttpWebRequest.ContentLength = sendData.senddata.Length; StreamWriter requestWriter = new StreamWriter(myHttpWebRequest.GetRequestStream()); requestWriter.Write(sendData.sendraw); requestWriter.Close(); requestWriter = null; } else { Stream requestStream = myHttpWebRequest.GetRequestStream(); requestStream.Write(sendData.senddata, 0, sendData.senddata.Length); requestStream.Close(); } } using (WebResponse myWebResponse = myWebRequest.GetResponse()) { Stream responseStream = myWebResponse.GetResponseStream(); sendData.response = responseStream; StreamReader myStreamReader = new StreamReader(responseStream, Encoding.Default); string pageContent = myStreamReader.ReadToEnd(); sendData.resstring = pageContent; responseStream.Close(); myWebResponse.Close(); // Console.WriteLine(pageContent); if (pageContent.Contains("FWUP_STATE_RDY4RST")) { // textBox1.Text += pageContent; start = true; } return pageContent; } } catch (Exception e) { // textBox2.Text += e.Message; return null; } }
解决方案
1. 调整分块大小并修复分块逻辑
将分块大小从1024*2改为1024*32,同时修复原代码中计算分块数量的误差(原代码用整数除法会丢失余数部分的分块)。简化后的分块逻辑如下:
int chunkSize = 1024 * 32; // 设置为32KB分块 int totalChunks = (int)Math.Ceiling((double)Data.Length / chunkSize); int sendBytes = 0; for (int chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) { int currentChunkSize = Math.Min(chunkSize, Data.Length - sendBytes); byte[] chunk = new byte[currentChunkSize]; Array.Copy(Data, sendBytes, chunk, 0, currentChunkSize); sendBytes += currentChunkSize; // 发送当前分块 uri = new Uri(http + DeviceIp + fwupdate); httpSend.uri = uri; httpSend.DigestAuth = true; httpSend.method = "POST"; httpSend.username = UName; httpSend.password = Pwd; httpSend.senddata = chunk; httpSend.contentType = "application/octet-stream"; fnUpdateCallAuthentication(ref httpSend); }
2. 优化HttpWebRequest配置
原代码中HttpWebRequest的配置存在缺失,补充以下配置解决大请求超时或发送失败问题:
// 在fnUpdateCallAuthentication方法中添加 myHttpWebRequest.Timeout = 30000; // 设置30秒超时,按需调整 myHttpWebRequest.ReadWriteTimeout = 30000; myHttpWebRequest.AllowWriteStreamBuffering = false; // 禁用缓冲,直接发送数据 myHttpWebRequest.ContentLength = sendData.senddata.Length; // 显式设置ContentLength,原代码逻辑有误需修正
注意:原代码中if (null == sendData.senddata)分支存在空指针风险,需调整该分支逻辑,避免在senddata为null时访问其Length属性。
3. 改用HttpClient替代HttpWebRequest
HttpWebRequest是较旧的API,HttpClient在.NET 6+中更稳定且易于配置,适合大请求发送。示例代码如下:
// 建议全局单例初始化HttpClient private static readonly HttpClient _httpClient = new HttpClient(); private async Task<string> SendChunkAsync(byte[] chunk, Uri uri, string username, string password) { var credential = new NetworkCredential(username, password); var cache = new CredentialCache {{ uri, "Digest", credential }}; _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Digest", cache.GetCredential(uri, "Digest").ToString()); using var content = new ByteArrayContent(chunk); content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); var response = await _httpClient.PostAsync(uri, content); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); }
调用该方法发送32KB分块即可,HttpClient会自动处理连接和超时配置,减少手动出错概率。
4. 检查服务器端配置
对比Wireshark中JavaScript和.NET发送的请求,确保请求头、认证信息完全一致。部分服务器可能对不同客户端的请求有特殊限制,需确认服务器端是否允许.NET客户端发送32KB大小的请求。
内容的提问来源于stack exchange,提问作者Aditya Desai
相关产品推荐
相关产品推荐

