好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Socket异步通信——使用SocketAsyncEventArgs

Socket异步通信——使用SocketAsyncEventArgs

Socket异步通信——使用SocketAsyncEventArgs

上一次的博文说错了东西,幸好有园友指出。才把错误改正过来,顺便也把利用SocketAsyncEventArgs进行Socket异步通信这方面的知识整理一下。    

  之前看了网上的代码,每进行一次异步操作都new 一个SocketAsyncEventArgs对象,然后网友评论太浪费资源了,于是就误以为用BeginXXX进行Socket异步通信会更优,幸好有园友指出我的误区,再看了这篇文章《 net3.5与.net2.0 Socket性能比较 》之后才发现SocketAsyncEventArgs是.NET Framework 3.5才出现的,而IAsyncResult是.NET Framework 2.0出现的,单纯从这点来看,新出的东西总会比旧的东西有优越之处吧。在用了之后明显的感觉就是,SocketAsyncEventArgs可以重复利用,而IAsyncResult对象就每次BeginXXX调用一次,就会生成一个,同样的进行多次的接收和发送之后,使用SocketAsyncEventArgs的一直都是使用那么一两个对象;可IAsyncResult的就每Begin一次就创建一次,累计下来创建了对象的数量很明显有差别。但是本人在使用SocketAsyncEventArgs时有部分思想还是停留在之前用BeginXXX方式时的。下面逐一列举吧

  同样也是定义了一个数据结构

 1       class   ConnectInfo
  2       {
  3           public  ArrayList tmpList {  get ;  set  ; }
  4           public  SocketAsyncEventArgs SendArg {  get ;  set  ; }
  5           public  SocketAsyncEventArgs ReceiveArg {  get ;  set  ; }
  6           public  Socket ServerSocket{ get ; set  ;}
  7      }

虽然SocketAsyncEventArgs可以重复利用,但我在整个程序里头总共使用了三个,一个用于Accept的,这个在Complete事件绑定的方法里释放资源了。另外两个分别用于发送和接收。ArrayList是暂存客户端发来的数据。

  下面则是Main方法里的代码

  1              IPEndPoint serverPoint =  new  IPEndPoint(IPAddress.Parse( "  127.0.0.1  " ),  8081  );
   2              Socket serverSocket =  new   Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
   3  
  4               serverSocket.Bind(serverPoint);
   5              serverSocket.Listen( 10  );
   6  
  7              Console.WriteLine( "  waiting for a client  "  );
   8              SocketAsyncEventArgs e= new   SocketAsyncEventArgs();
   9              e.Completed +=  new  EventHandler<SocketAsyncEventArgs> (Accept_Completed);
  10               serverSocket.AcceptAsync(e);
  11  
 12              Console.ReadLine();

个人感觉这部分与使用IAsyncReult的BeginXXX不同的就是不需要把客户端的Socket对象传到Accept的方法里。

         接着是与Complete事件定义的方法定义,也就是Accept成功之后调用的方法

  1           static   void  Accept_Completed( object   sender, SocketAsyncEventArgs e)
   2           {
   3              Socket client =  e.AcceptSocket;
   4              Socket server = sender  as   Socket;
   5  
  6               if  (sender ==  null )  return  ;
   7  
  8              SocketAsyncEventArgs sendArg =  new   SocketAsyncEventArgs();
   9              SocketAsyncEventArgs receciveArg =  new   SocketAsyncEventArgs();
  10  
 11              ConnectInfo info =  new   ConnectInfo();
  12              info.tmpList =  new   ArrayList();
  13              info.SendArg =  sendArg;
  14              info.ReceiveArg =  receciveArg;
  15              info.ServerSocket= server;
  16  
 17               byte [] sendbuffers=Encoding.ASCII.GetBytes( "  hello world  "  );
  18              sendArg.SetBuffer(sendbuffers,  0  , sendbuffers.Length);
  19  
 20              sendbuffers= new   byte [ 1024  ];
  21              receciveArg.SetBuffer(sendbuffers,  0 ,  1024  );
  22              receciveArg.UserToken =  info;
  23              receciveArg.Completed +=  new  EventHandler<SocketAsyncEventArgs> (Rececive_Completed);
  24  
 25               client.SendAsync(sendArg);
  26               client.ReceiveAsync(receciveArg);
  27  
 28               e.Dispose();
  29          }

由于有sender这个参数,就可以获取服务端的Socket对象,在这里把两个SocektAsyncEventArgs对象构造出来, 在构造了一个ConnectInfo的对象记录本次通信的一些信息。ConnectInfo这部分与BeginXXX的类似。但把ConnectInfo的对象传过去给相应的Complete事件处理方法里面就有所不同了,SocketAsyncEventArgs是用UserToken属性,把关联的用户或应用程序对象传过去。设置Buffer就用SetBuffer方法。发送消息并没有绑定Complete方法,感觉绑了也没什么用。

  最后上的是接受成功的代码

  1           static   void  Rececive_Completed( object   sender, SocketAsyncEventArgs e)
   2           {
   3              ConnectInfo info = e.UserToken  as   ConnectInfo;
   4               if  (info ==  null )  return  ;
   5              Socket client = sender  as   Socket;
   6               if  (client ==  null )  return  ;
   7  
  8               if  (e.SocketError==  SocketError.Success)
   9               {
  10                   int  rec =  e.BytesTransferred;
  11                   //  收不到数据表明客户端终止了通信
  12                   //  关闭相关的socket和释放资源 
 13                   if  (rec ==  0  )
  14                   {
  15                       client.Close();
  16                       client.Dispose();
  17  
 18                       info.ReceiveArg.Dispose();
  19                       info.SendArg.Dispose();
  20  
 21                       if  (info.ServerSocket !=  null  )
  22                       {
  23                           info.ServerSocket.Close();
  24                           info.ServerSocket.Dispose();
  25                       }
  26                       return  ;
  27                   }
  28  
 29                   byte [] datas= e.Buffer;
  30  
 31                   //  如果数据还没接收完的就把已接收的数据暂存
  32                   //  新开辟一个足够大的buffer来接收数据 
 33                   if  (client.Available >  0  )
  34                   {
  35                       for  ( int  i =  0 ; i < rec; i++ )
  36                           info.tmpList.Add(datas[i]);
  37                      Array.Clear(datas,  0  , datas.Length);
  38  
 39                      datas =  new   byte  [client.Available];
  40                      e.SetBuffer(datas,  0  , datas.Length);
  41                       client.ReceiveAsync(e);
  42                   }
  43                   else 
 44                   {
  45                       //  检查暂存数据的ArrayList中有没有数据,有就和本次的数据合并 
 46                       if  (info.tmpList.Count >  0  )
  47                       {
  48                           for  ( int  i =  0 ; i < rec; i++ )
  49                               info.tmpList.Add(datas[i]);
  50                          datas = info.tmpList.ToArray( typeof ( byte ))  as   byte  [];
  51                          rec =  datas.Length;
  52                       }
  53  
 54                       //  对接收的完整数据进行简单处理,回发给客户端 
 55                       string  msg = Encoding.ASCII.GetString(datas).Trim( '  \0  '  );
  56                       if  (msg.Length >  10 ) msg = msg.Substring( 0 ,  10 ) +  "  ...  "  ;
  57                      msg =  string .Format( "  rec={0}\r\nmessage={1}  "  , rec, msg);
  58  
 59                      info.SendArg.SetBuffer(Encoding.ASCII.GetBytes(msg), 0  ,msg.Length);
  60                       client.SendAsync(info.SendArg);
  61  
 62                       //  如果buffer过大的,把它换成一个小的 
 63                       info.tmpList.Clear();
  64                       if  (e.Buffer.Length >  1024  )
  65                       {
  66                          datas =  new   byte [ 1024  ];
  67                          e.SetBuffer(datas,  0  , datas.Length);
  68                       }
  69  
 70                       //  再次进行异步接收 
 71                       client.ReceiveAsync(e);
  72                   } 
  73               }
  74          }

这里也是考虑到接收数据量过大造成下次接收时粘包,也做类似上篇博文中的那样处理。在接收过大的数据时需要分两次读取,用一个ArrayList暂时存放已经读取的数据。在上一篇博文有位园友提了一下内存消耗的问题,于是这次我缩减了一下byte[]的使用量。这样应该消耗不再大了吧!

  尽管这次我做的感觉比上次的要好,但对于在行的人应该会挑得出不少毛病出来的。上面有什么说错的请各位指出,有什么说漏的,请各位提点,多多指导。谢谢!

完整代码

  1       class   ConnectInfo
    2       {
    3           public  ArrayList tmpList {  get ;  set  ; }
    4           public  SocketAsyncEventArgs SendArg {  get ;  set  ; }
    5           public  SocketAsyncEventArgs ReceiveArg {  get ;  set  ; }
    6           public  Socket ServerSocket{ get ; set  ;}
    7       }
    8  
   9       class   EventSocektServer
   10       {
   11           static   void   Main()
   12           {
   13              IPEndPoint serverPoint =  new  IPEndPoint(IPAddress.Parse( "  127.0.0.1  " ),  8081  );
   14              Socket serverSocket =  new   Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
   15  
  16               serverSocket.Bind(serverPoint);
   17              serverSocket.Listen( 10  );
   18  
  19              Console.WriteLine( "  waiting for a client  "  );
   20              SocketAsyncEventArgs e= new   SocketAsyncEventArgs();
   21              e.Completed +=  new  EventHandler<SocketAsyncEventArgs> (Accept_Completed);
   22               serverSocket.AcceptAsync(e);
   23  
  24               Console.ReadLine();
   25           }
   26  
  27           static   void  Accept_Completed( object   sender, SocketAsyncEventArgs e)
   28           {
   29              Socket client =  e.AcceptSocket;
   30              Socket server = sender  as   Socket;
   31  
  32               if  (sender ==  null )  return  ;
   33  
  34              SocketAsyncEventArgs sendArg =  new   SocketAsyncEventArgs();
   35              SocketAsyncEventArgs receciveArg =  new   SocketAsyncEventArgs();
   36  
  37              ConnectInfo info =  new   ConnectInfo();
   38              info.tmpList =  new   ArrayList();
   39              info.SendArg =  sendArg;
   40              info.ReceiveArg =  receciveArg;
   41              info.ServerSocket= server;
   42  
  43               byte [] sendbuffers=Encoding.ASCII.GetBytes( "  hello world  "  );
   44              sendArg.SetBuffer(sendbuffers,  0  , sendbuffers.Length);
   45  
  46              sendbuffers= new   byte [ 1024  ];
   47              receciveArg.SetBuffer(sendbuffers,  0 ,  1024  );
   48              receciveArg.UserToken =  info;
   49              receciveArg.Completed +=  new  EventHandler<SocketAsyncEventArgs> (Rececive_Completed);
   50  
  51               client.SendAsync(sendArg);
   52               client.ReceiveAsync(receciveArg);
   53  
  54               e.Dispose();
   55           }
   56  
  57           static   void  Rececive_Completed( object   sender, SocketAsyncEventArgs e)
   58           {
   59              ConnectInfo info = e.UserToken  as   ConnectInfo;
   60               if  (info ==  null )  return  ;
   61              Socket client = sender  as   Socket;
   62               if  (client ==  null )  return  ;
   63  
  64               if  (e.SocketError ==  SocketError.Success)
   65               {
   66                   int  rec =  e.BytesTransferred;
   67                   //  收不到数据表明客户端终止了通信
   68                   //  关闭相关的socket和释放资源 
  69                   if  (rec ==  0  )
   70                   {
   71                       client.Close();
   72                       client.Dispose();
   73  
  74                       info.ReceiveArg.Dispose();
   75                       info.SendArg.Dispose();
   76  
  77                       if  (info.ServerSocket !=  null  )
   78                       {
   79                           info.ServerSocket.Close();
   80                           info.ServerSocket.Dispose();
   81                       }
   82                       return  ;
   83                   }
   84  
  85                   byte [] datas =  e.Buffer;
   86  
  87                   //  如果数据还没接收完的就把已接收的数据暂存
   88                   //  新开辟一个足够大的buffer来接收数据 
  89                   if  (client.Available >  0  )
   90                   {
   91                       for  ( int  i =  0 ; i < rec; i++ )
   92                           info.tmpList.Add(datas[i]);
   93                      Array.Clear(datas,  0  , datas.Length);
   94  
  95                      datas =  new   byte  [client.Available];
   96                      e.SetBuffer(datas,  0  , datas.Length);
   97                       client.ReceiveAsync(e);
   98                   }
   99                   else 
 100                   {
  101                       //  检查暂存数据的ArrayList中有没有数据,有就和本次的数据合并 
 102                       if  (info.tmpList.Count >  0  )
  103                       {
  104                           for  ( int  i =  0 ; i < rec; i++ )
  105                               info.tmpList.Add(datas[i]);
  106                          datas = info.tmpList.ToArray( typeof ( byte ))  as   byte  [];
  107                          rec =  datas.Length;
  108                       }
  109  
 110                       //  对接收的完整数据进行简单处理,回发给客户端 
 111                       string  msg = Encoding.ASCII.GetString(datas).Trim( '  \0  '  );
  112                       if  (msg.Length >  10 ) msg = msg.Substring( 0 ,  10 ) +  "  ...  "  ;
  113                      msg =  string .Format( "  rec={0}\r\nmessage={1}  "  , rec, msg);
  114  
 115                      info.SendArg.SetBuffer(Encoding.ASCII.GetBytes(msg),  0  , msg.Length);
  116                       client.SendAsync(info.SendArg);
  117  
 118                       //  如果buffer过大的,把它换成一个小的 
 119                       info.tmpList.Clear();
  120                       if  (e.Buffer.Length >  1024  )
  121                       {
  122                          datas =  new   byte [ 1024  ];
  123                          e.SetBuffer(datas,  0  , datas.Length);
  124                       }
  125  
 126                       //  再次进行异步接收 
 127                       client.ReceiveAsync(e);
  128                   }
  129               }
  130          }

 

 

分类:  Socket编程 ,  C#

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Socket异步通信——使用SocketAsyncEventArgs的详细内容...

  阅读:39次