用SocketAsyncEventArgs+池+线程构建服务器“推”

说到服务器推技术,可谓是让人们又爱又恨,爱的是它能实现特殊的功能效果,恨的是它性能太低实现起来太复杂。大家知道Framework里有Socket、异步Socket,但实现起来只能是权且观赏,要真实际应用却问题重重,于是我拜读了园子里的很多大侠们的文章,从中找到了一些线索,于是参考兼独创弄出来一套方案。好,进入正题。

先来个引子。要做服务器推,当然要保证几点:连接数、性能、稳定性、灵活性,不能一台服务器只能连个百八十个用户,也不能一推就弄个CPU占用率100%甚至直接down掉,稳定嘛,自然就要保证服务器不能因为用户的变化而断开服务,而且还要有判断用户的能力,不能那边用户断开半个小时了,你服务器还以为人家还在线呢。因此,考虑到这些因素,我采用了SocketAsyncEventArgs对象(实际上就是BeginXX,EndXX一个变种)作为异步传输,构建连接池和缓冲区来提高性能,以及多线程来管理接收与发送同时进行的情况。

再介绍一下原理吧。首先为了不在Accept一个用户之后new一个对象,聪明一点的办法就是在服务一启动就把所有的对象创建好了,然后Accept一个用户之后从“池子”里拿出一个对象给这个用户,当这个用户断开了再把这个对象放回“池子”里,这也是线程池的原理。而且在创建连接池的时候我们把接收的缓冲区也给创建好了,也就是说缓冲区也不用new了, 直接拿来用(嘿嘿,性能应该不会太差吧)。当然连接池里对象的个数根据你的服务器允许的并发的连接数在创建连接池的时候指定。再就是接收和发送同时进行的 问题了,我把接收和发送放到两个线程里,这样就可以一边发送一边接收了,摆脱了对讲机式的通话,而且性能上多线程要好些。

         贴个类图:


      各个类说明:

            MySocketAsyncEventArgs:为了给SocketAsyncEventArgs对象一个用户和收发的属性,我让 MySocketAsyncEventArgs继承自SocketAsyncEventArgs,并添加了UID和Property属性,UID保持用户 标识,Property值为“Send”或“Receive”,就是标记“我”是管发送信息还是接收信息的。

              SocketAsyncEventArgsWithId:连接池里的一个连接单元,它有一个用户标识UID(跟 MySocketAsyncEventArgs对象的UID是相同的值)和两个分别用于接收和发送的MySocketAsyncEventArgs对象。

             RequestHandler:所传输数据的协议。

             BufferManager:给SocketAsyncEventArgsWithId对象分配和管理缓冲区的类。

             SocketListener和SocketClient就不用解释了,是地球人都能看懂。

      

      服务器端代码

        1     class Program

 
 2     {
 3         static SocketListener server;
 4         static string command;
 5         static bool exit = true;
 6         static void Main(string[] args)
 7         {
 8             Console.WriteLine("Server is Starting"+Environment.NewLine);
 9             try
10             {
11                 SocketListener.GetIDByIPFun getid = new SocketListener.GetIDByIPFun(GetId);//创建一个类库可以调用的根据IP返回用户标识的方法的委托,比如bill gate的ip是192.168.1.1,那么类库不知道bill gate,只知道ip,所以在用的时候要让类库知道这么一个对应关系
12                 server = new SocketListener(20000, 32768, getid);//参数为:并行连接的数目,每个连接的缓冲区大小,ip/id对应的函数委托
13                 server.StartListenThread += new SocketListener.StartListenHandler(server_listen);//可以开始监听用户发送的信息时触发的事件
14                 server.OnMsgReceived += new SocketListener.ReceiveMsgHandler(server_recInfo);//服务器收到来着客户端的信息时触发的事件
15                 server.OnSended += new SocketListener.SendCompletedHandler(server_sendcompleted);//服务器发送完信息时触发的事件
16                 server.Init();
17                 server.Start(2008);
18                 Console.WriteLine("Server is Running" + Environment.NewLine);
19                 Console.WriteLine("1-send   2-stop" + Environment.NewLine);
20 
21                 while (exit)
22                 {
23                     Console.WriteLine("command:");
24                     command = Console.ReadLine();
25                     if (command == "1")
26                     {
27                         Console.WriteLine("input your msg:");
28                         string msg = Console.ReadLine();
29                         Console.WriteLine("who will you send:");
30                         string uid = Console.ReadLine();
31                         server.Send(uid, msg);
32                         continue;
33                     }
34                     else if (command == "2")
35                     {
36                         server.Stop();
37                         continue;
38                     }
39                     else
40                     {
41                         Console.WriteLine("command not found");
42                         continue;
43                     }
44                 }
45             }
46             catch (Exception e)
47             {
48                 Console.WriteLine(e.Message + Environment.NewLine);
49                 Console.ReadLine();
50             }
51         }
52         static void server_listen()
53         {
54             Thread listenthread = new Thread(new ThreadStart(server.Listen));
55             listenthread.Start();//开启新的线程用于监听发来的数据
56         }
57         static void server_recInfo(string uid, string info)
58         {
59             Console.WriteLine("msg:" + info + "\n from:" + uid + Environment.NewLine);
60         }
61         static void server_sendcompleted(bool successorfalse)
62         {
63             Console.WriteLine("your msg send success" + successorfalse + Environment.NewLine);
64         }
65         static string GetId(string IP)
66         {
67             return "110";
68         }
69     }
 

 

     客户器端代码

 
 1 
 2     class Program
 3     {
 4         static SocketClient client;
 5         static string command;
 6         static bool exit = true;
 7         static void Main(string[] args)
 8         {
 9             Console.WriteLine("Client is Starting" + Environment.NewLine);
10             try
11             {
12                 client = new SocketClient("192.168.170.1", 2008);
13                 client.StartListenThread += new SocketClient.StartListenHandler(client_listen);
14                 client.OnMsgReceived += new SocketClient.ReceiveMsgHandler(client_recInfo);
15                 client.OnSended += new SocketClient.SendCompleted(client_sendcompleted);
16                 Console.WriteLine("Client is Running" + Environment.NewLine);
17                 Console.WriteLine("1-connect   2-disconnect   3-send" + Environment.NewLine);
18 
19                 while (exit)
20                 {
21                     Console.WriteLine("command:");
22                     command = Console.ReadLine();
23                     if (command == "1")
24                     {
25                         client.Connect();
26                         continue;
27                     }
28                     else if (command == "2")
29                     {
30                         client.Disconnect();
31                         continue;
32                     }
33                     else if (command == "3")
34                     {
35                         Console.WriteLine("input your msg:");
36                         string msg = Console.ReadLine();
37                         client.Send(msg);
38                     }
39                     else
40                     {
41                         Console.WriteLine("command not found");
42                         continue;
43                     }
44                 }
45             }
46             catch (Exception e)
47             {
48                 Console.WriteLine(e.Message + Environment.NewLine);
49                 Console.ReadLine();
50             }
51         }
52 
53         static void client_listen()
54         {
55             Thread listenthread = new Thread(new ThreadStart(client.Listen));
56             listenthread.Start();
57         }
58 
59         static void client_recInfo(string info)
60         {
61             Console.WriteLine(info);
62         }
63 
64         static void client_sendcompleted(bool successorfalse)
65         {
66             Console.WriteLine("msg send success:" + successorfalse + Environment.NewLine);
67         }
68     }
 
      贴个运行效果图:

      这是Server和Client端的通信,首先是Client先发送的,然后server回应。
      再来个性能分析:
 
      我 的本本配置:CPU奔腾T2080 1.73G 双核,内存2G。我的服务器端的并发连接数量设的是20000个连接并发,每个用户的缓存是32768字节,你们看第三四行的两个进 程,TestServer cpu:42% 内存25036K,TestClient cpu:0%几乎不用 内存2296k。性能如何大家看着给个分吧。(按照计算内存应该是32768/1024/1024*20000=645M为什么在这里只有25M,为什么 相差这么多?)

 

      参考文章:http://www.cnblogs.com/jeriffe/articles/1407603.html     

                       http://www.cnblogs.com/chuncn/archive/2009/06/22/1508018.html

                       http://www.cnblogs.com/dabing/archive/2009/07/10/1520586.html

                       http://www.cnblogs.com/JimmyZhang/archive/2008/09/16/1291854.html

posted @ 2013-05-16 23:46  Net-Spider  阅读(654)  评论(0)    收藏  举报