zoukankan      html  css  js  c++  java
  • 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#
  • 相关阅读:
    js实现两种实用的排序算法——冒泡、快速排序
    node端代理浏览器路由 解决浏览器跨域问题
    HTTP Request header
    移动H5前端性能优化指南
    express下使用ES6
    Nginx Location配置总结
    NODE_ENV=production 环境变量设置
    css3逐帧动画
    js scheme 打开手机app的方法
    jQuery hashchange监听浏览器url变化
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3023962.html
Copyright © 2011-2022 走看看