国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
深入探析c# Socket

深入探析c# Socket

2010-09-08 17:28 by 田志良, 14562 visits, 收藏, 編輯

  最近瀏覽了幾篇有關(guān)Socket發(fā)送消息的文章,發(fā)現(xiàn)大家對Socket Send方法理解有所偏差,現(xiàn)將自己在開發(fā)過程中對Socket的領(lǐng)悟?qū)懗鰜?,以供大家參考?/p>

 ?。ㄒ唬┘軜?gòu)

  基于TCP協(xié)議的Socket通信,架構(gòu)類似于B/S架構(gòu),一個Socket通信服務(wù)器,多個Socket通信客戶端。Socket通信服務(wù)器啟動時,會建立一個偵聽Socket,偵聽Socket將偵聽到的Socket連接傳給接受Socket,然后由接受Socket完成接受、發(fā)送消息,當(dāng)Socket存在異常時,斷開連接。在實際開發(fā)項目中,往往要求Socket通信服務(wù)器能提供高效、穩(wěn)定的服務(wù),一般會用到以下技術(shù):雙工通信、完成端口、SAEA、池、多線程、異步等。特別是池,用的比較多,池一般包括一下幾種:

1)Buffer池,用于集中管控Socket緩沖區(qū),防止內(nèi)存碎片。

2)SAEA池,用于集中管控Socket,重復(fù)利用Socket。

3)SQL池,用于分離網(wǎng)絡(luò)服務(wù)層與數(shù)據(jù)訪問層(SQL的執(zhí)行效率遠遠低于網(wǎng)絡(luò)層執(zhí)行效率)。

4)線程池,用于從線程池中調(diào)用空閑線程執(zhí)行業(yè)務(wù)邏輯,進一步提高網(wǎng)絡(luò)層運行效率。

 


  (二)Send

  主服務(wù)器接受Socket為一端口,客戶端Socket為一端口,這兩個端口通過TCP協(xié)議建立連接,通信基礎(chǔ)系統(tǒng)負責(zé)管理此連接,它有兩個功能:            

  1)發(fā)送消息            

  2)接受消息

  Socket的Send方法,并非大家想象中的從一個端口發(fā)送消息到另一個端口,它僅僅是拷貝數(shù)據(jù)到基礎(chǔ)系統(tǒng)的發(fā)送緩沖區(qū),然后由基礎(chǔ)系統(tǒng)將發(fā)送緩沖區(qū)的數(shù)據(jù)到連接的另一端口。值得一說的是,這里的拷貝數(shù)據(jù)與異步發(fā)送消息的拷貝是不一樣的,同步發(fā)送的拷貝,是直接拷貝數(shù)據(jù)到基礎(chǔ)系統(tǒng)緩沖區(qū),拷貝完成后返回,在拷貝的過程中,執(zhí)行線程會IO等待, 此種拷貝與Socket自帶的Buffer空間無關(guān),但異步發(fā)送消息的拷貝,是將Socket自帶的Buffer空間內(nèi)的所有數(shù)據(jù),拷貝到基礎(chǔ)系統(tǒng)發(fā)送緩沖區(qū),并立即返回,執(zhí)行線程無需IO等待,所以異步發(fā)送在發(fā)送前必須執(zhí)行SetBuffer方法,拷貝完成后,會觸發(fā)你自定義回調(diào)函數(shù)ProcessSend,在ProcessSend方法中,調(diào)用SetBuffer方法,重新初始化Buffer空間。

 

 

  口說無憑,下面給個例子:

  服務(wù)器端:

客戶端:

解釋:

 

客戶端第一次發(fā)送數(shù)據(jù):1234567890。

客戶端第一個接受數(shù)據(jù):1234567890,該數(shù)據(jù)由服務(wù)端用Send同步方法發(fā)送返回。

客戶端第二個接受數(shù)據(jù):1234567890,該數(shù)據(jù)由服務(wù)端用Send異步方法發(fā)送返回。

 

以上似乎沒什么異常,好,接下來,我只發(fā)送abc。

客戶端第一個接受數(shù)據(jù):abc,理所當(dāng)然,沒什么問題。

客戶端第二個接受數(shù)據(jù):abc4567890!為什么呢?應(yīng)該是abc才對呀!

 

好,現(xiàn)在為大家解釋一下:

異步發(fā)送是將其Buffer空間中所有數(shù)據(jù)拷貝到基礎(chǔ)系統(tǒng)發(fā)送緩沖區(qū),第一次拷貝1234567890到發(fā)送緩沖區(qū),所以收到1234567890,第二次拷貝abc到發(fā)送緩沖區(qū),替換了先前的123,所以收到abc4567890,大家明白的?

 

源碼:

 

 

BufferManager

 

 

 

SocketAsyncEventArgsPool

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;

class AsyncUserToken
{
public Socket Socket;
}

 

 

 

Server

 

 

 

 

Program

 

 

分類: Socket
博主前一篇:c# 多線程 --Mutex(互斥鎖)
博主后一篇:c#實現(xiàn)用SQL池(多線程),定時批量執(zhí)行SQL語句
Add your comment

37 條回復(fù)

  1. #1樓 dreamhappy      2010-09-08 18:48
    希望博主能寫幾篇關(guān)于
    c# .net環(huán)境 c/s多線程socket開發(fā),如何及時的釋放線程 關(guān)閉線程和socket的順序應(yīng)該怎樣處理的博文,因為我之前socket編程時候客戶端和服務(wù)器端分別有一個線程和一個socket 往往線程不知道什么時候合理的釋放


     回復(fù) 引用 查看   
  2. #2樓 秋色      2010-09-08 19:27
    線程的釋放,一般是定義開關(guān)變量。讓線程自己退出。
    while(開關(guān))
    {
    if(??)
    {
    開關(guān)=false;
    }
    }
     回復(fù) 引用 查看   
  3. #3樓 %admin      2010-09-09 10:36
    實際測試了一下,還真出現(xiàn)了樓主描述的問題,我想這問題主要就出在了Buffer池的使用上,Send的時候發(fā)送的是 從e.buff 拷貝出來的真實大小的數(shù)據(jù),SendAsyn的時候發(fā)送的是e.buff 。 樓主還真是細心啊,不過好像實際中要發(fā)送數(shù)據(jù)給客戶端的時候不應(yīng)該在用e.buff了, 此問題有待繼續(xù)深入
     回復(fù) 引用 查看   
  4. #4樓[樓主] 田志良      2010-09-09 10:37
    @dreamhappy
    多線程在Socket開發(fā)中尤為重要,若處理不當(dāng),會嚴(yán)重影響效率,接下來,我會陸續(xù)寫些這類博文,謝謝大家關(guān)注。
     回復(fù) 引用 查看   
  5. #5樓[樓主] 田志良      2010-09-09 10:42
    @安度
    如果你想使你的Socket服務(wù)器非常高效,池是一定用到的,如果不用池,高級消息隊列也行,這樣才能極大提高并發(fā)數(shù)和最大連接數(shù)。
     回復(fù) 引用 查看   
  6. #6樓 %admin      2010-09-09 10:42
    還好SocketAsyncEventArgs 提供了SetBuffer ,遇到這種情況是,不妨在SendAsyn之前 動態(tài)的 e.SetBuffer 一下,就沒問題了~~

    示例:
    e.AcceptSocket.Send(data);
    System.Threading.Thread.Sleep(1000);

    e.SetBuffer(0, data.Length);
    if (!e.AcceptSocket.SendAsync(e))
    {
    Console.WriteLine("asynsend error");
    }
     回復(fù) 引用 查看   
  7. #7樓[樓主] 田志良      2010-09-09 10:44
    @九九
    目前我開發(fā)的IM系統(tǒng)正在做壓力測試,基本上最大連接數(shù)能上到20000,并發(fā)能上到3000。你所提的問題平時我也有遇到過,有空大家一起研究研究。
     回復(fù) 引用 查看   
  8. #8樓 %admin      2010-09-09 10:51
    其實把下面3處代碼關(guān)聯(lián)起來看下就比較容易了解為什么會出現(xiàn)這種情況了,

    if (Buffer.SetBuffer(e))                    {                        if (!e.AcceptSocket.ReceiveAsync(e))  //是否觸發(fā) Asyn_Commpleted事件                        {                            BeginReceive(e);                        }                    }這段是接受連接時調(diào)用Buffer類的SetBuffer方法,實際上還是操作的SocketAsyncEventArgs.SetBuffer


    internal Boolean SetBuffer(SocketAsyncEventArgs args)        {            if (this.freeIndexPool.Count > 0)            {                args.SetBuffer(this.buffer, this.freeIndexPool.Pop(), this.bufferSize);            }            else            {                if ((this.numSize - this.bufferSize) < this.currentIndex)                {                    return false;                }                args.SetBuffer(this.buffer, this.currentIndex, this.bufferSize);                this.currentIndex += this.bufferSize;            }            return true;        }看Buffer類的SetBuffer函數(shù)就很清楚了,這個Buffer池與SocketAsyncEventArgs不存在邏輯上的關(guān)聯(lián),只是外部分配緩沖區(qū)然后設(shè)置SocketAsyncEventArgs


    e.AcceptSocket.Send(data);                        System.Threading.Thread.Sleep(1000);                        e.SetBuffer(0, data.Length);                        if (!e.AcceptSocket.SendAsync(e))                        {                            Console.WriteLine("asynsend error");                        }到這里,SendAsyn 實際上使用的緩沖區(qū)是在Accept時候就設(shè)置好了的,所以此時如果不進行e.SetBuffer(0, data.Length); 就出現(xiàn)了,樓主描述的問題,
     回復(fù) 引用 查看   
  9. #9樓[樓主] 田志良      2010-09-09 11:10
  10. #10樓 %admin      2010-09-09 11:29


    呵呵,你說的這問題也注意到了,我還是不夠深入,對于SAEA還沒有了解, 因為自己項目中代碼跟你的很相似也就自己測試了下,分析的也不知道對不對,呵呵,繼續(xù)學(xué)習(xí)! 去看看SAEA
  11. #11樓 henry      2010-09-09 11:46
    其實SocketAsyncEventArgs性能不錯的,在新的測試中cpu E5405 的服務(wù)器,服務(wù)端接收256byte數(shù)據(jù)并返回給client(有分包處理)其秒處理數(shù)據(jù)包的能力在2.5W. 而CPU只占用了50%,內(nèi)存在300M內(nèi).
    補充:這樣的處理方式在秒處理5000消息的時候估計會性能問題產(chǎn)生.
     回復(fù) 引用 查看   
  12. #12樓 阿三      2010-09-09 14:31
    小伙子進步不錯嘛。
     回復(fù) 引用 查看   
  13. #13樓 無為無知無欲      2010-09-09 16:27
    樓主,我剛做了這個.net 3.5 完成端口方法的測試,一臺普通的服務(wù)器,2G內(nèi)存,可以并發(fā)接受1500條消息/秒, 這個瓶頸主要是從消息隊列寫進數(shù)據(jù)庫的瓶頸,超過后就會造成消息隊列的增長,但前端還是能不斷接收數(shù)據(jù)的。所以如果數(shù)據(jù)庫服務(wù)器更高效的話,能力還能大幅提高。
    最高連接我做到了20000,CPU,和內(nèi)存還沒怎么提高,所以應(yīng)該能更高,問題是測試的時候這么多的客戶端不好做啊。
     回復(fù) 引用 查看   
  14. #14樓[樓主] 田志良      2010-09-09 17:16
    @無為無知無欲
    所以你要做一個SQL池,將要執(zhí)行的SQL語句放到池中,然后每隔一段時間,安排一條線程掃描SQL池,如果SQL池中有SQL語句,則批量執(zhí)行,如果沒有則退出。在對SQL池管理時要尤為小心,Push操作和Ececute操作要互斥,執(zhí)行SQL語句時,不能Push SQL語句,相反,Push SQL語句時,也不能執(zhí)行SQL語句。
     回復(fù) 引用 查看   
  15. #15樓 安度      2010-09-09 17:28
    我是最近才接觸Socket的,貌似SocketAsyncEventArgs我沒有在網(wǎng)上看到,大概用的都是BeginXXX和EndXXX,服務(wù)端的話,基本是用多線程(Accpet在一個獨立的線程),然后做一個線程池的管理類(貌似.net有現(xiàn)成的線程池),不知道樓主這種方法有什么優(yōu)點,不防解釋下
     回復(fù) 引用 查看   
  16. #16樓 安度      2010-09-09 17:33
    在.NET 3.5里System.Net.Sockets空間下有一組增強功能的類,提供可供專用的高性能套接字應(yīng)用程序使用的可選異步模式,SocketAsyncEventArgs 類就是這一組增強功能的一部分。該類專為需要高性能的網(wǎng)絡(luò)服務(wù)器應(yīng)用程序而設(shè)計。應(yīng)用程序可以完全使用增強的異步模式,也可以僅僅在目標(biāo)熱點區(qū)域(例如,在接收大量數(shù)據(jù)時)使用此模式。以下是關(guān)于此類的介紹(摘自MSDN)

    原來是3.5里面的!是不是就是傳說中的IOCP?有機會樓主寫詳細點,原來不知道樓主是用的這個!
     回復(fù) 引用 查看   
  17. #17樓[樓主] 田志良      2010-09-09 18:07
    @安度
    我的做法是開通300或更多個Socket用于接受偵聽Socket傳遞的SAEA,為什么要開通這么多?你在做壓力測試時就明白了,開通多一點,會使你的連接效率、連接速度大幅度提高。對于SAEA,它不能同時ReceiveAsync、SendAsync,所以采用雙工通信,讓收發(fā)數(shù)據(jù)在同一條連接上進行,以提高效率。對于業(yè)務(wù)邏輯層上的處理,主要采用線程池、SQL池,用SQL池主要將網(wǎng)絡(luò)層與數(shù)據(jù)訪問層分離,為什么要分離?數(shù)據(jù)庫操作會極大影響效率,如果不分離,數(shù)據(jù)操作會拖垮網(wǎng)絡(luò)層。
     回復(fù) 引用 查看   
  18. #18樓 Leon Weng      2010-09-11 02:07
    前段時間搞視頻通信時用到了sokect,順便研究了一下,感覺效率的確比較高,但是在多線程方面自我感覺很差,所以沒有使用socket完成改成WCF了,WCF封裝了SOCKET,更好用了。
     回復(fù) 引用 查看   
  19. #19樓 ToBin      2011-03-01 11:12
    雙工通信時需要一個客戶端連接兩個端口嘛,一個收,一個發(fā)?
    現(xiàn)在已經(jīng)正在使用此 SocketAsyncEventArgs 實例進行異步套接字操作。
    為什么會出現(xiàn)這個問題呢?
     回復(fù) 引用 查看   
  20. #20樓[樓主] 田志良      2011-03-01 11:30
    @ToBin
    當(dāng)一個SAEA對象已處于StartReceive狀態(tài)時,就不能用此SAEA發(fā)送消息。也就是說SAEA對象在一個時刻中只能處于StartAccept、StartReceive、StartSend狀態(tài)中的一種。解決的辦法就是用雙工通信,為一條連接開辟兩個SAEA對象,一個用于收,一個用于發(fā)。
     回復(fù) 引用 查看   
  21. #21樓 ToBin      2011-03-01 13:02
    剛又把文章仔細讀了一遍,很多東西還是沒有理解!
    剛好看到您的回復(fù)!很有幫助,我再研究研究您的文章!
    還有這個saea的三種狀態(tài),我現(xiàn)在在發(fā)送的時候報錯"
    現(xiàn)在已經(jīng)正在使用此 SocketAsyncEventArgs 實例進行異步套接字操作"
    但我這個確實是clientsocket.sendasync(saeasender)是發(fā)生的。
    我接受的時候用的clientsocket.receiveasync(saeareceiver),兩個沒有沖突啊,很奇怪!
     回復(fù) 引用 查看   
  22. #22樓[樓主] 田志良      2011-03-01 14:55
    @ToBin
    建議Receive用異步模式,Send用同步模式。
     回復(fù) 引用 查看   
  23. #23樓 ToBin      2011-03-01 17:31
    錯誤“現(xiàn)在已經(jīng)正在使用此 SocketAsyncEventArgs 實例進行異步套接字操作”是不是因為異步發(fā)送了就返回了,但實際上還沒有發(fā)送到,當(dāng)?shù)诙萎惒桨l(fā)送的時候,第一次還沒發(fā)送完,就出了這個錯誤?
    我猜測!
     回復(fù) 引用 查看   
  24. #24樓[樓主] 田志良      2011-03-02 16:51
    @ToBin
    當(dāng)Socket處于StartSend狀態(tài)時,也不能執(zhí)行發(fā)送操作,必須等到發(fā)送回調(diào)事件觸發(fā)后,才能繼續(xù)執(zhí)行StartSend。解決的辦法是:記錄Socket的當(dāng)前狀態(tài),并存儲在Socket的UserToken對象下,當(dāng)要執(zhí)行StartSend時,判斷狀態(tài)。不過這樣效率會很慢,當(dāng)并發(fā)量達到3000時,會報很多錯,推薦的方法是用同步發(fā)送。不要覺得同步發(fā)送就一定會比異步發(fā)送慢,事實證明,對于SocketAsyncEventArgs,同步發(fā)送比異步發(fā)送快多了。
     回復(fù) 引用 查看   
  25. #25樓 ToBin      2011-03-02 17:17
    拜讀了,現(xiàn)在還有一個問題請教,異步的時候是“但異步發(fā)送消息的拷貝,是將Socket自帶的Buffer空間內(nèi)的所有數(shù)據(jù),拷貝到基礎(chǔ)系統(tǒng)發(fā)送緩沖區(qū),并立即返回”這個基礎(chǔ)系統(tǒng)緩沖區(qū)是對應(yīng)winsocket的,有點疑惑,就是只有一個緩存區(qū),接受也從基礎(chǔ)系統(tǒng)緩沖區(qū)中拷貝處來,發(fā)送也是拷貝到這,如果我接收的時候同時發(fā)送,基礎(chǔ)系統(tǒng)緩沖區(qū)里的數(shù)據(jù)還沒取出來,將要取出來的時候發(fā)送,拷貝進去,會不是導(dǎo)致接收出來的數(shù)據(jù)不正確?導(dǎo)致臟讀,數(shù)據(jù)錯誤!
    有這樣的問題嘛?
     回復(fù) 引用 查看   
  26. #26樓[樓主] 田志良      2011-03-02 18:04
    @ToBin
    不會導(dǎo)致這個問題,基礎(chǔ)系統(tǒng)緩沖區(qū)為每個Socket分配發(fā)送緩沖區(qū)和接受緩沖區(qū),這兩個不沖突。
     回復(fù) 引用 查看   
  27. #27樓 ToBin      2011-03-02 18:33
    要西,外瑞thank you !哈哈
     回復(fù) 引用 查看   
  28. #28樓[樓主] 田志良      2011-03-02 18:45
    @ToBin
    呵呵,不客氣。
     回復(fù) 引用 查看   
  29. #29樓 ToBin      2011-03-03 17:02
    呵呵,又碰到問題了,可能我太笨了,您在這篇文章“Socket服務(wù)器整體架構(gòu)概述”中說到一個“消息隊列調(diào)度器”,這個東西該怎么實現(xiàn),能大概說說嘛?
    謝謝了!呵呵
     回復(fù) 引用 查看   
  30. #30樓[樓主] 田志良      2011-03-04 09:03
    @ToBin
    呵呵,把你的郵箱發(fā)給我,這個周末我寫個Demo給你。
     回復(fù) 引用 查看   
  31. #31樓 ToBin      2011-03-04 09:26
    太感謝了,激動!我的郵箱:tuablove@126.com
    辛苦您了!
     回復(fù) 引用 查看   
  32. #32樓 ToBin      2011-03-07 10:07
    您的郵件已經(jīng)收到,思路已經(jīng)了解,非常感謝能得到您的幫助,希望能繼續(xù)得到您的幫助,思路也行,呵呵,非常感謝!
     回復(fù) 引用 查看   
  33. #33樓 ToBin      2011-03-08 17:26
    又碰到個問題,不知道怎么處理了,問題是這樣的
    entityData data=getdata();public void getdata(){var data=null;//...socket.sendasync()//...data=socket.receivesync();return data;}

    getdata方法是socket 發(fā)送命令,接收返回值的方法,因為socket 服務(wù)器發(fā)送,接受時分開的,我怎么在getdata中讓方法阻塞,讓服務(wù)器接收到命令,然后再把結(jié)果發(fā)送過來!類似于javascript 的ajax!這種東西該怎么寫啊?
    類似于使用memcache 里
    MemcachedClient mc = new MemcachedClient();mc.Get("key");

    這里面這個mc.Get("key") 方法是怎么實現(xiàn)的啊 ?
     回復(fù) 引用 查看   
  34. #34樓 ToBin      2011-03-09 21:02
    志良哥,您好,我在socket 開發(fā)中碰到了一些問題,想請教您,已經(jīng)發(fā)送您郵箱了,思路解說在郵件里,代碼在附件!等待您的回復(fù)!
     回復(fù) 引用 查看   
  35. #35樓 edwardxh      2011-11-16 09:33
    引用田志良:
    @ToBin
    呵呵,把你的郵箱發(fā)給我,這個周末我寫個Demo給你。

    樓主您好,不知您是否可以把這個“Socket服務(wù)器整體架構(gòu)概述”Demo也發(fā)給我一份,因為我最近也在研究Socket通信,很希望能得到您的技術(shù)心得分享,謝謝!
    我的郵箱:79668157@qq.com
     回復(fù) 引用 查看   
  36. #36樓 glf      2011-11-23 17:19
    現(xiàn)在公司要求能夠接受2W左右的服務(wù)端,每5秒訪問一次,高能不能給點意見啊
     回復(fù) 引用 查看   
  37. #37樓 鵬@      2012-01-12 17:03
    @ToBin
    大哥,能不能給小弟也發(fā)一個demo:lipeng1988011@126.com!!!
    多謝啦!??!
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
.net 3.5平臺上的Socket開發(fā)
C#使用SocketAsyncEventArgs操作套接字的簡單異步通訊
SocketAsyncEventArgs使用解說
TCP之深入淺出send和recv
這是一份很全很全的IO基礎(chǔ)知識與概念(應(yīng)用程序不能直接操作內(nèi)核空間需要將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間才能使用無論是read操作還是write操作都只能在內(nèi)核空間里執(zhí)行)
阿里P7二面:聊聊零拷貝的原理
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服