青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Error

C++博客 首頁 新隨筆 聯系 聚合 管理
  217 Posts :: 61 Stories :: 32 Comments :: 0 Trackbacks
相關UML:
網絡引擎整體結構:


SocketItem細節:



先來看幾個底層結構:
//重疊結構類
class COverLapped
{
    
//變量定義
public:
    WSABUF                            m_WSABuffer;                        
//數據指針
    OVERLAPPED                        m_OverLapped;                        //重疊結構
    const enOperationType            m_OperationType;                    //操作類型

    
//函數定義
public:
    
//構造函數
    COverLapped(enOperationType OperationType);
    
//析構函數
    virtual ~COverLapped();

    
//信息函數
public:
    
//獲取類型
    enOperationType GetOperationType() { return m_OperationType; }
};

//接收重疊結構
class COverLappedSend : public COverLapped
{
    
//數據變量
public:
    BYTE                            m_cbBuffer[SOCKET_BUFFER];            
//數據緩沖

    
//函數定義
public:
    
//構造函數
    COverLappedSend();
    
//析構函數
    virtual ~COverLappedSend();
};

//重疊結構模板
template <enOperationType OperationType> class CATLOverLapped : public COverLapped
{
    
//函數定義
public:
    
//構造函數
    CATLOverLapped() : COverLapped(OperationType) {}
    
//析構函數
    virtual ~CATLOverLapped() {}
};


先復習下基礎,Windows下的網絡模型有很多種,這里只拿出三種來說:
EventSelect:基于信號機制,以socket為單位綁定信號量,當socket上有指定的事件發生時激發信號,然后查詢事件處理事件重設事件,繼續在信號量上等待。其實也是在伯克利select模型上的換不換藥的加強。
OverLapped:分兩種工作模式完成回調,和完成事件。重疊IO監視每次操作,每次IO都綁定一個重疊對象,當操作完成以后激發信號或者調用回調。
IOCP:和overlapped類似,不過結果經過了Windows的預處理以隊列的形式掛在完成端口上

根據上面的復習,可以得出一個結論,IOCP環境中每一次IO操作都需要一個重疊結構,那么一個CServerSocketItem至少需要如些這些東東:
他要接受數據,所以必須有一個接受數據的 OverLapped
它要發送數據,說以必須有一個發送數據的 OverLapped
netFox對OverLapped做了使用了類似池的的管理手段,他的Send都是不等待上一次完成就直接投遞下一個請求了,,,這是很操蛋的做法,,,

然后繼續復習下基礎:
在EventSelect模型中獲處理件類型流程是這樣:
event受信,使用::WSAEnumNetworkEvents查詢和這個event關聯的socket發生的事件,根據查詢到的事件類型去處理事件
在以每一次IO為查詢對象重疊IO、IOCP模型中是這樣:
使用GetOverlappedResult 或者 GetQueuedCompletionStatus然后根據重疊結構去查詢投遞的是什么類型的操作,然后找到關聯的socket去操作,,,

這樣必然要給OverLapped做個擴展,提供一種通過OverLapped查詢操作類型和socket的能力。
通過分析代碼,netFox關聯socket是通過在創建完成端口的時候綁定SocketItem對象指針完成的,操作類型是通過對OverLapped結構加強完成的。
通過GetQueuedCompletionStatus獲取到完成OverLapped以后使用一個宏:
(這是COverLapped類型)  pSocketLapped=CONTAINING_RECORD(pOverLapped,COverLapped,m_OverLapped);
來獲取包裝后的OverLapped,然后獲取操作類型,然后執行具體操作。
其實宏的展開如下:
(COverLapped*)((BYTE*)pOverLapped - (COverLapped*)(0)->m_OverLapped);
pOverLapped是獲取到的某個COverLapped中的成員變量,(COverLapped*)(0)->m_OverLapped是到在COverLapped中的偏移,((BYTE*)pOverLapped - (COverLapped*)(0)->m_OverLapped) 就是根據pOverLapped推算出來的包含地址為pOverLapped作為成員變量m_OverLapped的COverLapped對象的地址。
然后就分別調用:

//發送完成函數
bool CServerSocketItem::OnSendCompleted(COverLappedSend * pOverLappedSend, DWORD dwThancferred);

//接收完成函數
bool CServerSocketItem::OnRecvCompleted(COverLappedRecv * pOverLappedRecv, DWORD dwThancferred);

為毛要區分Send OverLapped 和 Recv OverLapped呢,,,
應為投遞一次Send不一定是瞬間完成的,在處理的過程中存儲數據的內存應該是鎖定的,也就是不允許修改的,,,所以OverLapped應該自己管理內存。
而recv應該也是需要有一片內存直接接受數據的,很奇怪netFox沒有提供,,,

recv居然是在投遞接受請求的時候給了一個空的buffer,然后在完成回調中自己再次調用recv方法接受數據。
接受有關的成員變量如下:
    //狀態變量
protected:
    
bool                            m_bNotify;                            //通知標志
    bool                            m_bRecvIng;                            //接收標志
    bool                            m_bCloseIng;                        //關閉標志
    bool                            m_bAllowBatch;                        //接受群發
    WORD                            m_wRecvSize;                        //接收長度
    BYTE                            m_cbRecvBuf[SOCKET_BUFFER*5];        //接收緩沖
int iRetCode=recv(m_hSocket,(char *)m_cbRecvBuf+m_wRecvSize,sizeof(m_cbRecvBuf)-m_wRecvSize,0);
難道這么蠢的做法只是為了躲開分包算法?
具體的看看接受代碼:
//接收完成函數
bool CServerSocketItem::OnRecvCompleted(COverLappedRecv * pOverLappedRecv, DWORD dwThancferred)
{
    
//效驗數據
    ASSERT(m_bRecvIng==true);

    
//設置變量
    m_bRecvIng=false;
    m_dwRecvTickCount
=GetTickCount();

    
//判斷關閉
    if (m_hSocket==INVALID_SOCKET)
    {
        CloseSocket(m_wRountID);
        
return true;
    }

    
//接收數據
    int iRetCode=recv(m_hSocket,(char *)m_cbRecvBuf+m_wRecvSize,sizeof(m_cbRecvBuf)-m_wRecvSize,0);
    
if (iRetCode<=0)
    {
        CloseSocket(m_wRountID);
        
return true;
    }

    
//接收完成
    m_wRecvSize+=iRetCode;
    BYTE cbBuffer[SOCKET_BUFFER];
    CMD_Head 
* pHead=(CMD_Head *)m_cbRecvBuf;

    
//處理數據
    try
    {
        
while (m_wRecvSize>=sizeof(CMD_Head))
        {
            
//效驗數據
            WORD wPacketSize=pHead->CmdInfo.wDataSize;
            
if (wPacketSize>SOCKET_BUFFER) throw TEXT("數據包超長");
            
if (wPacketSize<sizeof(CMD_Head)) throw TEXT("數據包非法");
            
if (pHead->CmdInfo.cbMessageVer!=SOCKET_VER) throw TEXT("數據包版本錯誤");
            
if (m_wRecvSize<wPacketSize) break;

            
//提取數據
            CopyMemory(cbBuffer,m_cbRecvBuf,wPacketSize);
            WORD wRealySize
=CrevasseBuffer(cbBuffer,wPacketSize);
            ASSERT(wRealySize
>=sizeof(CMD_Head));
            m_dwRecvPacketCount
++;

            
//解釋數據
            WORD wDataSize=wRealySize-sizeof(CMD_Head);
            
void * pDataBuffer=cbBuffer+sizeof(CMD_Head);
            CMD_Command Command
=((CMD_Head *)cbBuffer)->CommandInfo;

            
//內核命令
            if (Command.wMainCmdID==MDM_KN_COMMAND)
            {
                
switch (Command.wSubCmdID)
                {
                
case SUB_KN_DETECT_SOCKET:    //網絡檢測
                    {
                        
break;
                    }
                
defaultthrow TEXT("非法命令碼");
                }
            }
            
else 
            {
                
//消息處理
                m_pIServerSocketItemSink->OnSocketReadEvent(Command,pDataBuffer,wDataSize,this);            
            }

            
//刪除緩存數據
            m_wRecvSize-=wPacketSize;
            MoveMemory(m_cbRecvBuf,m_cbRecvBuf
+wPacketSize,m_wRecvSize);
        }
    }
    
catch ()
    { 
        CloseSocket(m_wRountID);
        
return false;
    }

    
return RecvData();
}

這是還是有分包算法的,總的來說接受流程如下:
直接使用recv把數據接受到SocketItem的緩沖區中,當長度大于CMD_HEAD之后,進入處理階段,處理head數據各種判斷,然后將數據扔出去,再調整緩沖區,,,

簡單的說:
Send完全不考慮同步問題,不管一個勁的網隊列投遞Send請求,,,這邊處理隊列也是直接Send完事,完全不考慮上一次是否send成功,,,
Recv更是莫名其妙的使用完成端口繞一圈還回到recv直接接受了,,,

很狗血的做法,,,

更正下我自己狗血的不理解:
如果一個服務器提交了非常多的重疊的receive在每一個連接上,那么限制會隨著連接數的增長而變化。如果一個服務器能夠預先估計可能會產生的最大并發連接數,服務器可以投遞一個使用零緩沖區的receive在每一個連接上。因為當你提交操作沒有緩沖區時,那么也不會存在內存被鎖定了。使用這種辦法后,當你的receive操作事件完成返回時,該socket底層緩沖區的數據會原封不動的還在其中而沒有被讀取到receive操作的緩沖區來。此時,服務器可以簡單的調用非阻塞式的recv將存在socket緩沖區中的數據全部讀出來,一直到recv返回 WSAEWOULDBLOCK 為止。 這種設計非常適合那些可以犧牲數據吞吐量而換取巨大 并發連接數的服務器。當然,你也需要意識到如何讓客戶端的行為盡量避免對服務器造成影響。在上一個例子中,當一個零緩沖區的receive操作被返回后使 用一個非阻塞的recv去讀取socket緩沖區中的數據,如果服務器此時可預計到將會有爆發的數據流,那么可以考慮此時投遞一個或者多個receive 來取代非阻塞的recv來進行數據接收。(這比你使用1個缺省的8K緩沖區來接收要好的多。)

源碼中提供了一個簡單實用的解決WSAENOBUF錯誤的辦法。我們執行了一個零字節緩沖的異步WSARead(...)(參見 OnZeroByteRead(..))。當這個請求完成,我們知道在TCP/IP棧中有數據,然后我們通過執行幾個有MAXIMUMPACKAGESIZE緩沖的異步WSARead(...)去讀,解決了WSAENOBUFS問題。但是這種解決方法降低了服務器的吞吐量。

總結:

解決方法一:

投遞使用空緩沖區的 receive操作,當操作返回后,使用非阻塞的recv來進行真實數據的讀取。因此在完成端口的每一個連接中需要使用一個循環的操作來不斷的來提交空緩沖區的receive操作。

解決方法二:

在投遞幾個普通含有緩沖區的receive操作后,進接著開始循環投遞一個空緩沖區的receive操作。這樣保證它們按照投遞順序依次返回,這樣我們就總能對被鎖定的內存進行解鎖。



///////////
如果一個服務器同時連接了許多客戶端, 對每個客戶端又調用了許多 WSARecv, 那么大量的內存將會被鎖定到非分頁內存池. 鎖定這些內存時是按照頁面邊界來鎖定的, 也就是說即使你 WSARecv 的緩存大小是 1 字節, 被鎖定的內存也將會是 4k. 非分頁內存池是由整個系統共用的, 如果用完的話最壞的情況就是系統崩潰. 一個解決辦法是, 使用大小為 0 的緩沖區調用 WSARecv. 等到調用成功時再換用非阻塞的 recv 接收到來的數據, 直到它返回 WSAEWOULDBLOCK 表明數據已經全部讀完. 在這個過程中沒有任何內存需要被鎖定, 但壞處是效率稍低.
posted on 2011-06-07 23:28 Enic 閱讀(2286) 評論(0)  編輯 收藏 引用 所屬分類: 網狐棋牌源碼分析
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美高清视频一区| 一区二区三区蜜桃网| 国产亚洲激情视频在线| 你懂的视频一区二区| 黄色一区二区三区| 欧美 日韩 国产一区二区在线视频| 亚洲尤物精选| 性刺激综合网| 9久草视频在线视频精品| 欧美电影在线观看| 奶水喷射视频一区| 99国产精品久久久久久久久久 | 欧美成人综合网站| 91久久极品少妇xxxxⅹ软件| 最新高清无码专区| 免费看精品久久片| 久久人人爽人人爽| 欧美日韩视频一区二区| 欧美在线视频二区| 欧美在线观看视频| 亚洲视频免费在线| 久久国产精品网站| 一区二区三区久久精品| 欧美一区二区性| 亚洲国产网站| 久久国产夜色精品鲁鲁99| 宅男精品视频| 欧美国产激情| 亚洲二区免费| 精品二区视频| 亚洲男女自偷自拍图片另类| 亚洲精品一区二区三区不| 久久综合999| 免费高清在线一区| 亚洲国产精品va在线看黑人动漫| 影音先锋另类| 99精品99久久久久久宅男| 激情亚洲网站| 欧美中在线观看| 欧美自拍偷拍午夜视频| 国产精品久在线观看| 亚洲日本欧美日韩高观看| 亚洲国产精品成人综合色在线婷婷| 久久精品观看| 欧美本精品男人aⅴ天堂| 亚洲第一福利在线观看| 久久夜色精品国产欧美乱极品| 久久青青草综合| 1024欧美极品| 欧美国产欧美亚洲国产日韩mv天天看完整| 久久综合九色综合久99| 狠狠色综合色区| 欧美日韩国产小视频| 亚洲欧美久久久久一区二区三区| 久久在线免费| 99香蕉国产精品偷在线观看| 国产精品久久久久9999| 欧美在线亚洲| 欧美r片在线| 一片黄亚洲嫩模| 国产日韩一区二区三区| 欧美不卡视频一区发布| 亚洲午夜精品国产| 欧美xx69| 久久―日本道色综合久久| 正在播放亚洲一区| 99国产欧美久久久精品| 国产一级久久| 国产乱码精品一区二区三区五月婷| 美女诱惑一区| 久久不见久久见免费视频1| 亚洲视频在线观看免费| 亚洲激情电影在线| 亚洲国产另类精品专区| 国产精品入口麻豆原神| 欧美日韩视频专区在线播放| 欧美日韩国产区| 欧美少妇一区二区| 国产精品每日更新| 欧美偷拍另类| 国产精品老女人精品视频| 欧美黄色aaaa| 欧美午夜精品久久久久免费视 | 欧美大尺度在线观看| 中文精品视频| 欧美中文字幕视频| 久久久蜜臀国产一区二区| 久久精品1区| 美国十次成人| 亚洲欧洲一区二区三区在线观看 | 亚洲欧洲精品一区二区三区不卡 | 国产欧美日韩免费看aⅴ视频| 欧美日韩成人精品| 国产精品免费观看在线| 国产麻豆午夜三级精品| 国产主播喷水一区二区| 亚洲欧洲日本专区| 午夜精品一区二区三区在线播放| 久久天堂国产精品| 亚洲色图综合久久| 久色成人在线| 国产一区深夜福利| 亚洲国产老妈| 亚洲欧洲在线一区| 午夜精品久久久久久久久久久| 欧美一区免费视频| 一本色道久久综合狠狠躁篇的优点| 中文高清一区| 亚洲国产欧美一区| 欧美sm极限捆绑bd| 亚洲国产片色| 欧美成人精品1314www| 欧美一乱一性一交一视频| 欧美视频1区| 亚洲欧美国产日韩天堂区| 亚洲国产裸拍裸体视频在线观看乱了| 亚洲欧美激情四射在线日 | 午夜性色一区二区三区免费视频 | 欧美一乱一性一交一视频| 亚洲国产三级网| 欧美日韩久久不卡| 99re在线精品| 亚洲理论在线观看| 欧美体内she精视频在线观看| 亚洲图片欧洲图片av| 亚洲欧美成人网| 在线观看欧美视频| 91久久精品一区二区别| 欧美色另类天堂2015| 亚洲欧美在线看| 久久久久久久综合| 亚洲剧情一区二区| 亚洲国产欧美日韩精品| 国产精品高潮在线| 免费美女久久99| 欧美日韩一区二区在线观看| 久久国产精品久久国产精品| 久久久之久亚州精品露出| 一级日韩一区在线观看| 久久精品免费电影| 一区二区精品在线观看| 亚洲一区在线免费观看| 国产亚洲一区二区三区在线观看 | 国产噜噜噜噜噜久久久久久久久 | 久久蜜桃香蕉精品一区二区三区| 国产一区清纯| 亚洲精品社区| 99视频在线精品国自产拍免费观看| 国产欧美一区二区三区视频| 噜噜噜躁狠狠躁狠狠精品视频 | 亚洲国产精品久久久久秋霞不卡 | 欧美成人免费全部观看天天性色| 亚洲午夜精品久久| 蜜桃av噜噜一区| 久久久久国色av免费观看性色| 亚洲永久视频| 欧美午夜精品久久久久久人妖 | 亚洲免费观看视频| 亚洲国产黄色片| 欧美激情1区2区3区| 欧美电影免费观看高清| 国产精品亚洲一区| 亚洲一区二区三区国产| 黄色成人免费网站| 91久久综合亚洲鲁鲁五月天| 久久精品在线播放| 久久综合婷婷| 曰韩精品一区二区| 久久久久久久久久久成人| 欧美黑人一区二区三区| 亚洲毛片av| 国产午夜精品理论片a级探花| 性做久久久久久久久| 欧美成人精品| 午夜视频在线观看一区二区| 国产嫩草一区二区三区在线观看| 亚洲免费在线视频一区 二区| 欧美怡红院视频| 亚洲激情自拍| 国产精品理论片| 免费永久网站黄欧美| 亚洲色在线视频| 亚洲精品日本| 亚洲国产岛国毛片在线| 久久精品亚洲一区| 亚洲免费影视第一页| 亚洲精品日韩激情在线电影| 国产欧美日本一区视频| 欧美日韩一区在线观看| 欧美激情无毛| 欧美日韩国产精品自在自线| 久久一二三国产| 久久综合电影| 久色婷婷小香蕉久久| 久久视频国产精品免费视频在线 | 亚洲一区观看| 亚洲一区二区三区高清 | 亚洲欧美日韩一区二区| 亚洲人成网站色ww在线 | 在线亚洲自拍|