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

解决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[i]);
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[i] = 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                 }
时间: 2024-12-18 09:47:38

解决Socket沾包问题——C#代码的相关文章

C#下利用封包、拆包原理解决Socket粘包、半包问题(新手篇)

介于网络上充斥着大量的含糊其辞的Socket初级教程,扰乱着新手的学习方向,我来扼要的教一下新手应该怎么合理的处理Socket这个玩意儿. 一般来说,教你C#下Socket编程的老师,很少会教你如何解决Socket粘包.半包问题. 更甚至,某些师德有问题的老师,根本就没跟你说过Socket的粘包.半包问题是什么玩意儿. 直到有一天,你的Socket程序在传输信息时出现了你预期之外的结果(多于的信息.不完整的信息.乱码.Bug等等). 任你喊了一万遍“我擦”,依旧是不知道问题出在哪儿! 好了,不说

Android开发--解决AndroidADT开发工具不能代码提示的问题

google android的新的开发工具,打开以后没有代码自动提示功能,下面对ADT工具的一些配置: 1.设置代码的字体 设置JAVA文件代码的字体:我这里设置的14 常规. 2.设置XML文件中代码的字体: 3.设置代码编辑器的背景颜色 色调85.饱和度90.亮度205 RGB:199.237.204 自定义: 4.设置代码提示功能: 快捷方式:Alt + /    可以出现代码提示. 默认的只有输入“ .” 以后才会有代码补全提示,可作如下设置: java->content Assist-

Window8.1 64位无法使用Debug命令的解决方法[附牛人代码]

偶然看到网上一篇文章,讲的是世界黑客编程大赛第一名的一个非常酷的程序,大小仅有4KB,使用debug命令运行. 悲催的是win8.1的debug命令不能使用. 错误如下: 解决方法如下: 1. 下载DOSBox 和Debug.exe 下载地址1:http://download.csdn.net/detail/ljgstudy/7557693(PS:需要1积分,积分多的大神高抬贵手哈~) 下载地址2:http://pan.baidu.com/s/1iwkGY(免积分) 2.安装DOSBox并启动,

我的Git教程 之 解决 git clone后无代码

解决 git clone 后无代码 前言:这个教程只适用于像我一样大致理解Git的原理,但是不太记得住Git命令的同学使用.所以具体原理只会提一下,具体可以参见Pro Git. 在另一篇 简明的教程 里提到获取Git库有两种方式,一种是直接在工作目录下创建一个新的Git库,另一种是从已有的库中克隆,即使用git clone. 其中使用第二种方法可能出现目录为空,即没有代码的现象. (1)分析原因 在Git Bash中,切换到目标目录.然后使用 $ ls -a 查看如果能看到.git目录,说明克隆

java.net.SocketException:Software caused connection abort: recv failed 异常分析 +socket客户端&amp;服务端代码

java.net.SocketException:Software caused connection abort: recv failed 异常分析 分类: 很多的技术 2012-01-04 12:54 8004人阅读 评论(6) 收藏 举报 socket服务器bufferstring网络java 第 1个异常是java.net.BindException:Address already in use: JVM_Bind.该异常发生在服务器端进行new ServerSocket(port)(p

Java Socket长连接示例代码

SocketListenerPusher.java代码如下: Java代码   import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import ja

解决如何使用Notepad++让代码高亮复制至word,网页问题

Notepad++的确是一个编写,注释,分析代码的好工具.但是当我决定复制自己的注释详尽的代码至博客上时,却遇到了一个非常让人郁闷的问题,就是在编辑器上清晰整洁的代码,发到博客上却变得极其混乱,不堪入目. 如何解决这个问题,notepad其实是有很好的解决方案的. 工具栏中: 插件--->NppExport--->Export to RTF  /  Export to HTML  / Copy RTF to Clipboard / Copy Html to Clipboard / Copy a

解决因为本地代码和远程代码冲突,导致git pull无法拉取远程代码的问题

一.问题 当本地代码和远程代码有冲突的时候,执行git pull操作的时候,会提示有冲突,然后直接终止本次pull,查了些资料没有找到强制pull的方式,但是可以使用如下方式解决. 二.解决思路 可以先将本地内容stash到仓库中,执行stash操作后,本地代码将返回到修改前的内容.这时,就可以正常将远程代码下载到本地了.然后再通过stash操作将仓库中的内容合到本地,如果有冲突就可以进行解决了. 三.操作命令 1.git stash 将本地代码stash到仓库中. 可以使用git stash

上传文件form表单enctype=&quot;multipart/form-data&quot;传值解决办法(代原代码)

最近做的一个项目里遇到一个问 题,就是如何在上传文件的表单里传递其它的变量,因为一但form表单用了enctype="multipart/form-data"类型后,所有的值 都是以二进制进行传递的,所以当我们想取出这个表单里传递过来的其它变量的时候,就会遇到一个问题,那就是用request取不到传递过来的变量.在网上 找了很多资料,也都是大至说一下,在这里我就借花献佛发个完整的原代码解决这个问题!!! 工程目录如下: 两个jar可以去apache上下载: http://commons