[ヒール]
長い各TCPソケット接続は、独自のキャッシュバッファを持っている#1スティックパック現象は、デフォルトのサイズは手動設定をサポートすることができ、8Kです。長い棒のパッケージTCP接続は、図のように、最も一般的な現象です。
ソケットキャッシュが含ま5(5又はパッケージ)心拍数データ、ヘッダすなわちF0 AA 55 0F(16進数)、我々は、エンドデータへの各他端をキャッシュからヘッダデータの数で、5つのハートビート・パッケージが存在することを確認したが、5一緒になって結合をリンクされ、この共通のTCPバッファ現象は、我々はスティックパッケージを呼び出します。
#2 ## 2.1スティックパッケージ理由。同じクライアントは、継続的に(解析されたキャッシュが一掃される場合)、TCPサーバーが解決する機会があったときは、同じクライアントは、継続的に心拍データを送信する送信します。債券、同じキャッシュされたデータパケットを引き起こします。
ネットワークの輻輳が発生した時にスティック包装に起因する2.2。ネットワークの輻輳が、しばらくの間、突然ネットワークを滑らかに、TCPサーバはより多くの同じクライアントのハートビートパケットの受信##、複数のパケットは、サーバ側のTCPにキャッシュされています彼らは、接合しました。
過剰な、またはその他の理由の計算は、計算がTCPソケットのデータキャッシュ、ハートビートパケット(または他のメッセージ)を複数に対処するには遅すぎ、遅いため、サーバーはソケットバッファになりますとき## 2.3サーバが立ち往生エンドツーエンド、スティックパッケージ。
要約すると、複数のデータパケットは、TCPソケットバッファ、即ちスティックパッケージ現象を終了する同じ端現象です。
彼らは区別しない場合は#3害スティックパッケージによる客観のスティックパック現象の存在のために、我々は人為的にその区別のプログラムロジックでは、各パケットの付着をさせなければならない、次のような危険があります。## 3.1サーバーが正しくパケットは、無効なパッケージとして認識し、クライアントに伝えていきます、クライアントは、このようにクライアントサービス側の運転圧力を高め、再び報告され、計算自体の大量の場合、それはいくつかの異常現象ベンの崩壊になります解決できません。
## 3.2エラーエラーパケットが間違ったスティックパッケージには、成功したサーバーで解析することが起こった場合、それは間違っハンドラ・プロセスになり、偶然に解析されます。このエラー処理は3.1以上に危険にさらすだろう。
周波数が速すぎる場合、この現象が発生する3.3。無限ループを入力し、サーバーが無効なパケットとしてスティックパッケージを識別するために続けて##、クライアントは、CPUの使用率を消費するためには、報告され続けています。
要約すると、我々は、堅牢な例外処理メカニズムの基本的なソフトウェアシステムである接着されたTCPパケット処理、でなければなりません。
#スティックパッケージ## 4.1取り扱い4ロジック。、キャッシュソケットバイト全体を検索フレームトレーラー前記TCPパケット(例えば、4バイト)は、パケット特性パラメータの端当たり所定バイト数を区別するいつでもエンド・バイトの機能が、それは正しく分割スティックパッケージするために、パケットに分割されている場合、パケットが検出されました。前記各バイトは、パケットが非常に長いために適していない場合、ショートメッセージのために、低効率を検出する必要があります。
## 4.2 4.1は、同様の、より多くの特徴パラメータヘッダ検出部ヘッダートレーラーを区別する。前記短い及び長いパケットの最初のフレームのみを検出する各バイト、第二フレームヘッダ部検出
## 4.3。接着剤パッケージ(ソケットバッファ)から分割され、最初のフレームのパケットが読み出され、オフセット値のパケット長に応じてパケットの長さに応じてスティックパッケージを区別するには第二のフレームを見つけるために、最初のフレームのメッセージを修正パケット長、最終的に分割するために、第二のフレームを分割します。例:(0から数えて)にバイアスされた長さ5、すなわち、第6、第7バイトのパケット長バイト。
前記検出のみパケット長部、粘度の適切な長さのパケットにパケット。
#5パケット長コードはスティックパッケージ床を区別するために - ベースNewLife.Netパイプライン処理パイプラインアーキテクチャ## 5.1 NewLife.Net治療Newlife.Netパイプラインアーキテクチャ、参照は、Java網状のオープンソース・フレームワークに形成されています、その最もネッティーコーデックは、ここで使用することができます。コード内の特定の性能
_pemsServer.Add(new StickPackageSplit { Size = 2 });
来LengthCodecこのコーデックは、復号化に成功プッシュOnReceive方法をここで主に、復号化無し(の長さに応じて、有効な複数のパケットに粘度パケット)コーディングLengthCodec通過するすべてのメッセージにパイプラインに加えました。メッセージの長さが2バイトであることを示すサイズ= 2。## 5.2。おなじみ見つかったかどうか、パイプやWEBAPIプロジェクトのダクトHTTPネットコアとのアナロジーを追加しますか?
app.UseAuthentication();
app.UseRequestLog();
app.UseCors(_defaultCorsPolicyName);
app.UseMvc();
すなわち、パイプラインを介してパイプシーケンシャルデータフローを追加する配列。ただ、追求する必要はありませんでした第1のパイプライン処理機構ソケットである、またはパイプは、機構のHTTPコンテキストを扱います。しかし、真実は同じです。
## 5.3。被着体分割パケットデコーダ(長さに従って復号)### 5.3.1。オフセットアドレスの長さ位置のオフセット長さ属性を相殺しました。デフォルトは5、詳細な説明4.3です。
//
// 摘要:
// 长度所在位置
public int Offset
{
get;
set;
} = 5;
### 5.3.2。の長さは、サイズ特性は、本明細書で論じ長さ2バイトの数バイト、4.3参照
//
// 摘要:
// 长度占据字节数,1/2/4个字节,0表示压缩编码整数,默认2
public int Size
{
get;
set;
} = 2;
### 5.3.3。エンコード符号化方法
//
// 摘要:
// 编码,此应用不需要编码,只需解码,
// 按长度将粘包划分成多个数据包
//
// 参数:
// context:
//
// msg:
protected override object Encode(IHandlerContext context, Packet msg)
{
return msg;
}
ここでは何のコーディングませんので、直接返さMSG。
### 5.3.4。デコード復号化方法
//
// 摘要:
// 解码
//
// 参数:
// context:
//
// pk:
protected override IList<Packet> Decode(IHandlerContext context, Packet pk)
{
IExtend extend = context.Owner as IExtend;
LengthCodec packetCodec = extend["Codec"] as LengthCodec;
if (packetCodec == null)
{
IExtend extend2 = extend;
LengthCodec obj = new LengthCodec
{
Expire = Expire,
GetLength = ((Packet p) => MessageCodec<Packet>.GetLength(p, Offset, Size))
};
packetCodec = obj;
extend2["Codec"] = obj;
}
Console.WriteLine("报文解码前:{0}", BitConverter.ToString(pk.ToArray()));
IList<Packet> list = packetCodec.Parse(pk);
Console.WriteLine("报文解码");
foreach (var item in list)
{
Console.WriteLine("粘包处理结果:{0}", BitConverter.ToString(item.ToArray()));
}
return list;
}
#### 5.3.4.1ステップ1 - 復号レングスデコーダは、長さ復号が完了した後、オブジェクト・インスタンスをインスタンス化し、辞書に追加します。
IExtend extend2 = extend;
LengthCodec obj = new LengthCodec
{
Expire = Expire,
GetLength = ((Packet p) => MessageCodec<Packet>.GetLength(p, Offset, Size))
};
packetCodec = obj;
extend2["Codec"] = obj;
。#### 5.3.4.2ステップ2 - ステップは、読者が最終結果の増加を見ることができるようにするためには、この非必須を印刷デコードする前に、パケットをデコードします。
Console.WriteLine("报文解码前:{0}", BitConverteToString(pk.ToArray()));
。#### 5.3.4.3ステップ3 - デコーダがパケットを復号化
IList<Packet> list = packetCodec.Parse(pk);
次のようにコードをデコード:
//
// 摘要:
// 分析数据流,得到一帧数据
//
// 参数:
// pk:
// 待分析数据包
public virtual IList<Packet> Parse(Packet pk)
{
MemoryStream stream = Stream;
bool num = stream == null || stream.Position < 0 || stream.Position >= stream.Length;
List<Packet> list = new List<Packet>();
if (num)
{
if (pk == null)
{
return list.ToArray();
}
int i;
int num2;
for (i = 0; i < pk.Total; i += num2)
{
Packet packet = pk.Slice(i);
num2 = GetLength(packet);
Console.WriteLine(" pk. GetLength(packet):{0}", num2);
if (num2 <= 0 || num2 > packet.Total)
{
break;
}
packet.Set(packet.Data, packet.Offset, num2);
list.Add(packet);
}
if (i == pk.Total)
{
return list.ToArray();
}
pk = pk.Slice(i);
}
lock (this)
{
CheckCache();
stream = Stream;
if (pk != null && pk.Total > 0)
{
long position = stream.Position;
stream.Position = stream.Length;
pk.CopyTo(stream);
stream.Position = position;
}
while (stream.Position < stream.Length)
{
Packet packet2 = new Packet(stream);
int num3 = GetLength(packet2);
if (num3 <= 0 || num3 > packet2.Total)
{
break;
}
packet2.Set(packet2.Data, packet2.Offset, num3);
list.Add(packet2);
stream.Seek(num3, SeekOrigin.Current);
}
if (stream.Position >= stream.Length)
{
stream.SetLength(0L);
stream.Position = 0L;
}
return list;
}
}
次のようにコアコードをデコードすることである:すなわち、フレームごと方法を委任することにより、パケット長、GETLENGTH(パケット)を得、その後、すべてのパケットを通るループは、各フレームのパケット、及び最終的に返されるリストの長さに応じて分割されたリストを保存するために、パッケージスティック。リストの各要素は、メッセージ受信イベントをトリガします。
1人の関心の代表者の滞在を使用してください、コード6を参照してください委託。
for (i = 0; i < pk.Total; i += num2)
{
Packet packet = pk.Slice(i);
num2 = GetLength(packet);
Console.WriteLine(" pk. GetLength(packet):{0}", num2);
if (num2 <= 0 || num2 > packet.Total)
{
break;
}
packet.Set(packet.Data, packet.Offset, num2);
list.Add(packet);
}
#### 5.3.4.4。印刷処理結果スティックパッケージ
foreach (var item in list)
{
Console.WriteLine("粘包处理结果:{0}"BitConverter.ToString(item.ToArray()));
}
### NewLife.Netネットワークライブラリ呼び出し作ら5.3.5。空のスティッキーパケットエンコーダは、我々は気にしないでください。
//
// 摘要:
// 连接关闭时,清空粘包编码器
//
// 参数:
// context:
//
// reason:
public override bool Close(IHandlerContext contextstring reason)
{
IExtend extend = context.Owner as IExtend;
if (extend != null)
{
extend["Codec"] = null;
}
return base.Close(context, reason);
}
### 5.3.6。高解像度でのスティックパッケージコードデコーダ
// 摘要:
// 长度字段作为头部
//
public class StickPackageSplit : MessageCodec<Packet>
{
//
// 摘要:
// 长度所在位置
public int Offset
{
get;
set;
} = 5;
//
// 摘要:
// 长度占据字节数,1/2/4个字节,0表示压缩编码整数,默认2
public int Size
{
get;
set;
} = 2;
//
// 摘要:
// 过期时间,超过该时间后按废弃数据处理,默认500ms
public int Expire
{
get;
set;
} = 500;
//
// 摘要:
// 编码,此应用不需要编码,只需解码,
// 按长度将粘包划分成多个数据包
//
// 参数:
// context:
//
// msg:
protected override object Encode(IHandlerContext context, Packet msg)
{
return msg;
}
//
// 摘要:
// 解码
//
// 参数:
// context:
//
// pk:
protected override IList<Packet> Decode(IHandlerContext context, Packet pk)
{
IExtend extend = context.Owner as IExtend;
LengthCodec packetCodec = extend["Codec"] as LengthCodec;
if (packetCodec == null)
{
IExtend extend2 = extend;
LengthCodec obj = new LengthCodec
{
Expire = Expire,
GetLength = ((Packet p) => MessageCodec<Packet>.GetLength(p, Offset, Size))
};
packetCodec = obj;
extend2["Codec"] = obj;
}
Console.WriteLine("报文解码前:{0}", BitConverter.ToString(pk.ToArray()));
IList<Packet> list = packetCodec.Parse(pk);
Console.WriteLine("报文解码");
foreach (var item in list)
{
Console.WriteLine("粘包处理结果:{0}", BitConverter.ToString(item.ToArray()));
}
return list;
}
//
// 摘要:
// 连接关闭时,清空粘包编码器
//
// 参数:
// context:
//
// reason:
public override bool Close(IHandlerContext context, string reason)
{
IExtend extend = context.Owner as IExtend;
if (extend != null)
{
extend["Codec"] = null;
}
return base.Close(context, reason);
}
}
#6。GETLENGTH 5.3.6長さの計算手数料は、デリゲートを次のように各パケットの長さが計算されて呼び出します。デリゲートを説明するために、次のいずれかを使用しますについて、それが起動しません。
//
// 摘要:
// 从数据流中获取整帧数据长度
//
// 参数:
// pk:
//
// offset:
//
// size:
//
// 返回结果:
// 数据帧长度(包含头部长度位)
protected static int GetLength(Packet pk, int offsetint size)
{
if (offset < 0)
{
return pk.Total - pk.Offset;
}
int offset2 = pk.Offset;
if (offset >= pk.Total)
{
return 0;
}
int num = 0;
switch (size)
{
case 0:
{
MemoryStream stream = pk.GetStream();
if (offset > 0)
{
stream.Seek(offset, SeekOrigiCurrent);
}
num = stream.ReadEncodedInt();
num += (int)(stream.Position - offset);
break;
}
case 1:
num = pk[offset];
break;
case 2:
num = pk.ReadBytes(offset, 2).ToUInt16();
break;
case 4:
num = (int)pk.ReadBytes(offset, 4).ToUInt32;
break;
case -2:
num = pk.ReadBytes(offset, 2).ToUInt16(0isLittleEndian: false);
break;
case -4:
num = (int)pk.ReadBytes(offset, 4).ToUInt(0, isLittleEndian: false);
break;
default:
throw new NotSupportedException();
}
if (num > pk.Total)
{
return 0;
}
return num;
}
#7。最終スティックパッケージ分割レンダリング
免責事項:この記事はCC 4.0 BY-SAの著作権契約書に従って、ブロガーオリジナル記事です、複製、元のソースのリンクと、この文を添付してください。
このリンクします。https://www.cnblogs.com/JerryMouseLi/p/12659903.html