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 }
转载于:https://www.cnblogs.com/HopeGi/archive/2013/04/16/3023833.html
Socket异步通信——使用SocketAsyncEventArgs相关推荐
- .net中的socket异步通信实现--客户端代码
在上一篇文章中主要介绍了socket异步通信实现的服务器端代码,下面我们来分析一下客户端代码: 那么在设计客户端代码时我们主要考虑哪些问题呢? 第一是如何接收数据,往往一次传输的数据量较大,但sock ...
- C#实现Socket异步通信,及完整源码库
C#实现Socket异步通信,及完整源码库 背景 关键代码 完整源码下载 背景 工控上位机系统开发过程中不可避免的会用到socket通信技术,但是在支持多客户端并发连接时,常规方法效率很低.提高通信效 ...
- Socket 异步通信编程
参考网址:http://www.cnblogs.com/sunev/archive/2012/08/07/2625688.html 一.摘要 本篇博文阐述基于TCP通信协议的异步实现. 二.实验平台 ...
- 手写简单的HttpServer基于Java nio 实现socket异步通信(请求映射注解方式)
HttpServer服务类 1 package javax.servlet.http.server2; 2 3 import java.io.IOException; 4 import java.ne ...
- windowoPhone7.1 Socket编程-实现手机与电脑通信
执行异步的Socket操作的模式包含已下步骤: 1. 配置新的 SocketAsyncEventArgs 对象,或者从应用程序池中获取一个这样的空闲对象. 2.针对该对象设置即将执行的操作所需的属性( ...
- BeetleX框架详解-小结
到这里BeetleX组件代码讲解完成了,由于组件只封装了TCP基础通讯的功能,因此在内容上并不会有太多:通以上内容相信对BeetleX的设计有一定的了解,在使用上也更加容易. 要点 Socket对象应 ...
- linux常用c函数(中文版)
都是linux的c函数东西略多,用页面搜索来查找吧. << Back to man.ChinaUnix.net isalnum(测试字符是否为英文或数字) 相关函数 isalpha,isd ...
- 百度前200页部分答案(初稿)
1操作系统中 heap 和 stack 的区别 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.Java自动管理栈和堆,程序员不能直接地设置栈或堆. 在函数中定义的一些基本类 ...
- [转|整理]翻译:使用.net3.5的缓存池和SocketAsyncEventArgs类创建socket服务器
原文地址:http://www.cnblogs.com/onlytiancai/archive/2008/06/25/1229321.html http://www.cnblogs.com/killk ...
最新文章
- Java基础点:多线程
- sed 删除windows下的CR/LF
- leetcode1029. 两地调度(贪心算法)
- GET请求参数中文乱码的解决办法
- buuctf [ElasticSearch]CVE-2015-1427
- SQL语句 SQL Server中Text类型操作
- Excel 2010 VBA 入门 071 工作表事件之Worksheet_Change
- 黑暗星空中的秘密——《黑暗森林》简评
- Mapreduce Wordcount白名单 Python实现
- JSON学习思维导图
- win10系统资源管理器频繁崩溃重启的解决思路
- python编程midi键盘按键_python 偷懒技巧——使用 keyboard 录制键盘事件
- 数据库安全性 --- 概述
- 无人洗车小程序源码开发
- 当前安装包签名出现异常_关于部分华为手机安装游戏提示“签名异常”问题说明...
- 嵌入式uClinux及其应用开发(1)
- 想开发手机APP软件,首先要弄清楚以下10点
- 一个陌生女人的来信-未完不续
- DQN、DDQN、DPG、DDPG、Dueling QN
- HTML实现简单注册登录页面