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://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于Socket异步通信——使用SocketAsyncEventArgs的详细内容...