Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 699|回复: 0

解决Socket粘包问题——C#代码

[复制链接]
  • TA的每日心情
    奋斗
    2025-3-18 14:43
  • 签到天数: 805 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    73万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    731050
    发表于 2021-5-27 19:20:16 | 显示全部楼层 |阅读模式

    解决Socket粘包问题——C#代码

      前天晚上,曾经的一个同事问我socket发送消息如果太频繁,接收方就会有消息重叠,因为当时在外面,没有多加思考 第一反应还以为是多线程导致的数据不同步,让他加个线程锁搞定。后来回到家慢慢思考感觉这个和加锁没啥关系,如果是多线程导致的,消息只会被覆盖呀。后来就上网搜索socket 消息重叠,后来了解到这属于socket粘包。

    简单以自己的理解介绍下Socket粘包。

      Socket粘包简单说就是:Socket发送方,发送消息很频繁导致接收方接收到的消息是之前的两个或者多个消息连在一起。至于详细介绍可以百度相关资料了解。

     

    Socket粘包一般分为两种:

    1、Socket发送方:Socket发送消息时会将消息存放在一个缓冲区里,等待缓冲区满或者等待时间已到则发送出去。

    2、Socket接收方:接收方一般是因为没有及时从Socket缓冲区里取数据导致后来数据累加到一起才获取导致。

     

    在此只介绍发送方导致的

    发送方处理方法一般是两种

    1、NoDelay=true,NoDelay是否正在使用 Nagle 算法,这是socket提供的一个方法,我试了单独使用感觉效果不大,还有就是在发送后加延时,这样就不会造成粘包。

    2、分包方法,即发送消息的头4个字节加上消息长度,接受方接收到数据后先解析长度然后根据消息长度来获取消息。每次接收数据有个总长度 在总长度里循环遍历消息即可。

    直接上代码。

     

    分包方法发送端主要代码

     1            //测试要发送的消息
     2             string[] sendData = new string[] { "00", "11", "2222", "333333", "44444444", "我爱中国 中国爱我 哈哈哈 中国", "大中华你china"};
     3             int userKey = listConnect.SelectedIndex;
     4             if (userKey < 0)
     5             {
     6                 MessageBox.Show("请选择要发送者!");
     7             }
     8 
     9             else
    10             {
    11                 //为了测试循环发送消息
    12                 for (int i = 0; i < 7; i++)
    13                 {
    14 
    15                     //获取消息内容
    16                     byte[] msg = Encoding.UTF8.GetBytes(sendData);
    17 
    18                     byte[] array = new byte[msg.Length + 1 + 4];
    19 
    20                     //第一个字节代表消息类型 1:文字消息 2:文件
    21                     array[0] = 1;
    22                     //将消息长度写入4个字节 从第二个2个字节开始写入
    23                     ConvertIntToByteArray(msg.Length, ref array);
    24
    27                     //字节拷贝
    28                     Buffer.BlockCopy(msg, 0, array, 5, msg.Length);
    29                     //socket发送  dic存储的当前连接的socket列表 
    30                     dic[userKey].Send(array);
    31 
    32 
    33                 }
    34             }

     

    接收端消息部分代码

     1                //循环监听接收消息
     2                 while (flag)
     3                 {
     4                     byte[] bytes = new byte[1024 * 1024 * 2];
     5 
     6                     //消息总长度
     7                     int dataLength = 0;
     8                     dataLength = ServerSocket.receive(out bytes);
     9 
    10                     //判断消息类型
    11                     if (bytes[0] == 1)
    12                     {
    13 
    14                         //解决粘包问题代码
    15 
    16                         int msgBeginIndex = -1;
    17                         int msgEndIndex = -1;
    18                         int msgLength = 0;
    19                         int n = 0;
    20 
    21                         /**以下是接收数据包在byte[]中的内容
    22                          * 
    23                         0=1  代表消息类型
    24                         1=2  消息长度(消息长度占4个字节)
    25                         2=0  ..
    26                         3=0  ..
    27                         4=0  ..
    28                         5=48  消息内容
    29                         6=48  ..
    30                         以下内容和前面结构一样,粘包导致两个消息粘在一起
    31                         7=1   
    32                         8=2
    33                         9=0
    34                         10=0
    35                         11=0
    36                         12=49
    37                         13=49
    38                         ...
    39                         ******/
    40                         while (n < dataLength)
    41                         {
    42                             //将消息长度4个字节单独提取 
    43                             byte[] msgLengthByte = new byte[4];
    44 
    45                             for (int i = 0; i < 4; i++)
    46                             {
    47                                 //msgEndIndex:消息结束所在索引
    48                                 //第一个次消息长度可能是2;
    49                                 //第二个长度可能是8了索引这里要使用msgEndIndex;
    50                                 //最后+2+i:每个消息记录的长度都是从本次消息的第二个字节开始 直到第四个字节
    51 
    52                                 //之所以msgEndIndex默认=-1 消息长度所在位置在前一个消息结束后的第二个字节开始 ;
    53                                 //-1+2=1 这样保证获取第一个消息所在长度,也能保证后面的消息长度所在位置
    54                                 msgLengthByte = bytes[msgEndIndex + 2 + i];
    55                             }
    56                             //将消息长度转成int 
    57                             msgLength = BitConverter.ToInt32(msgLengthByte, 0);
    58 
    59                             //消息开始索引=消息结束索引+6(前面5个字节不是消息内容消息内容都是从第6个字节开始;+6是因为msgEndIndex索引是从-1开始)
    60                             msgBeginIndex = msgEndIndex + 6;
    61 
    62                             //消息结束索引位置=游标索引开始位置 +消息长度-1  因为消息开始索引本身就是消息开始 索引消息结束必须减除开始索引
    63                             msgEndIndex = msgBeginIndex + msgLength - 1;
    64 
    65                             //获取消息内容 
    66                             string receive = Encoding.UTF8.GetString(bytes, msgBeginIndex, msgLength);
    67                             if (receive != null)
    68                             {
    69                                 txtLog.BeginInvoke(new Action(() =>
    70                                 {
    71                                    
    72                                     txtLog.AppendText(DateTime.Now.ToString("HH:mm:ss") + " " + receive + " \r\n");
    73                                 }));
    74                             }
    75 
    76                          
    77 
    78                             n = msgEndIndex + 1;
    79                         }
    80                     }
    81 
    82 
    83                     //文件接收
    84                     else if (bytes[0] == 2)
    85                     {
    86                     }
    87                 }

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-4-21 07:05 , Processed in 0.076540 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表