博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Socket异步通信——使用SocketAsyncEventArgs
阅读量:6708 次
发布时间:2019-06-25

本文共 11109 字,大约阅读时间需要 37 分钟。

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

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

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

1     class ConnectInfo2     {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
(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
(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                 else44                 {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
(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
(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 else100 {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 }

 

转载地址:http://zenlo.baihongyu.com/

你可能感兴趣的文章
操作xml格式的字符串的方法(C#)
查看>>
[转]MySQL索引详解(1)
查看>>
poj 3040 Allowance 贪心
查看>>
分享27款最佳的复古风格 WordPress 主题
查看>>
ASP.NET 打包多CSS或JS文件以加快页面加载速度的Handler
查看>>
让“是男人就下到100层”在Android平台上跑起来
查看>>
hdu 1010:Tempter of the Bone(DFS + 奇偶剪枝)
查看>>
文件和目录之umask函数
查看>>
ecmall的物流配送体系改造
查看>>
[Python]网络爬虫(九):百度贴吧的网络爬虫(v0.4)源码及解析(转)
查看>>
hdu4292Food(最大流Dinic算法)
查看>>
手机网站判断及跳转
查看>>
[LeetCode] Text Justification
查看>>
webdriver API study
查看>>
QoS令牌桶工作原理
查看>>
android工程gen目录中R.java包名是怎么确定
查看>>
【Machine Learning in Action --4】朴素贝叶斯过滤网站的恶意留言
查看>>
Java基础之类Class使用
查看>>
Ubuntu+Eclipse+ADT+Genymotion+VirtualBox开发环境搭建
查看>>
Android 学习之 开源项目PullToRefresh的使用
查看>>