• <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>

            yehao's Blog

            套接字IO模型(一) Select模型

            http://www.cnblogs.com/NeuqUstcIim/archive/2008/08/14/1268023.html
            講一下套接字模式和套接字I/O模型的區(qū)別。先說(shuō)明一下,只針對(duì)Winsock,如果你要骨頭里挑雞蛋把UNIX下的套接字概念來(lái)往這里套,那就不關(guān)我的事。
            套接字模式阻塞套接字非阻塞套接字。或者叫同步套接字和異步套接字。
            套接字模型:描述如何對(duì)套接字的I/O行為進(jìn)行管理。
            Winsock提供的I/O模型一共有五種:

            select,WSAAsyncSelect,WSAEventSelect,Overlapped,Completion。今天先講解select。

            1:select模型擇模(選型)

            先看一下下面的這句代碼:

            阻塞socket

            int iResult = recv(s, buffer,1024);

               這是用來(lái)接收數(shù)據(jù)的,在默認(rèn)的阻塞模式下的套接字里,recv會(huì)阻塞在那里,直到套接字連接上有數(shù)據(jù)可讀,把數(shù)據(jù)讀到buffer里后recv函數(shù)才會(huì)返 回,不然就會(huì)一直阻塞在那里。

               在單線(xiàn)程的程序里出現(xiàn)這種情況會(huì)導(dǎo)致主線(xiàn)程(單線(xiàn)程程序里只有一個(gè)默認(rèn)的主線(xiàn)程)被阻塞,這樣整個(gè)程序被鎖死在這里,如果永 遠(yuǎn)沒(méi)數(shù)據(jù)發(fā)送過(guò)來(lái),那么程序就會(huì)被永遠(yuǎn)鎖死。這個(gè)問(wèn)題可以用多線(xiàn)程解決,但是在有多個(gè)套接字連接的情況下,這不是一個(gè)好的選擇,擴(kuò)展性很差。

            非阻塞 socket:

            再看代碼:
            int iResult = ioctlsocket(s, FIOBIO, (unsigned long *)&ul);
            iResult = recv(s, buffer,1024);

            //-------------------------
            // Initialize Winsock
            WSADATA wsaData;
            int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
            if (iResult != NO_ERROR)
              printf(
            "Error at WSAStartup()\n");

            //-------------------------
            // Create a SOCKET object.
            SOCKET m_socket;
            m_socket 
            = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (m_socket == INVALID_SOCKET) {
              printf(
            "Error at socket(): %ld\n", WSAGetLastError());
              WSACleanup();
              
            return;
            }

            //-------------------------
            // Set the socket I/O mode: In this case FIONBIO
            // enables or disables the blocking mode for the 
            // socket based on the numerical value of iMode.
            // If iMode = 0, blocking is enabled; 
            // If iMode != 0, non-blocking mode is enabled.
            int iMode = 0;
            ioctlsocket(m_socket, FIONBIO, (u_long FAR
            *&iMode);


            這一次recv的調(diào)用不管套接字連接上有沒(méi)有數(shù)據(jù)可以接收都會(huì)馬上返回。原因就在于我們用ioctlsocket把套接字設(shè)置為非阻塞模式了。不過(guò)你跟蹤 一下就會(huì)發(fā)現(xiàn),在沒(méi)有數(shù)據(jù)的情況下,recv確實(shí)是馬上返回了,但是也返回了一個(gè)錯(cuò)誤:WSAEWOULDBLOCK,意思就是請(qǐng)求的操作沒(méi)有成功完成。 看到這里很多人可能會(huì)說(shuō),那么就重復(fù)調(diào)用recv并檢查返回值,直到成功為止,但是這樣做效率很成問(wèn)題,開(kāi)銷(xiāo)太大

             

            多線(xiàn)程來(lái)解決使用阻塞套接字存在的問(wèn)題:

              多線(xiàn)程來(lái)解決阻塞套接字的方法是為阻塞套接字的IO操作創(chuàng)建單獨(dú)的線(xiàn)程,阻塞的套接字IO操作放在單獨(dú)的線(xiàn)程中,而不會(huì)因?yàn)樘捉幼諭O操作的阻塞造成整個(gè)主線(xiàn)程的阻塞,但是這樣也會(huì)造成一定的問(wèn)題:

            1) 如果是多個(gè)套接字的場(chǎng)合通過(guò)多線(xiàn)程來(lái)解決主線(xiàn)程阻塞就會(huì)顯得不合適了,server端創(chuàng)建一個(gè)監(jiān)聽(tīng)socket來(lái)負(fù)責(zé)監(jiān)聽(tīng)連接,而為accept函數(shù)

               為每個(gè)client端連接創(chuàng)建一個(gè)套接字,這樣就會(huì)創(chuàng)建很多的套接字。如果是創(chuàng)建不同的套接字則應(yīng)該創(chuàng)建多個(gè)線(xiàn)程,而每個(gè)線(xiàn)程的線(xiàn)程函數(shù)是

               不同的,這樣就造成了所謂的擴(kuò)展性很差。

            2)如果不是每個(gè)連接創(chuàng)建一個(gè)套接字的話(huà),duoxanch方法比較直觀,程序非常簡(jiǎn)單而且可移植性好,但是不能利用平臺(tái)相關(guān)的特性。例如,如 果連接數(shù)增多的時(shí)候(成千上萬(wàn)的連接),那么線(xiàn)程數(shù)成倍增長(zhǎng),操作 系統(tǒng)忙于頻繁的線(xiàn)程間切換,而且大部分線(xiàn)程在其生命周期內(nèi)都是處于非活動(dòng)狀態(tài)的,這大大浪費(fèi)了系統(tǒng)的資源。所以,如果你已經(jīng)知道你的代碼只會(huì)運(yùn)行在 Windows平臺(tái)上,建議采用Winsock I/O模型。

             

            微軟提供了select函數(shù)來(lái)解決這個(gè)問(wèn)題

             int select(
            int nfds,
            fd_set FAR *readfds,
            fd_set FAR *writefds,
            fd_set FAR *exceptfds,
            const struct timeval FAR *timeout
            );

            第一個(gè)參數(shù)不要管,會(huì)被系統(tǒng)忽略的。第二個(gè)參數(shù)是用來(lái)檢查套接字可讀性,也就說(shuō)檢查套接字上是否有數(shù)據(jù)可讀,同樣,第三個(gè)參數(shù)用來(lái)檢查數(shù)據(jù)是否可以發(fā)出。最后一個(gè)是檢查是否有帶外數(shù)據(jù)可讀取。

             

             

            最后一個(gè)參數(shù)是用來(lái)設(shè)置select等待多久的,是個(gè)結(jié)構(gòu):
            struct timeval {
            long tv_sec; // seconds
            long tv_usec; // and microseconds
            };
            如果將這個(gè)結(jié)構(gòu)設(shè)置為(0,0),那么select函數(shù)會(huì)馬上返回。

             說(shuō)了這么久,select的作用到底是什么?

            他的作用就是:

            1)防止在在阻塞模式的套接字里被鎖死

            2)避免在非阻塞套接字里重復(fù)檢查WSAEWOULDBLOCK錯(cuò)誤。

             

            他的工作流程如下:

            1:用FD_ZERO宏來(lái)初始化我們感興趣的fd_set,也就是select函數(shù)的第二三四個(gè)參數(shù)。
            2:用FD_SET宏來(lái)將套接字句柄分配給相應(yīng)的fd_set。
            3:調(diào)用select函數(shù)。
            4:用FD_ISSET對(duì)套接字句柄進(jìn)行檢查,如果我們所關(guān)注的那個(gè)套接字句柄仍然在開(kāi)始分配的那個(gè)fd_set里,那么說(shuō)明馬上可以進(jìn)行相應(yīng)的IO操 作。比如一個(gè)分配給select第一個(gè)參數(shù)的套接字句柄在select返回后仍然在select第一個(gè)參數(shù)的fd_set里,那么說(shuō)明當(dāng)前數(shù)據(jù)已經(jīng)來(lái)了, 馬上可以讀取成功而不會(huì)被阻塞

             

             1 #include "stdafx.h"
             2 #include <iostream>
             3 #include <winsock2.h>
             4 #include <windows.h>
             5 
             6 #define TRACE ATLTrace //必須要加上這個(gè)宏定義,否則在WIN32的控制臺(tái)程序中是不能直接用的
             7 
             8 #define InternetAddr "127.0.0.1"
             9 #define iPort 5055
            10 
            11 #pragma comment(lib, "ws2_32.lib")
            12 
            13 int _tmain(int argc, _TCHAR* argv[])
            14 {
            15     WSADATA wsa;
            16     WORD wVersionRequested;
            17     int err;
            18 
            19    wVersionRequested = MAKEWORD( 22 );
            20     err = WSAStartup( wVersionRequested, &wsa);
            21     if ( err != 0 ) {
            22     //Tell the user that we could not find a usable 
            23     //WinSock DLL.     
            24     TRACE("你忘記添加WinSock DLL了\n");
            25     WSACleanup();
            26     return 1;
            27      }
            28 
            29    // Create a SOCKET for listening for  incoming connection requests
            30     SOCKET fdServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            31    
            32     sockaddr_in server;
            33 
            34  server.sin_family = AF_INET;
            35  server.sin_addr.s_addr = inet_addr(InternetAddr);
            36  server.sin_port = htons(iPort);
            37  //Bind the socket.
            38     int ret = bind(fdServer, (sockaddr*)&server, sizeof(server));
            39     ret = listen(fdServer, 4);
            40 
            41     SOCKET AcceptSocket;
            42     fd_set     fdread;
            43  timeval    tv;
            44  int nSize;
            45    //其實(shí)也算是輪訓(xùn),那么對(duì)阻塞socket用select和對(duì)使用非阻塞socket的優(yōu)點(diǎn)在哪?
            46   //可能的優(yōu)點(diǎn)就是避免在非阻塞套接字里重復(fù)檢查WSAEWOULDBLOCK錯(cuò)誤。
            47     while(1)
            48   {
            49                  
            50          FD_ZERO(&fdread);//初始化fd_set
            51          FD_SET(fdServer, &fdread);//分配套接字句柄到相應(yīng)的fd_set
            52                              
            53         tv.tv_sec = 2;//這里我們打算讓select等待兩秒后返回,避免被鎖死,也避免馬上返回
            54         tv.tv_usec = 0;
            55                                                  
            56         select(0&fdread, NULL, NULL, &tv);
            57                                                          
            58         nSize = sizeof(server);
            59         //先判斷fdServer是否還在fd_set內(nèi)來(lái)判斷是否可以讀,這樣就避免因?yàn)?nbsp;accept在等待
            60         //時(shí)造成的阻塞
            61         if (FD_ISSET(fdServer, &fdread))
            62             //如果套接字句柄還在fd_set里,說(shuō)明客戶(hù)端已經(jīng)有connect的請(qǐng)求發(fā)過(guò)來(lái)了,
            63             //馬上可以accept成功
            64          {
            65              AcceptSocket = accept(fdServer,( sockaddr*&server, &nSize);
            66              break;
            67            }                                             
            68         else
            69         //還沒(méi)有客戶(hù)端的connect請(qǐng)求,我們可以去做別的事,避免像沒(méi)有用select方式
            70         //的阻塞套接字程序被鎖死的情況,如果沒(méi)用select,當(dāng)程序運(yùn)行到accept的時(shí)候客戶(hù)
            71         //端恰好沒(méi)有connect請(qǐng)求,那么程序就會(huì)被鎖死,做不了任何事情
            72             {
            73             //do something
            74                MessageBox(NULL, "waiting""recv", MB_ICONINFORMATION);
            75         //別的事做完后,繼續(xù)去檢查是否有客戶(hù)端連接請(qǐng)求
            76             }
            77    }
            78 
            79    char buffer[128];
            80       ZeroMemory(buffer, 128);
            81 
            82          ret = recv(AcceptSocket,buffer,128,0);//這里同樣可以用select,用法和上面一樣
            83 
            84          MessageBox(NULL, buffer, "recv", MB_ICONINFORMATION);
            85 
            86         closesocket(AcceptSocket);
            87         WSACleanup();
            88         return 0;
            89 }
            90 

             

            select函數(shù)的返回值 :

            函數(shù)失敗的返回值:調(diào)用失敗返回SOCKET_ERROR,超時(shí)返回0。

             

            int ret;
                    
            if((ret=select(0,&fdread,NULL,NULL,NULL))==SOCKET_ERROR)
                    {
                        
            //Error Condition
                    }
                    
            if(ret > 0)//ret>0這個(gè)ret值表示滿(mǎn)足條件的socket的數(shù)量,不止一個(gè)socket滿(mǎn)足IO操作的條件
                    {
                        
            if(FD_ISSET(fdServer,&fdread))
                        {
                            
            //A read event has occured on socket fdServer
                        }
                    }

            posted on 2011-08-18 16:11 厚積薄發(fā) 閱讀(500) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): 網(wǎng)絡(luò)編程

            導(dǎo)航

            <2025年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            統(tǒng)計(jì)

            常用鏈接

            留言簿

            隨筆分類(lèi)

            文章分類(lèi)

            文章檔案

            搜索

            最新評(píng)論

            久久综合亚洲色HEZYO社区| 国产精品久久久久国产A级| 久久久久亚洲精品无码网址| 色悠久久久久久久综合网| 综合久久精品色| 99久久精品费精品国产一区二区| 国产精品成人99久久久久91gav | 久久久无码精品亚洲日韩京东传媒| 中文国产成人精品久久不卡| 波多野结衣中文字幕久久| Xx性欧美肥妇精品久久久久久| 久久婷婷色综合一区二区| 久久99精品久久久久子伦| 国产精品热久久毛片| 无码日韩人妻精品久久蜜桃 | 亚洲成色www久久网站夜月| 久久综合狠狠综合久久激情 | 久久亚洲AV成人无码软件| 91精品国产综合久久香蕉| 麻豆一区二区99久久久久| 亚洲精品久久久www| 狠狠色丁香婷婷综合久久来来去| 97精品伊人久久大香线蕉app| 99精品国产99久久久久久97| 精品久久人人做人人爽综合| 久久综合久久综合久久| 99久久婷婷国产综合亚洲| 新狼窝色AV性久久久久久| 久久综合九色综合网站| 国产精品久久久香蕉| 欧美国产精品久久高清| 久久九色综合九色99伊人| 国产福利电影一区二区三区,免费久久久久久久精 | 久久久久无码精品| A级毛片无码久久精品免费| 日本一区精品久久久久影院| 国产99久久精品一区二区| 99久久久精品免费观看国产| 久久99国产精品一区二区| 欧美久久综合性欧美| 99久久伊人精品综合观看|