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

            kenlistian

            厚積薄發(fā). 勤為槳,思為帆

               :: 首頁(yè) :: 新隨筆 ::  :: 聚合  :: 管理 ::
              73 隨筆 :: 4 文章 :: 22 評(píng)論 :: 0 Trackbacks

            未命名

            在其中,要注意的是:

              1.關(guān)于服務(wù)端在客戶端連接之前

                  如果沒(méi)有客戶端連接時(shí),在調(diào)用accept()時(shí)程序?qū)?huì)出現(xiàn)freeze,即阻塞,而一旦有客戶端連接過(guò)來(lái),accept將會(huì)新建一Socket與客戶端的Socket相通,原先Socket繼續(xù)進(jìn)入監(jiān)聽(tīng)狀態(tài),等待他人的連接要求。
                 該函數(shù)調(diào)用成功返回一個(gè)新產(chǎn)生的Socket對(duì)象,否則返回INVALID_SOCKET,該新socket就是一個(gè)和一個(gè)客戶端對(duì)話的套接字。有點(diǎn)類似孫悟空對(duì)陣天兵天將時(shí),當(dāng)遇到某個(gè)具體敵手來(lái),拔出一根毫毛變出一個(gè)孫行者去應(yīng)付,再來(lái)再拔毫毛一樣。accept()定義如下:
            SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen );
            s:Socket的識(shí)別碼;
            addr:存放來(lái)連接的客戶端的地址;
            addrlen:addr的長(zhǎng)度

              當(dāng)客戶端連接上后一直沒(méi)有斷開(kāi)情況下,如果連接越來(lái)越多時(shí),則創(chuàng)建的Socket也越多,其最大上限在listen中已經(jīng)設(shè)置。

            2.關(guān)于服務(wù)端的accept()使用

              accept過(guò)后才是真正的和客戶端進(jìn)行交互,在accept時(shí),由于程序會(huì)freeze,在調(diào)用accept時(shí)有多種方法,其中方法有:

            *事件處理模式:

                 通過(guò)WSAAsyncSelect()函數(shù),其異步通知有accept信號(hào)來(lái),然后在一個(gè)窗體自定義事件中處理accept信號(hào)。   

               如下在listen()之后調(diào)用:

                    WSAAsyncSelect(m_hSocket, m_hWnd, WM_CLIENT_ACCEPT,FD_ACCEPT);  //wm_xxx_xxz自定義消息。

              這樣在構(gòu)建的自定義消息中處理accept()連接請(qǐng)求。如下,OnAccept()單元

                   LRESULT CPublicNetSoftDlg::OnAccept(WPARAM wParam,LPARAM lParam)
                   {

                         。。。。
                           if(WSAGETSELECTEVENT(lParam) == FD_ACCEPT)//如果
                            {
                                     Client = accept(ServerSocket,(LPSOCKADDR)&m_sockServerAddr,0);

                                    if (Client == INVALID_SOCKET) 
                                        return 0L;
                            }

                        。。。。。
                }

            *線程處理模式:將accept放在線程中讓其freeze,一旦來(lái)了連接,則自然從freeze中出來(lái)進(jìn)行處理下一步。下面就是直接把a(bǔ)ccetp放在線程中處于等待狀態(tài)。

            //連接請(qǐng)求隊(duì)列長(zhǎng)度為1,即只允許有一個(gè)請(qǐng)求,若有多個(gè)請(qǐng)求, 則出現(xiàn)錯(cuò)誤,給出錯(cuò)誤代碼WSAECONNREFUSED。
            listen(sock,1);
            //開(kāi)啟線程避免主程序的阻塞
            AfxBeginThread(Server,NULL);
            ……

            //處理線程,等待客戶端連接
            UINT Server(LPVOID lpVoid)
            {
            ……
               int nLen = sizeof(SOCKADDR);
               connSocket = accept(ListSocket,(LPSOCKADDR)& sockin,(LPINT)& nLen);
               ……
                WSAAsyncSelect(connSocket,
                             m_hWnd,
                             WM_SOCKET_MSG,
                             FD_READ|FD_CLOSE);
                return 1;
            }
              把a(bǔ)ccept()放到線程中去是因?yàn)樵趫?zhí)行到該函數(shù)時(shí)如沒(méi)有客戶連接請(qǐng)求到來(lái),服務(wù)器就會(huì)停在accept語(yǔ)句上處于等待阻塞,這勢(shì)必會(huì)引起進(jìn)程的阻塞,雖然也可以通過(guò)設(shè)置套接字為非阻塞方式使在沒(méi)有客戶等待時(shí)可以使accept()函數(shù)調(diào)用立即返回,但這種輪詢套接字的方式會(huì)使CPU處于忙等待方式,從而降低程序的運(yùn)行效率大大浪費(fèi)系統(tǒng)資源(我覺(jué)得做法很多,暫不考慮非阻塞情況)。
                  在阻塞工作方式,為其單獨(dú)開(kāi)辟一個(gè)子線程,將其阻塞控制在子線程范圍內(nèi)而不會(huì)造成整個(gè)應(yīng)用程序的阻塞。對(duì)于網(wǎng)絡(luò)事件的響應(yīng)顯然要采取異步選擇機(jī)制,只有采取這種方式才可以在由網(wǎng)絡(luò)對(duì)方所引起的不可預(yù)知的網(wǎng)絡(luò)事件發(fā)生時(shí)能馬上在進(jìn)程中做出及時(shí)的響應(yīng)處理,而在沒(méi)有網(wǎng)絡(luò)事件到達(dá)時(shí)則可以處理其他事件,這種效率是很高的。前面那段代碼中的WSAAsyncSelect()函數(shù)便是實(shí)現(xiàn)網(wǎng)絡(luò)事件異步選擇的核心函數(shù)。
                第4個(gè)參數(shù)注冊(cè)應(yīng)用程序關(guān)心的網(wǎng)絡(luò)事件,在這里通過(guò)FD_READ|FD_CLOSE指定了網(wǎng)絡(luò)讀和網(wǎng)絡(luò)斷開(kāi)兩種事件,當(dāng)這種事件發(fā)生時(shí)變會(huì)發(fā)出由第三個(gè)參數(shù)指定的自定義消息 WM_SOCKET_MSG,接收該消息的窗口通過(guò)第二個(gè)參數(shù)指定其句柄。

            其響應(yīng)函數(shù)如下:

            void CNetServerView::OnSocket(WPARAM wParam,LPARAM lParam)
            {
                int iReadLen=0;
               int message=lParam & 0x0000FFFF;
               switch(message)
               {
                 case FD_READ:     //讀事件發(fā)生。此時(shí)有字符到達(dá),需要進(jìn)行接收處理
                    char cDataBuffer[MTU*10];
                    //通過(guò)套接字接收信息
                    iReadLen = recv(newskt,cDataBuffer,MTU*10,0);
                   //將信息保存到文件
                    if(!file.Open("ServerFile.txt",CFile::modeReadWrite))
                         file.Open("E:ServerFile.txt",
                             CFile::modeCreate|CFile::modeReadWrite);
                     file.SeekToEnd();
                     file.Write(cDataBuffer,iReadLen);
                     file.Close();
                     break;
                     case FD_CLOSE://網(wǎng)絡(luò)斷開(kāi)事件發(fā)生。此時(shí)客戶機(jī)關(guān)閉或退出。
                         ……//進(jìn)行相應(yīng)的處理
                            break;
                     default:
                           break;
                  }
            }

               對(duì)于recv和send的處理一般就是在事件中處理,通過(guò)WSAAsySelect()來(lái)傳遞信號(hào),這種方式形成一種固定寫socket方式,比如有的人喜歡把recv和send各自放入一個(gè)線程中通過(guò)輪詢+阻塞模式,或者采用事件通知模式,一般來(lái)說(shuō)采用I/O模型是較為專業(yè)的做法。

            3.客戶端的connect()

                客戶端連接時(shí)存在阻塞現(xiàn)象,就是程序在connect會(huì)出現(xiàn)freeze,一般可以容忍。但若想通過(guò)超時(shí)設(shè)置來(lái)解決這個(gè)問(wèn)題,可采用在vckbase中,對(duì)于connect()超時(shí)的處理辦法。不過(guò)覺(jué)得有時(shí)調(diào)用封裝好的socket,直接是指connectionTimeout屬性倒是簡(jiǎn)單的方法。

            WSADATA wsd;
            SOCKET cClient;
            int ret;
            struct sockaddr_in server;
            hostent *host=NULL;

            if(WSAStartup(MAKEWORD(2,0),&wsd))

            {

               return 0;

            }
            cClient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            if(cClient == INVALID_SOCKET){return 0;}
            //set Recv and Send time out
            int TimeOut=6000; //設(shè)置發(fā)送超時(shí)6秒
            if(::setsockopt(cClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
                 return 0;
            }
            TimeOut = 6000;//設(shè)置接收超時(shí)6秒
            if(::setsockopt(cClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
            return 0;
            }
            //設(shè)置非阻塞方式連接
            unsigned long ul = 1;
            ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul);
            if(ret==SOCKET_ERROR)  return 0;

            //連接
            server.sin_family = AF_INET;
            server.sin_port = htons(25);
            server.sin_addr .s_addr = inet_addr((LPCSTR)pSmtp);
            if(server.sin_addr.s_addr == INADDR_NONE){return 0;}

            //運(yùn)行這里將不會(huì)阻塞,而是直接運(yùn)行下去,通過(guò)select中設(shè)置的 timeval結(jié)構(gòu)參數(shù)設(shè)定連接超時(shí)處理。

            connect(cClient,(const struct sockaddr *)&server,sizeof(server));

            //select 模型,即設(shè)置超時(shí)
            struct timeval timeout ;
            fd_set r;

            FD_ZERO(&r);
            FD_SET(cClient, &r);
            timeout.tv_sec = 15; //連接超時(shí)15秒
            timeout.tv_usec =0;
            ret = select(0, 0, &r, 0, &timeout);            //超時(shí)socket將關(guān)閉
            if ( ret <= 0 )
            {
                     ::closesocket(cClient);
                      return 0;
            }
            //一般非鎖定模式套接比較難控制,可以根據(jù)實(shí)際情況考慮 再設(shè)回阻塞模式,又把狀態(tài)設(shè)置為 阻塞狀態(tài)。
            unsigned long ul1= 0 ;
            ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul1);
            if(ret==SOCKET_ERROR){
                     ::closesocket (cClient);
                      return 0;
            }

             

            4.select()

            a. 當(dāng)你希望服務(wù)器監(jiān)聽(tīng)連接服務(wù)請(qǐng)求,而又不想通過(guò)輪詢的方式,則理想的方式是調(diào)用select().它運(yùn)行你把程序本身掛起來(lái),而同時(shí)使系統(tǒng)內(nèi)核監(jiān)聽(tīng)所要求的一組文件描述符的任何活動(dòng),只要確認(rèn)在任何被監(jiān)控的文件描述符上出現(xiàn)了活動(dòng),select()調(diào)用將返回指示該文件描述符已準(zhǔn)備好的信息,從而實(shí)現(xiàn)了程序的選出是隨機(jī)變化的,而不必由程序本身對(duì)輸入進(jìn)行測(cè)試而浪費(fèi)cpu開(kāi)銷,

                在socket編程中,select函數(shù)一般在非阻塞的socket中,用來(lái)檢查socket緩沖區(qū)中是否有數(shù)據(jù)可讀,或是否可以寫數(shù)據(jù)到socket緩沖區(qū)。  
              有時(shí),select()也被用來(lái)當(dāng)作延時(shí)函數(shù)使用。sleep()延時(shí)會(huì)釋放cpu,用select的話,可以在占用cpu的情況下,延時(shí)。

               select()是用來(lái)進(jìn)行多路轉(zhuǎn)接的函數(shù)。它可以同時(shí)等待n(n大于等于1)個(gè)文件描述字或者socket套接口。只要它等待的任意描述字準(zhǔn)備好或者等待時(shí)間超過(guò)了設(shè)定時(shí)間程序就往下執(zhí)行。可以防止進(jìn)程長(zhǎng)時(shí)間阻塞,占用資源。

            b.簡(jiǎn)單說(shuō)法:

              如果你要發(fā)數(shù)據(jù)用select(sock+1,&s,NULL,NULL,NULL);  
                 if(FD_ISSET(sock,&s)   ,你可以發(fā)了。send   it  
              如果你要收數(shù)據(jù)用select(sock+1,NULL,&s,NULL,NULL);  
              if(FD_ISSET(sock,&s)   ,你可以收了。recv   it  

            socket默認(rèn)情況下是阻塞的,除非你用WSAAsyncSelect   OR   select   就變成NOBBLOCKING,  
              將阻塞設(shè)為非阻塞如下:  
              int   opt=1;  
              ioctlsocket(sock,FIONBIO,&opt)  
              若opt=0就是阻塞的了

             

            c.用法一

              fd_set m_readfds;  
              fd_set m_exceptfds;  
              timeval m_tmOut;  
              m_tmOut.tv_sec =   120;     //接收時(shí)間如果超過(guò)120秒,即認(rèn)為網(wǎng)絡(luò)連接已經(jīng)中斷,  
              m_tmOut.tv_usec =   0;      //客戶端應(yīng)該定時(shí)每40秒發(fā)送一次空閑信號(hào),以防止被誤認(rèn)為是網(wǎng)絡(luò)連接中斷。  
              FD_ZERO(   &m_readfds   );  
              FD_ZERO(   &m_exceptfds   );  
              FD_SET(   m_scSocket,   &m_exceptfds   );  
              FD_SET(   m_scSocket,   &m_readfds   );  
              int CNet::Receive(   char   *   szBuff,   int   iSize   )  
              {  
                    int   iRet;  
                    if(   m_ntType   ==   _NET_SERVER_   )  
                    {  
                          iRet   =   select(   m_scSocket   +   1,   &m_readfds,   NULL,   &m_exceptfds,   &m_tmOut   );  
                          if(   iRet   ==   0   )  
                         {  
                               m_iError =   13; //超時(shí)  
                               return   -2;  
                         }  
                         if(   iRet   ==   SOCKET_ERROR   )  
                        {  
                             GetLastError(   );  
                              return   -1;  
                        }  
                        if(   FD_ISSET(   m_scSocket,   &m_exceptfds   )   )  
                        {  
                              m_iError =   14; //連接被終止  
                              return   -1;  
                       }  
                  }  
                  iRet   =   recv(   m_scSocket,   szBuff,   iSize,   0   );  
                  if(   iRet   ==   0   )  
                 {  
                           m_iError =   14; //連接被終止  
                          return   -1;  
                }  
               if(   iRet   ==   SOCKET_ERROR   )  
               {  
                      GetLastError(   );  
                      return   -1;  
               }  
                return   iRet;  
              }  

            用法二

            int   recvex_sock(SOCKET   sock,   void*   buf,   int   len,   int   sec)  
              {  
                              int   rs;  
                              fd_set   fd;  
                              struct   timeval   tv;  
                              memset(&tv,   0,   sizeof(tv));  
                              if   (   sec   >   0   )  
                                              tv.tv_sec   =   sec;  
                              FD_ZERO(&fd);  
                              FD_SET(sock,   &fd);  
                              rs   =   select(sock   +   1,   &fd,   0,   0,   sec   >=   0   ?   &tv   :   NULL);  
                              if   (   rs   ==   0   )  
                                              return   SOCKET_TIMEOUT;  
                              if   (   rs   <   0   )  
                                              return   SOCKET_ERROR;  
                              if   (   !FD_ISSET(sock,   &fd)   )  
                                              return   SOCKET_ERROR;  
                              return   (recv_sock(sock,   buf,   len));  
              } 

            posted on 2007-12-06 14:21 kenlistian 閱讀(3054) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            国内精品人妻无码久久久影院导航 | 亚洲国产精品嫩草影院久久 | 久久w5ww成w人免费| 久久这里有精品| 怡红院日本一道日本久久 | 亚洲va久久久噜噜噜久久狠狠 | 久久天天躁狠狠躁夜夜av浪潮| 久久国产精品久久久| 国产精品久久久福利| 久久免费精品一区二区| 日韩亚洲欧美久久久www综合网| 成人久久精品一区二区三区| 国产精品一区二区久久不卡| 精品久久久久久综合日本| 99久久精品免费看国产免费| 91精品观看91久久久久久| 国产成人99久久亚洲综合精品| 日本精品久久久久中文字幕8| 久久99精品免费一区二区| 久久天天躁狠狠躁夜夜不卡| 久久中文字幕人妻熟av女| 亚洲日韩中文无码久久| 久久精品国产福利国产秒| 久久一区二区免费播放| 日韩人妻无码一区二区三区久久 | 色综合久久久久综合体桃花网| 久久精品国产亚洲av麻豆小说| 94久久国产乱子伦精品免费 | 欧美亚洲色综久久精品国产 | 久久夜色精品国产亚洲av| 天天躁日日躁狠狠久久| 久久久久国产精品| 中文成人无码精品久久久不卡| 久久香蕉超碰97国产精品| 久久亚洲高清综合| 久久婷婷久久一区二区三区| 女同久久| 国产成人精品久久| 丰满少妇高潮惨叫久久久| 久久免费视频1| 精品久久国产一区二区三区香蕉|