Socket编程,如何保证外网环境下通信可靠无误

来源:互联网 发布:js判断一个月有多少天 编辑:程序博客网 时间:2024/06/06 03:10
    Socket编程,对一些函数接口的认识我觉得很多人都不够深入,比如:Socket.Send,Socket.Receive,Socket.BeginReceive,Socket.BeginSend等函数做一些理解肤浅,导致写出的程序在外网的环境下,根本不能正常运行。网上很多列程都是粗粗的说了一下下,根本没有探讨和深入这些问题,导致很多初学者,被辱歧途(误人子弟啊)。今天闲来无聊,谢谢自己的经验给大家分享下吧!

    Socket.Send是一个同步阻塞函数,表是向网络套借口发送数据,原型是:int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags);这里面要注意返回值哦!很多初级程序员,以为只要调用这个函数,发送数据的事情就万事大吉了,这是因为在编程环境中,都是本机或100M内网,网络速度足够快,一般99%不会出现问题。但如果你到Internet环境中的时候,那就漏洞百出了。

       这里首先要注意,此函数返回的是已经成功发送的字节数,你缓冲区的Buffer如果大于此返回值,这样数据就没有发送完毕。如果你要包装一个函数,一定要发送完你的Buffuer中的数据,那你建以你采用下面的方案:

           
       public bool MySendData(const byte[] Buffer,  Socket theSocket)
      {
            int  SendPos = 0;//缓冲区发送的开始位置
            int  OneDataSendLen = 0;//每次发送的数量
            while (SendPos < Buffer.Length)//发送完毕
            {               
                OneDataSendLen = theSocket.Send(Buffer, SendPos, Buffer.Length - SendPos, SocketFlags.None);
                SendPos += OneDataSendLen;//位置往后移动

            }

          return true;

        }
     同样的道理,Receive函数的包装方式也得调整,如果你接受一个固定大小的数据包,包装方式如下:
       
     public  byte[] MyReceive(const int PackLenth,Socket theSocket)
    {
                  byte[] Buff = new bytePackLenth
                  int RecPos=0;
                    while (RecPos < PackLenth)//未接受完毕,继续接收
                    {
                        OneDataSendLen = theSocket.Receive(CmdBuff, RecPos, CmdLength - RecPos, SocketFlags.None);
                        RecPos += OneDataSendLen;
                        if (RecPos >= PackLenth)
                            break;
                    }
            return Buff;

     }


     上面的Send和Receive是同步阻塞方法,一般在为了设计效率问题和灵活性,在做大规模高效的通信软件的情况下,一般是不会采用的,所以大家一般要用到一步方法BeginSend,BeginReceive方法。
     先看看他们的原型:
             public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state);
         其它函数都差不多,关键多了一个AsyncCallback和State参数,AsyncCallback这表示此次发送完成后(注意是此次完成,不一定是所有数据已发送完成),需要执行的回调函数,其类  型为:void AsyncCallback(IAsyncResult ar);
        另外还有一个Object 类型的State参数,这个参数用传送发送参数,并最终传递到AsyncCallback函数中,为此我们构建一个对象,
     public class TDatagram
    {
        public int OffSet = 0;
        public int OneLength = 1024 *8;//2
        public int Length;
        public byte[] Buffer;
        public Socket TheSocket;
        public bool IsObj;
     }

    //异步发送数据

    public bool AsnySend( byte[] Buffer, Socket TheSocket)

  {

                    TDatagram Datagram = new TDatagram();
                    Datagram.OffSet = 0;
                    Datagram.Length = Buffer.Length;
                    Datagram.Buffer = Buffer;
                    Datagram.TheSocket = TheSocket;                  
                    TheSocket.BeginSend(Buffer, 0,Buffer.Length, SocketFlags.None, new AsyncCallback(CallBackSendData), Datagram);
                  

  }

//     回调函数需要执行EndSend();

      private void CallBackSendData(IAsyncResult IAsync)
     {
           
                TDatagram Datagram = (TDatagram)(IAsync.AsyncState);
                Datagram.OffSet += Datagram.TheSocket.EndSend(IAsync);//回调结束此次发送,返回此次发送的字节数
                if (Datagram.OffSet < Datagram.Buffer.Length)//没有发完,继续发送
                {
                      Datagram.TheSocket.BeginSend(Datagram.Buffer, Datagram.OffSet, Datagram.Buffer.Length - Datagram.OffSet, SocketFlags.None, new AsyncCallback                                (CallBackSendData), Datagram);
                }             
      }
      就是说如果本次还没有发送完毕,需要继续调用发送方法,如果网络速度很慢,还可能出现反复调用发送方法。
      至于异步接受方法的实现,大家可以参考发送的方法了,在此我不再做描述了。

   

      本人曾经给海外朋友设计了一个大型高并发的实时赔率传送软件,基本就是采用上面的思路,实现了不同地区的数完整传输,没有出过差错,可以说是经过检验的。本文希望对大家设计自己专用的通信程序的时候做参考之用。如果一知半解,想当然,你的程序肯定漏洞百出!

   
1 0
原创粉丝点击