上一次的博文说错了东西,幸好有园友指出。才把错误改正过来,顺便也把利用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相关推荐

  1. .net中的socket异步通信实现--客户端代码

    在上一篇文章中主要介绍了socket异步通信实现的服务器端代码,下面我们来分析一下客户端代码: 那么在设计客户端代码时我们主要考虑哪些问题呢? 第一是如何接收数据,往往一次传输的数据量较大,但sock ...

  2. C#实现Socket异步通信,及完整源码库

    C#实现Socket异步通信,及完整源码库 背景 关键代码 完整源码下载 背景 工控上位机系统开发过程中不可避免的会用到socket通信技术,但是在支持多客户端并发连接时,常规方法效率很低.提高通信效 ...

  3. Socket 异步通信编程

    参考网址:http://www.cnblogs.com/sunev/archive/2012/08/07/2625688.html 一.摘要 本篇博文阐述基于TCP通信协议的异步实现. 二.实验平台 ...

  4. 手写简单的HttpServer基于Java nio 实现socket异步通信(请求映射注解方式)

    HttpServer服务类 1 package javax.servlet.http.server2; 2 3 import java.io.IOException; 4 import java.ne ...

  5. windowoPhone7.1 Socket编程-实现手机与电脑通信

    执行异步的Socket操作的模式包含已下步骤: 1. 配置新的 SocketAsyncEventArgs 对象,或者从应用程序池中获取一个这样的空闲对象. 2.针对该对象设置即将执行的操作所需的属性( ...

  6. BeetleX框架详解-小结

    到这里BeetleX组件代码讲解完成了,由于组件只封装了TCP基础通讯的功能,因此在内容上并不会有太多:通以上内容相信对BeetleX的设计有一定的了解,在使用上也更加容易. 要点 Socket对象应 ...

  7. linux常用c函数(中文版)

    都是linux的c函数东西略多,用页面搜索来查找吧. << Back to man.ChinaUnix.net isalnum(测试字符是否为英文或数字) 相关函数 isalpha,isd ...

  8. 百度前200页部分答案(初稿)

    1操作系统中 heap 和 stack 的区别 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.Java自动管理栈和堆,程序员不能直接地设置栈或堆. 在函数中定义的一些基本类 ...

  9. [转|整理]翻译:使用.net3.5的缓存池和SocketAsyncEventArgs类创建socket服务器

    原文地址:http://www.cnblogs.com/onlytiancai/archive/2008/06/25/1229321.html http://www.cnblogs.com/killk ...

最新文章

  1. Java基础点:多线程
  2. sed 删除windows下的CR/LF
  3. leetcode1029. 两地调度(贪心算法)
  4. GET请求参数中文乱码的解决办法
  5. buuctf [ElasticSearch]CVE-2015-1427
  6. SQL语句 SQL Server中Text类型操作
  7. Excel 2010 VBA 入门 071 工作表事件之Worksheet_Change
  8. 黑暗星空中的秘密——《黑暗森林》简评
  9. Mapreduce Wordcount白名单 Python实现
  10. JSON学习思维导图
  11. win10系统资源管理器频繁崩溃重启的解决思路
  12. python编程midi键盘按键_python 偷懒技巧——使用 keyboard 录制键盘事件
  13. 数据库安全性 --- 概述
  14. 无人洗车小程序源码开发
  15. 当前安装包签名出现异常_关于部分华为手机安装游戏提示“签名异常”问题说明...
  16. 嵌入式uClinux及其应用开发(1)
  17. 想开发手机APP软件,首先要弄清楚以下10点
  18. 一个陌生女人的来信-未完不续
  19. DQN、DDQN、DPG、DDPG、Dueling QN
  20. HTML实现简单注册登录页面

热门文章

  1. 利用Apache ab以及GNUPlot来进行Web测试
  2. 安装IE8不能调试VS2005的解决办法
  3. 让你费解的函数原型:Linux下的signa函数
  4. 快速加载生成nlp数据
  5. jittor和pytorch生成网络对比之relativistic_gan
  6. Pytorch可视化工具tensorboardX(安装不踩坑)
  7. python ftp文件传输服务端
  8. 第 1 章 虚拟化 - 013 - 动手实践 Linux VLAN
  9. MQTT 与 Kafka
  10. 使用 qrcodejs 生成二维码的几个问题