本文转载自:
http://www.cnblogs.com/ttlin2010yeah/archive/2010/11/17/1879958.htmlC# Socket通信三大问题是什么呢?让我们开始讲述:
C# Socket通信三大问题之数据包界限符问题。
根据原项目中交通部标准,在连续观测站中数据包中,使用﹤﹥两个字符表示有效数据包开始和结束。实际项目有各自的具体技术规范
C# Socket通信三大问题之数据包不连续问题。
在TCP/IP等通信中,由于时延等原因,一个数据包被Socket做两次或多次接收,此时在接收第一个包后,必须保存到TSession的DatagramBuffer中,在以后一并处理
C# Socket通信三大问题包并发与重叠问题。
由于客户端发送过快或设备故障等原因,一次接收到一个半、两个或多个包文。此时,也需要处理、一个半、两个或多个包
先补充异步BeginReceive()回调函数EndReceiveData()中的数据包分合函数ResolveBuffer()。
下面是C# Socket通信三大问题的实例演示:
01
/// ﹤summary﹥
02
/// 1) 报文界限字符为﹤﹥,其它为合法字符,
03
/// 2) 按报文头、界限标志抽取报文,可能合并包文
04
/// 3) 如果一次收完数据,此时 DatagramBuffer 为空
05
/// 4) 否则转存到包文缓冲区 session.DatagramBuffer
06
/// ﹤/summary﹥
07
private
void
ResolveBuffer(TSession session,
int
receivedSize)
08
{
09
// 上次留下的报文缓冲区非空(注意:必然含有开始字符 ﹤,空时不含 ﹤)
10
bool
hasBeginChar = (session.DatagramBufferLength ﹥ 0);
11
12
int
packPos = 0;
// ReceiveBuffer 缓冲区中包的开始位置
13
int
packLen = 0;
// 已经解析的接收缓冲区大小
14
15
byte
dataByte = 0;
// 缓冲区字节
16
int
subIndex = 0;
// 缓冲区下标
17
18
while
(subIndex ﹤ receivedSize)
19
{
20
// 接收缓冲区数据,要与报文缓冲区 session.DatagramBuffer 同时考虑
21
dataByte = session.ReceiveBuffer[subIndex];
22
23
if
(dataByte == TDatagram.BeginChar)
// 是数据包的开始字符﹤,则前面的包文均要放弃
24
{
25
// ﹤前面有非空串(包括报文缓冲区),则前面是错包文,防止 AAA﹤A,1,A﹥ 两个报文一次读现象
26
if
(packLen ﹥ 0)
27
{
28
Interlocked.Increment(
ref
_datagramCount);
// 前面有非空字符
29
Interlocked.Increment(
ref
_errorDatagramCount);
// 一个错误包
30
this
.OnDatagramError();
31
}
32
session.ClearDatagramBuffer();
// 清空会话缓冲区,开始一个新包
33
34
packPos = subIndex;
// 新包起点,即﹤所在位置
35
packLen = 1;
// 新包的长度(即﹤)
36
hasBeginChar =
true
;
// 新包有开始字符
37
}
38
else
if
(dataByte == TDatagram.EndChar)
// 数据包的结束字符 ﹥
39
{
40
if
(hasBeginChar)
// 两个缓冲区中有开始字符﹤
41
{
42
++packLen;
// 长度包括结束字符﹥
43
44
// ﹥前面的为正确格式的包,则分析该包,并准备加入包队列
45
AnalyzeOneDatagram(session, packPos, packLen);
46
47
packPos = subIndex + 1;
// 新包起点。注意:subIndex 在循环最后处 + 1
48
packLen = 0;
// 新包长度
49
}
50
else
// ﹥前面没有开始字符,则认为结束字符﹥为一般字符,待后续的错误包处理
51
{
52
++packLen;
// hasBeginChar = false;
53
}
54
}
55
else
// 非界限字符﹤﹥,就是是一般字符,长度 + 1,待解析包处理
56
{
57
++packLen;
58
}
59
++subIndex;
// 增加下标号
60
}
// end while
61
62
if
(packLen ﹥ 0)
// 剩下的待处理串,分两种情况
63
{
64
// 剩下包文,已经包含首字符且不超长,转存到包文缓冲区中,待下次处理
65
if
(hasBeginChar && packLen +
66
session.DatagramBufferLength ﹤= _maxDatagramSize)
67
{
68
session.CopyToDatagramBuffer(packPos, packLen);
69
}
70
else
// 不含首字符,或超长
71
{
72
Interlocked.Increment(
ref
_datagramCount);
73
Interlocked.Increment(
ref
_errorDatagramCount);
74
75
this
.OnDatagramError();
76
session.ClearDatagramBuffer();
// 丢弃全部数据
77
}
78
}
79
}
C# Socket通信三大问题之分析包文AnalyzeOneDatagram()函数代码补充如下:
01
/// ﹤summary﹥
02
/// 具有﹤﹥格式的数据包加入到队列中
03
/// ﹤/summary﹥
04
private
void
AnalyzeOneDatagram(
05
TSession session,
int
packPos,
int
packLen)
06
{
07
if
(packLen + session.DatagramBufferLength ﹥ _maxDatagramSize)
08
// 超过长度限制
09
{
10
Interlocked.Increment(
ref
_datagramCount);
11
Interlocked.Increment(
ref
_errorDatagramCount);
12
this
.OnDatagramError();
13
}
14
else
// 一个首尾字符相符的包,此时需要判断其类型
15
{
16
Interlocked.Increment(
ref
_datagramCount);
17
TDatagram datagram =
new
TDatagram();
18
19
if
(!datagram.CheckDatagramKind())
20
// 包格式错误(只能是短期BG、或长期SG包)
21
{
22
Interlocked.Increment(
ref
_datagramCount);
23
Interlocked.Increment(
ref
_errorDatagramCount);
24
this
.OnDatagramError();
25
datagram =
null
;
// 丢弃当前包
26
}
27
else
// 实时包、定期包,先解析数据,判断正误,并发回确认包
28
{
29
datagram.ResolveDatagram();
30
if
(
true
)
// 正确的包才入包队列
31
{
32
Interlocked.Increment(
ref
_datagramQueueCount);
33
lock
(_datagramQueue)
34
{
35
_datagramQueue.Enqueue(datagram);
// 数据包入队列
36
}
37
}
38
else
39
{
40
Interlocked.Increment(
ref
_errorDatagramCount);
41
this
.OnDatagramError();
42
}
43
}
44
}
45
session.ClearDatagramBuffer();
// 清包文缓冲区
46
}
C# Socket通信三大问题之TSession的拷贝转存数据包文的方法CopyToDatagramBuffer()代码如下:
01
/// ﹤summary﹥
02
/// 拷贝接收缓冲区的数据到数据缓冲区(即多次读一个包文)
03
/// ﹤/summary﹥
04
public
void
CopyToDatagramBuffer(
int
startPos,
int
packLen)
05
{
06
int
datagramLen = 0;
07
if
(DatagramBuffer !=
null
) datagramLen =
08
DatagramBuffer.Length;
09
10
// 调整长度(DataBuffer 为 null 不会出错)
11
Array.Resize(
ref
DatagramBuffer,
12
datagramLen + packLen);
13
14
// 拷贝到数据就缓冲区
15
Array.Copy(ReceiveBuffer, startPos,
16
DatagramBuffer, datagramLen, packLen);
17
}
代码中注释比较详细了,下面指出C# Socket通信三大问题实例开发思路:
使用TSession会话对象的字节数组ReceiveBuffer保存BeginReceiver()接收到的数据,使用字节数组DatagramBuffer保存一次接收后分解或合并的剩下的包文。本项目中,由于是5分钟一个包,正常情况下不需要用到DatagramBuffer数组
处理ReceiveBuffer中的字节数据包时,先考虑DatagramBuffer是否有开始字符﹤。如果有,则当前包文是前个包文的补充,否则前个包文是错误的。正确的包文可能存在于两个缓冲区中,见分析函数AnalyzeOneDatagram()
分析完接收数据包后,剩下的转存到DatagramBuffer中,见函数CopyToDatagramBuffer()
设计时考虑的另一个重要问题就是处理速度。如果自动观测站达到100个,此时5*60=300秒钟就有100个包,即每3秒种一个包,不存在处理速度慢问题。但是,真正耗时的是判断包是否重复!特别地,当设备故障时存在混乱上传数据包现象,此时将存在大量的重复包。笔者采用了所谓的区间判重算法,较好地解决了判重速度问题,使得系统具有很好的可伸缩性(分析算法的论文被EI核心版收录,呵呵,意外收获)。事实上,前年的交通部接收服务器还不具备该项功能,可能是太费时间了。
还有,就是在.NET Framework的托管CLR下,系统本身的响应速度如何?当时的确没有把握,认为只要稳定性和速度满足要求就行了。三年半运行情况表明,系统有良好的处理速度、很好的稳定性、满足了部省要求。
C# Socket通信三大问题的基本内容就向你介绍到这里了,希望对你了解和学习C# Socket通信三大问题有所帮助。