IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [转]详解通信 C# Socket之问题

    lincyang发表于 2011-05-14 08:59:00
    love 0

      本文转载自:

      http://www.cnblogs.com/ttlin2010yeah/archive/2010/11/17/1879958.html

       

      C# 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通信三大问题有所帮助。



沪ICP备19023445号-2号
友情链接