// 接收通讯包 // 本函数支持 Pipeline 方式。 // parameters: // cache 用来支持 Pipeline 方式,把多于一个通讯包的 bytes 部分,存储起来,下次先处理这部分内容 // 如果为 null,表示不支持 Pipeline 方式 // nMaxLength 读入等待处理的 bytes 极限数字。超过了这个,还没有找到结束符,就会抛出异常。意在防范攻击。-1 表示不限制 public static async Task <RecvResult> SimpleRecvTcpPackage(TcpClient client, List <byte> cache, Delegate_isComplete procIsComplete, int nMaxLength = 4096) { // string strError = ""; RecvResult result = new RecvResult(); int recieved = 0; // 累计读取的 byte 数 int current = 0; // 本次读取的 byte 数 // bool bInitialLen = false; Debug.Assert(client != null, "client为空"); int CHUNK_SIZE = 4096; result.Package = new byte[CHUNK_SIZE]; recieved = 0; result.Length = CHUNK_SIZE; // 优先从 cache 中复制数据过来进行处理 if (cache != null && cache.Count > 0) { result.Package = EnlargeBuffer(cache.ToArray(), CHUNK_SIZE); result.Length = result.Package.Length; recieved = result.Length; cache.Clear(); } while (recieved < result.Length) { if (client == null) { return(new RecvResult { Value = -1, ErrorInfo = "通讯中断", ErrorCode = "abort" }); } try { current = await client.GetStream().ReadAsync(result.Package, recieved, result.Package.Length - recieved).ConfigureAwait(false); } catch (SocketException ex) { if (ex.ErrorCode == 10035) { System.Threading.Thread.Sleep(100); continue; } return(new RecvResult { Exception = ex, Value = -1, ErrorInfo = "recv出错1: " + ex.Message, // "ConnectionAborted" ErrorCode = ((SocketException)ex).SocketErrorCode.ToString() }); } catch (Exception ex) { result.Exception = ex; if (ex is IOException && ex.InnerException is SocketException) { // "ConnectionAborted" result.ErrorCode = ((SocketException)ex.InnerException).SocketErrorCode.ToString(); } result.ErrorInfo = "recv出错2: " + ex.Message; result.Value = -1; return(result); } if (current == 0) { return(new RecvResult { Value = -1, ErrorCode = "Closed", ErrorInfo = "Closed by remote peer" }); } // 得到包的长度 if (current >= 1 || recieved >= 1) { var ret = procIsComplete(result.Package, 0, recieved + current); if (ret.Item1 > 0) { result.Length = ret.Item1; result.Terminator = ret.Item2; // 将结束符后面多出来的部分复制到 cache 中,以便下一次调用处理 if (result.Length > recieved + current) // ?? bug { if (cache == null) { throw new Exception("当前不支持 Pipeline 方式的请求。发现前端一次性发送了多于一个通讯包"); } for (int i = result.Length; i < recieved + current; i++) { cache.Add(result.Package[i]); } } break; } } recieved += current; if (recieved >= result.Package.Length) { #if NO // 扩大缓冲区 byte[] temp = new byte[result.Package.Length + 4096]; Array.Copy(result.Package, 0, temp, 0, nInLen); result.Package = temp; result.Length = result.Package.Length; #endif if (nMaxLength != -1 && result.Package.Length >= nMaxLength) { throw new Exception("接收超过 " + nMaxLength + " bytes 也没有找到通讯包结束符"); } // 扩大缓冲区 result.Package = EnlargeBuffer(result.Package, CHUNK_SIZE); result.Length = result.Package.Length; } } // 最后规整缓冲区尺寸,如果必要的话 if (result.Package.Length > result.Length) { byte[] temp = new byte[result.Length]; Array.Copy(result.Package, 0, temp, 0, result.Length); result.Package = temp; } return(result); }
// (支持 Pipeline 的版本) // 接收通讯包 // 本函数支持 Pipeline 方式。 // parameters: // cache 用来支持 Pipeline 方式,把多于一个通讯包的 bytes 部分,存储起来,下次先处理这部分内容 // 如果为 null,表示不支持 Pipeline 方式 // nMaxLength 读入等待处理的 bytes 极限数字。超过了这个,还没有找到结束符,就会抛出异常。意在防范攻击。-1 表示不限制 public static async Task <RecvResult> SimpleRecvTcpPackage(TcpClient client, List <byte> cache, Delegate_isComplete procIsComplete, delegate_touch touch_func = null, int nMaxLength = 4096) { RecvResult result = new RecvResult(); Debug.Assert(client != null, "client为空"); List <byte> package = new List <byte>(); int CHUNK_SIZE = 4096; // 优先从 cache 中复制数据过来进行处理 if (cache != null && cache.Count > 0) { package = cache; cache.Clear(); } while (true) { if (client == null) { return(new RecvResult { Value = -1, ErrorInfo = "通讯中断", ErrorCode = "abort" }); } // byte[] temp = new byte[CHUNK_SIZE]; byte[] temp = ArrayPool <byte> .Shared.Rent(CHUNK_SIZE); int current = 0; try { current = await client.GetStream().ReadAsync(temp, 0, CHUNK_SIZE).ConfigureAwait(false); touch_func?.Invoke(); if (current > 0) { if (current == temp.Length) { package.AddRange(temp); } else { int i = 0; foreach (byte b in temp) { if (i >= current) { break; } package.Add(b); i++; } } // package.AddRange(GetValues<byte>(temp, current)); } } catch (SocketException ex) { if (ex.ErrorCode == 10035) { System.Threading.Thread.Sleep(100); continue; } return(new RecvResult { Value = -1, ErrorInfo = "recv出错1: " + ex.Message, // "ConnectionAborted" ErrorCode = ((SocketException)ex).SocketErrorCode.ToString() }); } catch (Exception ex) { result.Exception = ex; if (ex is IOException && ex.InnerException is SocketException) { // "ConnectionAborted" result.ErrorCode = ((SocketException)ex.InnerException).SocketErrorCode.ToString(); } result.ErrorInfo = "recv出错2: " + ex.Message; result.Value = -1; return(result); } finally { ArrayPool <byte> .Shared.Return(temp); } // 得到包的长度 if (package.Count >= 1) { var ret = procIsComplete(package.ToArray(), 0, package.Count); if (ret.Item1 > 0) { result.Length = ret.Item1; result.Terminator = ret.Item2; // 将结束符后面多出来的部分复制到 cache 中,以便下一次调用处理 if (result.Length < package.Count) { if (cache == null) { throw new Exception("当前不支持 Pipeline 方式的请求。发现前端一次性发送了多于一个通讯包"); } for (int i = result.Length; i < package.Count; i++) { cache.Add(package[i]); } } break; } } if (current == 0) { return(new RecvResult { Value = -1, ErrorCode = "Closed", ErrorInfo = "Closed by remote peer" }); } if (nMaxLength != -1 && package.Count >= nMaxLength) { throw new Exception("接收超过 " + nMaxLength + " bytes 也没有找到通讯包结束符"); } } // 最后规整缓冲区尺寸,如果必要的话 if (package.Count > result.Length) { package.RemoveRange(result.Length, package.Count - result.Length); } result.Package = package.ToArray(); return(result); }