C#Socket通信三⼤问题详解
C# Socket通信三⼤问题是什么呢?让我们开始讲述:
C# Socket通信三⼤问题之数据包界限符问题。
根据原项⽬中交通部标准,在连续观测站中数据包中,使⽤﹤﹥两个字符表⽰有效数据包开始和结束。实际项⽬有各⾃的具体技术规范C# Socket通信三⼤问题之数据包不连续问题。
在TCP/IP等通信中,由于时延等原因,⼀个数据包被Socket做两次或多次接收,此时在接收第⼀个包后,必须保存到TSession的DatagramBuffer中,在以后⼀并处理
C# Socket通信三⼤问题包并发与重叠问题。
由于客户端发送过快或设备故障等原因,⼀次接收到⼀个半、两个或多个包⽂。此时,也需要处理、⼀个半、两个或多个包
先补充异步BeginReceive()回调函数EndReceiveData()中的数据包分合函数ResolveBuffer()。
下⾯是C# Socket通信三⼤问题的实例演⽰:
/
// ﹤summary﹥
/// 1) 报⽂界限字符为﹤﹥,其它为合法字符,
/// 2) 按报⽂头、界限标志抽取报⽂,可能合并包⽂
/// 3) 如果⼀次收完数据,此时 DatagramBuffer 为空
/// 4) 否则转存到包⽂缓冲区 session.DatagramBuffer
/// ﹤/summary﹥
private void ResolveBuffer(TSession session, int receivedSize)
{
// 上次留下的报⽂缓冲区⾮空(注意:必然含有开始字符﹤,空时不含﹤)
bool hasBeginChar = (session.DatagramBufferLength ﹥ 0);
int packPos = 0;  // ReceiveBuffer 缓冲区中包的开始位置
int packLen = 0;  // 已经解析的接收缓冲区⼤⼩
byte dataByte = 0;  // 缓冲区字节
int subIndex = 0;  // 缓冲区下标
while (subIndex ﹤ receivedSize)
{
// 接收缓冲区数据,要与报⽂缓冲区 session.DatagramBuffer 同时考虑
dataByte = session.ReceiveBuffer[subIndex];
if (dataByte == TDatagram.BeginChar) // 是数据包的开始字符﹤,则前⾯的包⽂均要放弃
{
// ﹤前⾯有⾮空串(包括报⽂缓冲区),则前⾯是错包⽂,防⽌ AAA﹤A,1,A﹥两个报⽂⼀次读现象
if (packLen ﹥ 0)
{
Interlocked.Increment(ref _datagramCount);  // 前⾯有⾮空字符
Interlocked.Increment(ref _errorDatagramCount);  // ⼀个错误包
this.OnDatagramError();
}
session.ClearDatagramBuffer();  // 清空会话缓冲区,开始⼀个新包
packPos = subIndex;  // 新包起点,即﹤所在位置
packLen = 1;// 新包的长度(即﹤)
hasBeginChar = true;  // 新包有开始字符
}
else if (dataByte == TDatagram.EndChar)  // 数据包的结束字符﹥
{
if (hasBeginChar)  // 两个缓冲区中有开始字符﹤
{
++packLen;  // 长度包括结束字符﹥
// ﹥前⾯的为正确格式的包,则分析该包,并准备加⼊包队列
AnalyzeOneDatagram(session, packPos, packLen);
packPos = subIndex + 1;  // 新包起点。注意:subIndex 在循环最后处 + 1
packLen = 0;  // 新包长度
}
else  // ﹥前⾯没有开始字符,则认为结束字符﹥为⼀般字符,待后续的错误包处理
{
++packLen;  //  hasBeginChar = false;
}
}
else  // ⾮界限字符﹤﹥,就是是⼀般字符,长度 + 1,待解析包处理
{
++packLen;
}
++subIndex;  // 增加下标号
}  // end while
if (packLen ﹥ 0)  // 剩下的待处理串,分两种情况
{
/
/ 剩下包⽂,已经包含⾸字符且不超长,转存到包⽂缓冲区中,待下次处理
if (hasBeginChar && packLen +
session.DatagramBufferLength ﹤= _maxDatagramSize)
{
session.CopyToDatagramBuffer(packPos, packLen);
}
else  // 不含⾸字符,或超长
{
Interlocked.Increment(ref _datagramCount);
Interlocked.Increment(ref _errorDatagramCount);
this.OnDatagramError();
session.ClearDatagramBuffer();  // 丢弃全部数据
}
}
}
C# Socket通信三⼤问题之分析包⽂AnalyzeOneDatagram()函数代码补充如下:
/// ﹤summary﹥
/// 具有﹤﹥格式的数据包加⼊到队列中
/// ﹤/summary﹥
private void AnalyzeOneDatagram(
TSession session, int packPos, int packLen)
{
if (packLen + session.DatagramBufferLength ﹥ _maxDatagramSize)
// 超过长度限制
{
Interlocked.Increment(ref _datagramCount);
Interlocked.Increment(ref _errorDatagramCount);
this.OnDatagramError();
}socket通信报文格式
else // ⼀个⾸尾字符相符的包,此时需要判断其类型
{
Interlocked.Increment(ref _datagramCount);
TDatagram datagram = new TDatagram();
if (!datagram.CheckDatagramKind())
// 包格式错误(只能是短期BG、或长期SG包)
{
Interlocked.Increment(ref _datagramCount);
Interlocked.Increment(ref _errorDatagramCount);
this.OnDatagramError();
datagram = null;  // 丢弃当前包
}
else  // 实时包、定期包,先解析数据,判断正误,并发回确认包
{
datagram.ResolveDatagram();
if (true)  // 正确的包才⼊包队列
{
Interlocked.Increment(ref _datagramQueueCount);
lock (_datagramQueue)
{
_datagramQueue.Enqueue(datagram);  // 数据包⼊队列
}
}
else
{
Interlocked.Increment(ref _errorDatagramCount);
this.OnDatagramError();
}
}
}
session.ClearDatagramBuffer();  // 清包⽂缓冲区
}
C# Socket通信三⼤问题之TSession的拷贝转存数据包⽂的⽅法CopyToDatagramBuffer()代码如下:/// ﹤summary﹥
/// 拷贝接收缓冲区的数据到数据缓冲区(即多次读⼀个包⽂)
/// ﹤/summary﹥
public void CopyToDatagramBuffer(int startPos, int packLen)
{
int datagramLen = 0;
if (DatagramBuffer != null) datagramLen =
DatagramBuffer.Length;
// 调整长度(DataBuffer 为 null 不会出错)
Array.Resize(ref DatagramBuffer,
datagramLen + packLen);
// 拷贝到数据就缓冲区
Array.Copy(ReceiveBuffer, startPos,
DatagramBuffer, datagramLen, packLen);
}
代码中注释⽐较详细了,下⾯指出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通信三⼤问题有所帮助。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。