国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C# - C#中TCP粘包問題的解決方法

C#中TCP粘包問題的解決方法

2022-01-17 12:43白云隨風 C#

這篇文章主要為大家詳細介紹了C#中TCP粘包問題的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下

一、tcp粘包產生的原理

1.tcp粘包是指發送方發送的若干包數據到接收方接收時粘成一包,從接收緩沖區看,后一包數據的頭緊接著前一包數據的尾。出現粘包現象的原因是多方面的,它既可能由發送方造成,也可能由接收方造成。

2.發送方引起的粘包是由tcp協議本身造成的,tcp為提高傳輸效率,發送方往往要收集到足夠多的數據后才發送一包數據。若連續幾次發送的數據都很少,通常tcp會根據優化算法把這些數據合成一包后一次發送出去,這樣接收方就收到了粘包數據。接收方引起的粘包是由于接收方用戶進程不及時接收數據,從而導致粘包現象。

3.這是因為接收方先把收到的數據放在系統接收緩沖區,用戶進程從該緩沖區取數據,若下一包數據到達時前一包數據尚未被用戶進程取走,則下一包數據放到系統接收緩沖區時就接到前一包數據之后,而用戶進程根據預先設定的緩沖區大小從系統接收緩沖區取數據,這樣就一次取到了多包數據。、

二、解決原理及代碼實現

1.采用包頭(固定長度,里面存著包體的長度,發送時動態獲取)+包體的傳輸機制。如圖

C#中TCP粘包問題的解決方法

headersize 存放著包體的長度,其headersize本身是定長4字節;

一個完整的數據包(l)=headersize+bodysize;

2.分包算法

  其基本思路是首先將待處理的接收數據流即系統緩沖區數據(長度設為m)強行轉換成預定的結構數據形式,并從中取出結構數據長度字段l,而后根據包頭計算得到第一包數據長度。

       m=系統緩沖區大小;l=用戶發送的數據包=headersize+bodysize;

1)若l<m,則表明數據流包含多包數據,從其頭部截取若干個字節存入臨時緩沖區,剩余部分數據依此繼續循環處理,直至結束。

C#中TCP粘包問題的解決方法

2)若l=m,則表明數據流內容恰好是一完整結構數據(即用戶自定義緩沖區等于系統接收緩沖區大小),直接將其存入臨時緩沖區即可。

C#中TCP粘包問題的解決方法

3)若l>m,則表明數據流內容尚不夠構成一完整結構數據,需留待與下一包數據合并后再行處理。

C#中TCP粘包問題的解決方法

4)下面是代碼代碼實現(hp-socket框架的服務器端來接收數據)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int headsize = 4;//包頭長度 固定4
  byte[] surplusbuffer = null;//不完整的數據包,即用戶自定義緩沖區
  /// <summary>
  /// 接收客戶端發來的數據
  /// </summary>
  /// <param name="connid">每個客戶的會話id</param>
  /// <param name="bytes">緩沖區數據</param>
  /// <returns></returns>
  private handleresult onreceive(intptr connid, byte[] bytes)
  {
   //bytes 為系統緩沖區數據
   //bytesread為系統緩沖區長度
   int bytesread = bytes.length;
   if (bytesread > 0)
   {
    if (surplusbuffer == null)//判斷是不是第一次接收,為空說是第一次
     surplusbuffer = bytes;//把系統緩沖區數據放在自定義緩沖區里面
    else
     surplusbuffer = surplusbuffer.concat(bytes).toarray();//拼接上一次剩余的包
    //已經完成讀取每個數據包長度
    int haveread = 0;
    //這里totallen的長度有可能大于緩沖區大小的(因為 這里的surplusbuffer 是系統緩沖區+不完整的數據包)
    int totallen = surplusbuffer.length;
    while (haveread <= totallen)
    {
     //如果在n此拆解后剩余的數據包連一個包頭的長度都不夠
     //說明是上次讀取n個完整數據包后,剩下的最后一個非完整的數據包
     if (totallen - haveread < headsize)
     {
      byte[] bytesub = new byte[totallen - haveread];
      //把剩下不夠一個完整的數據包存起來
      buffer.blockcopy(surplusbuffer, haveread, bytesub, 0, totallen - haveread);
      surplusbuffer = bytesub;
      totallen = 0;
      break;
     }
     //如果夠了一個完整包,則讀取包頭的數據
     byte[] headbyte = new byte[headsize];
     buffer.blockcopy(surplusbuffer, haveread, headbyte, 0, headsize);//從緩沖區里讀取包頭的字節
     int bodysize = bitconverter.toint32(headbyte, 0);//從包頭里面分析出包體的長度
 
     //這里的 haveread=等于n個數據包的長度 從0開始;0,1,2,3....n
     //如果自定義緩沖區拆解n個包后的長度 大于 總長度,說最后一段數據不夠一個完整的包了,拆出來保存
     if (haveread + headsize + bodysize > totallen)
     {
      byte[] bytesub = new byte[totallen - haveread];
      buffer.blockcopy(surplusbuffer, haveread, bytesub, 0, totallen - haveread);
      surplusbuffer = bytesub;
      break;
     }
     else
     {
      //挨個分解每個包,解析成實際文字
      string strc = encoding.utf8.getstring(surplusbuffer, haveread + headsize, bodysize);
      //addmsg(string.format(" > [onreceive] -> {0}", strc));
      //依次累加當前的數據包的長度
      haveread = haveread + headsize + bodysize;
      if (headsize + bodysize == bytesread)//如果當前接收的數據包長度正好等于緩沖區長度,則待拼接的不規則數據長度歸0
      {
       surplusbuffer = null;//設置空 回到原始狀態
       totallen = 0;//清0
      }
     }
    }
   }
   return handleresult.ok;
  }

值此完成拆包解析文字工作。但實際上還沒完成,如果這段代碼是客戶端接收來自服務器的數據的話就沒問題了。

仔細看intptr connid 每個連接的會話id

private handleresult onreceive(intptr connid, byte[] bytes)

{

}

但是服務器端還要分辨出 每個數據包是哪個會話產生的,因為服務器端是多線程,多用戶的模式,第一個數據包和第二個可能來自不同會話的數據,所以上面的代碼只適用于單會話模式。

下面我要解決這個問題。

采用c#安全的concurrentdictionary,具體參考

最新的代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//線程安全的字典
  concurrentdictionary<intptr, byte[]> dic = new concurrentdictionary<intptr, byte[]>();
  int headsize = 4;//包頭長度 固定4
  /// <summary>
  /// 接收客戶端發來的數據
  /// </summary>
  /// <param name="connid">每個客戶的會話id</param>
  /// <param name="bytes">緩沖區數據</param>
  /// <returns></returns>
  private handleresult onreceive(intptr connid, byte[] bytes)
  {
   //bytes 為系統緩沖區數據
   //bytesread為系統緩沖區長度
   int bytesread = bytes.length;
   if (bytesread > 0)
   {
    byte[] surplusbuffer = null;
    if (dic.trygetvalue(connid, out surplusbuffer))
    {
     byte[] curbuffer = surplusbuffer.concat(bytes).toarray();//拼接上一次剩余的包
     //更新會話id 的最新字節
     dic.tryupdate(connid, curbuffer, surplusbuffer);
     surplusbuffer = curbuffer;//同步
    }
    else
    {
     //添加會話id的bytes
     dic.tryadd(connid, bytes);
     surplusbuffer = bytes;//同步
    }
 
    //已經完成讀取每個數據包長度
    int haveread = 0;
    //這里totallen的長度有可能大于緩沖區大小的(因為 這里的surplusbuffer 是系統緩沖區+不完整的數據包)
    int totallen = surplusbuffer.length;
    while (haveread <= totallen)
    {
     //如果在n此拆解后剩余的數據包連一個包頭的長度都不夠
     //說明是上次讀取n個完整數據包后,剩下的最后一個非完整的數據包
     if (totallen - haveread < headsize)
     {
      byte[] bytesub = new byte[totallen - haveread];
      //把剩下不夠一個完整的數據包存起來
      buffer.blockcopy(surplusbuffer, haveread, bytesub, 0, totallen - haveread);
      dic.tryupdate(connid, bytesub, surplusbuffer);
      surplusbuffer = bytesub;
      totallen = 0;
      break;
     }
     //如果夠了一個完整包,則讀取包頭的數據
     byte[] headbyte = new byte[headsize];
     buffer.blockcopy(surplusbuffer, haveread, headbyte, 0, headsize);//從緩沖區里讀取包頭的字節
     int bodysize = bitconverter.toint32(headbyte, 0);//從包頭里面分析出包體的長度
 
     //這里的 haveread=等于n個數據包的長度 從0開始;0,1,2,3....n
     //如果自定義緩沖區拆解n個包后的長度 大于 總長度,說最后一段數據不夠一個完整的包了,拆出來保存
     if (haveread + headsize + bodysize > totallen)
     {
      byte[] bytesub = new byte[totallen - haveread];
      buffer.blockcopy(surplusbuffer, haveread, bytesub, 0, totallen - haveread);
      dic.tryupdate(connid, bytesub, surplusbuffer);
      surplusbuffer = bytesub;
      break;
     }
     else
     {
      //挨個分解每個包,解析成實際文字
      string strc = encoding.utf8.getstring(surplusbuffer, haveread + headsize, bodysize);
      addmsg(string.format(" > {0}[onreceive] -> {1}", connid, strc));
      //依次累加當前的數據包的長度
      haveread = haveread + headsize + bodysize;
      if (headsize + bodysize == bytesread)//如果當前接收的數據包長度正好等于緩沖區長度,則待拼接的不規則數據長度歸0
      {
       byte[] xbtye=null;
       dic.tryremove(connid, out xbtye);
       surplusbuffer = null;//設置空 回到原始狀態
       totallen = 0;//清0
      }
     }
    }
   }
   return handleresult.ok;
  }

這樣就解決了,多客戶端會話造成的接收混亂。至此所有工作完成。以上代碼就是為了參考學習,如果實在不想這么麻煩。可以直接使用hp-socket通信框架的pack模型,里面自動實現了解決粘包的問題。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

延伸 · 閱讀

精彩推薦
  • C#深入解析C#中的交錯數組與隱式類型的數組

    深入解析C#中的交錯數組與隱式類型的數組

    這篇文章主要介紹了深入解析C#中的交錯數組與隱式類型的數組,隱式類型的數組通常與匿名類型以及對象初始值設定項和集合初始值設定項一起使用,需要的...

    C#教程網6172021-11-09
  • C#C#裁剪,縮放,清晰度,水印處理操作示例

    C#裁剪,縮放,清晰度,水印處理操作示例

    這篇文章主要為大家詳細介紹了C#裁剪,縮放,清晰度,水印處理操作示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    吳 劍8332021-12-08
  • C#C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    這篇文章主要介紹了C# 實現對PPT文檔加密、解密及重置密碼的操作方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下...

    E-iceblue5012022-02-12
  • C#C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    這篇文章主要介紹了C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題,簡單描述了訪問者模式的定義并結合具體實例形式分析了C#使用訪問者模式解決長...

    GhostRider9502022-01-21
  • C#Unity3D實現虛擬按鈕控制人物移動效果

    Unity3D實現虛擬按鈕控制人物移動效果

    這篇文章主要為大家詳細介紹了Unity3D實現虛擬按鈕控制人物移動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一...

    shenqingyu060520232410972022-03-11
  • C#C#通過KD樹進行距離最近點的查找

    C#通過KD樹進行距離最近點的查找

    這篇文章主要為大家詳細介紹了C#通過KD樹進行距離最近點的查找,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    帆帆帆6112022-01-22
  • C#C#實現XML文件讀取

    C#實現XML文件讀取

    這篇文章主要為大家詳細介紹了C#實現XML文件讀取的相關代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Just_for_Myself6702022-02-22
  • C#WPF 自定義雷達圖開發實例教程

    WPF 自定義雷達圖開發實例教程

    這篇文章主要介紹了WPF 自定義雷達圖開發實例教程,本文介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下...

    WinterFish13112021-12-06
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 亚洲专区 中文字幕 | 午夜精品福利在线观看 | av在线黄| 日韩电影免费在线观看中文字幕 | 91久久国产综合久久91精品网站 | 日本一区二区高清视频 | 国产精品久久久 | 国产精品久久久久久久久久久久久久久久 | 成人爽a毛片一区二区免费 日韩av高清在线 | 爱爱免费视频网站 | 91免费观看视频 | 欧美精品在线一区二区三区 | 色在线视频网站 | 中文字幕三区 | 久久一二| 亚洲免费网站 | 欧美一级在线 | 国产精品久久久久久久久图文区 | 亚洲国产精品成人 | 亚洲电影一区二区 | 伊人干| 91精品国产综合久久久久 | 欧美自拍一区 | 国产精品毛片一区二区三区 | 欧美 日韩 综合 | 国产精品成人国产乱一区 | 久久中文免费 | 日韩免费视频 | 日韩免费高清视频 | 欧美日韩一区二区三区在线观看 | 骚虎av| 91在线网| 色爱区综合五月激情 | 成人午夜性成交 | 精品一区二区三区在线观看 | 久草在线 | 日韩成人免费 | 欧美日韩在线免费观看 | 成年人在线观看 | 精品99在线| 日韩在线成人 |