小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

用SocketAsyncEventArgs 池 線程構(gòu)建服務器“推”

 fatcat916 2012-01-22

用SocketAsyncEventArgs+池+線程構(gòu)建服務器“推”

2009-07-23 15:46 by Creason New, 3573 visits, 收藏, 編輯

下篇文章:【用SocketAsyncEventArgs+池+線程構(gòu)建服務器“推”】的源代碼分析1

說到服務器推技術(shù),可謂是讓人們又愛又恨,愛的是它能實現(xiàn)特殊的功能效果,恨的是它性能太低實現(xiàn)起來太復雜。大家知道Framework里有Socket、異步Socket,但實現(xiàn)起來只能是權(quán)且觀賞,要真實際應用卻問題重重,于是我拜讀了園子里的很多大俠們的文章,從中找到了一些線索,于是參考兼獨創(chuàng)弄出來一套方案。好,進入正題。

先來個引子。要做服務器推,當然要保證幾點:連接數(shù)、性能、穩(wěn)定性、靈活性,不能一臺服務器只能連個百八十個用戶,也不能一推就弄個CPU占用率100%甚至直接down掉,穩(wěn)定嘛,自然就要保證服務器不能因為用戶的變化而斷開服務,而且還要有判斷用戶的能力,不能那邊用戶斷開半個小時了,你服務器還以為人家還在線呢。因此,考慮到這些因素,我采用了SocketAsyncEventArgs對象(實際上就是BeginXX,EndXX一個變種)作為異步傳輸,構(gòu)建連接池和緩沖區(qū)來提高性能,以及多線程來管理接收與發(fā)送同時進行的情況。

再介紹一下原理吧。首先為了不在Accept一個用戶之后new一個對象,聰明一點的辦法就是在服務一啟動就把所有的對象創(chuàng)建好了,然后Accept一個用戶之后從“池子”里拿出一個對象給這個用戶,當這個用戶斷開了再把這個對象放回“池子”里,這也是線程池的原理。而且在創(chuàng)建連接池的時候我們把接收的緩沖區(qū)也給創(chuàng)建好了,也就是說緩沖區(qū)也不用new了,直接拿來用(嘿嘿,性能應該不會太差吧)。當然連接池里對象的個數(shù)根據(jù)你的服務器允許的并發(fā)的連接數(shù)在創(chuàng)建連接池的時候指定。再就是接收和發(fā)送同時進行的問題了,我把接收和發(fā)送放到兩個線程里,這樣就可以一邊發(fā)送一邊接收了,擺脫了對講機式的通話,而且性能上多線程要好些。

         貼個類圖:

類圖

      各個類說明:

            MySocketAsyncEventArgs:為了給SocketAsyncEventArgs對象一個用戶和收發(fā)的屬性,我讓MySocketAsyncEventArgs繼承自SocketAsyncEventArgs,并添加了UID和Property屬性,UID保持用戶標識,Property值為“Send”或“Receive”,就是標記“我”是管發(fā)送信息還是接收信息的。

              SocketAsyncEventArgsWithId:連接池里的一個連接單元,它有一個用戶標識UID(跟MySocketAsyncEventArgs對象的UID是相同的值)和兩個分別用于接收和發(fā)送的MySocketAsyncEventArgs對象。

             RequestHandler:所傳輸數(shù)據(jù)的協(xié)議。

             BufferManager:給SocketAsyncEventArgsWithId對象分配和管理緩沖區(qū)的類。

             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);//創(chuàng)建一個類庫可以調(diào)用的根據(jù)IP返回用戶標識的方法的委托,比如bill gate的ip是192.168.1.1,那么類庫不知道bill gate,只知道ip,所以在用的時候要讓類庫知道這么一個對應關(guān)系
12                 server = new SocketListener(2000032768, getid);//參數(shù)為:并行連接的數(shù)目,每個連接的緩沖區(qū)大小,ip/id對應的函數(shù)委托
13                 server.StartListenThread += new SocketListener.StartListenHandler(server_listen);//可以開始監(jiān)聽用戶發(fā)送的信息時觸發(fā)的事件
14                 server.OnMsgReceived += new SocketListener.ReceiveMsgHandler(server_recInfo);//服務器收到來著客戶端的信息時觸發(fā)的事件
15                 server.OnSended += new SocketListener.SendCompletedHandler(server_sendcompleted);//服務器發(fā)送完信息時觸發(fā)的事件
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();//開啟新的線程用于監(jiān)聽發(fā)來的數(shù)據(jù)
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先發(fā)送的,然后server回應。
      再來個性能分析:
      我的本本配置:CPU奔騰T2080 1.73G 雙核,內(nèi)存2G。我的服務器端的并發(fā)連接數(shù)量設(shè)的是20000個連接并發(fā),每個用戶的緩存是32768字節(jié),你們看第三四行的兩個進程,TestServer cpu:42% 內(nèi)存25036K,TestClient cpu:0%幾乎不用 內(nèi)存2296k。性能如何大家看著給個分吧。(按照計算內(nèi)存應該是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

      附上源代碼:http://files.cnblogs.com/niuchenglei/SocketLib.zip

博主前一篇:Pushlet(服務器推技術(shù))技術(shù)在.NET上的實現(xiàn)
博主后一篇:【用SocketAsyncEventArgs+池+線程構(gòu)建服務器“推”】的源代碼分析1
Add your comment

17 條回復

  1. #1樓 povy      2009-07-23 16:25
    看看~~
     回復 引用 查看   
  2. #2樓 Jeffrey Zhao      2009-07-23 17:08
    沒有仔細看實現(xiàn),你這個25M是什么內(nèi)存?看看虛擬內(nèi)存?
    // 異步Socket應該用了IOCP,性能的確會很好。
     回復 引用 查看   
  3. #3樓 大 兵      2009-07-23 17:45
    寫的不錯。
    1:其實客戶端的鏈接,發(fā)送和接收以及服務端的檢測端口,接收以及發(fā)送我們都是用了異步。我們并沒有等一個操作完成了才進行下個操作。所以還是可以保證同一時刻同一端口接收和發(fā)送在同時進行的。
    2:服務器推我猜想你是在解決客戶端關(guān)閉而服務端不知道這個問題的吧。修正接收過程【e.BytesTransferred > 0 && e.SocketError == SocketError.Success)】否則就關(guān)閉客戶端連接。

    圖畫的不錯。
     回復 引用 查看   
  4. #4樓 小黑三      2009-07-23 18:09
    不錯,能否把代碼上傳???
     回復 引用 查看   
  5. #5樓 非空      2009-07-23 22:15
    那個并發(fā)連接數(shù)的問題解決了,能否透露下你怎么解決通訊中出現(xiàn)的各種異常啊,比如積極拒絕啊 那邊突然掉了 拔網(wǎng)線了等等
     回復 引用 查看   
  6. #6樓 Galactica      2009-07-24 08:36
    圖很漂亮,用啥畫的?
     回復 引用 查看   
  7. #7樓[樓主] 小老牛      2009-07-24 09:03
    @Jeffrey Zhao
    25M內(nèi)存就是任務管理器里面的內(nèi)存啊,虛擬內(nèi)存從哪里看啊,你看看我貼的那個任務管理器的截圖的第三個和第四個進程。
     回復 引用 查看   
  8. #8樓[樓主] 小老牛      2009-07-24 09:05
    @大 兵
    其實在我的類庫里面已經(jīng)封裝了檢測機制了,就是用e.BytesTransferred > 0 && e.SocketError == SocketError.Success來檢測連接狀態(tài)的。把源代碼貼上,里面的注釋很詳細,你看看吧。謝謝回貼
     回復 引用 查看   
  9. #9樓[樓主] 小老牛      2009-07-24 09:06
    @小黑三
    貼上代碼地址了,看看吧,謝謝
     回復 引用 查看   
  10. #10樓[樓主] 小老牛      2009-07-24 09:08
    @非空
    我用了e.BytesTransferred > 0 和 e.SocketError == SocketError.Success來確定連接的狀態(tài),我想這足以解決各種連接中斷的問題了吧,你有什么高招嗎?請指教。
     回復 引用 查看   
  11. #11樓[樓主] 小老牛      2009-07-24 09:08
    @Galactica
    那Enterprise Architect做的
     回復 引用 查看   
  12. #12樓 Jeffrey Zhao      2009-07-24 09:17
    引用小老牛:
    @Jeffrey Zhao
    25M內(nèi)存就是任務管理器里面的內(nèi)存啊,虛擬內(nèi)存從哪里看啊,你看看我貼的那個任務管理器的截圖的第三個和第四個進程。

    任務管理器里select column,內(nèi)存有許多種。
     回復 引用 查看   
  13. #13樓 Autumn770[未注冊用戶]2009-07-29 12:05
    老兄,我看了你的服務器后,我試了一下,在客戶端斷開后,再連接以后發(fā)消息服務器就收不到了。
     回復 引用   
  14. #14樓 songcan      2009-10-16 10:31
    代碼無法下載
     回復 引用 查看   
  15. #15樓 heihoo      2010-11-26 20:47
    急需這個代碼,謝謝。heihoo@163.com
     回復 引用 查看   
  16. #16樓 qiuqingpo      2010-12-27 11:57
    70 /// <summary>
    171 /// 開始監(jiān)聽線程的入口函數(shù)
    172 /// </summary>
    173 public void Listen()
    174 {
    175 while (true)
    176 {
    177 string[] keys = readWritePool.OnlineUID;
    178 foreach (string uid in keys)
    179 {
    180 if (uid != null && readWritePool.busypool[uid].ReceiveSAEA.LastOperation != SocketAsyncOperation.Receive)
    181 {
    182 Boolean willRaiseEvent = (readWritePool.busypool[uid].ReceiveSAEA.UserToken as Socket).ReceiveAsync(readWritePool.busypool[uid].ReceiveSAEA);
    183 if (!willRaiseEvent)
    184 ProcessReceive(readWritePool.busypool[uid].ReceiveSAEA);
    185 }
    186 }
    187 }
    188 }

    大哥.你這樣不讓我CPU 100% 才怪呢.真不知道你是怎么測試的?
     回復 引用 查看   
  17. #17樓 l_rain      2011-10-21 18:03
    寫的很詳細,最新的代碼可以給我發(fā)一份嗎? email : l_rain@foxmail.
    樓上說的沒錯,CPU 40% 以上,內(nèi)存25M,這個效率太低了,不實用。內(nèi)存不是那樣算的

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多