1. 程式人生 > >基於Socket的多執行緒和非同步非阻塞模式程式設計

基於Socket的多執行緒和非同步非阻塞模式程式設計

      剛開始接觸socket的程式設計的時候,遇到了很多的問題,費了很大勁搞懂。其實往往都是一些比較基本的知識,但是都是很重要的,只要對其熟練的掌握後,相信對基於網路的程式設計會有很大的提高,呵呵。

      就拿基於C/S結構的例子來說,我們先看看伺服器和客戶端的流程(異常處理就省略了):

     伺服器:

              //初始化

               WSAData wsaData;
               int iRet=WSAStartup(MAKEWORD(1,1),&wsaData);//網路初始化,返回0則初始化成功

               m_socketServer=socket

(AF_INET,SOCK_STREAM,IPPROTO_TCP);//建立伺服器端套接字,INVALID_SOCKET(失敗)

               //繫結到本地一個埠上 
               bind(m_socketServer ,(struct sockaddr*)&localaddr,sizeof(sockaddr))//   SOCKET_ERROR(失敗)

               listen(m_socketServer, 5); //設定偵聽模式

               //-----------------初始化完成後------------

               m_socketClient  

= accept(m_socketServer,   (SOCKADDR*)&remoteAddr,   &nAddrLen); //得到一個連線

               recv(m_socketClient,buff,sizeof(buff),0);//獲取資料

               send(m_socketClient,buff,sizeof(buff),0);//傳送資料

                //關閉

                   closesocket(m_socketServer); 
                   WSACleanup();

    客戶端:

               客戶端與伺服器大部分是比較類似的,如下:

                WSAData wsaData;
               int iRet=WSAStartup(MAKEWORD(1,1),&wsaData);//網路初始化,返回0則初始化成功

               m_socketServer=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//建立伺服器端套接字,INVALID_SOCKET(失敗)

               connect(m_clientSocket,(sockaddr*)&server,sizeof(server))//連線到伺服器

               recv............

               send...........

               closesocket...............

               WSACleanup..............

上面就是基於socket網路程式設計的基本流程了,具體的方法以及說明就不在這裡說明了,  當然,要很好的使用socket,寫出高質量的程式碼,瞭解這些還遠遠不夠,例如:

        對應多個客戶端怎麼辦?

        怎麼實現非阻塞模式?

       我們知道,請求伺服器的客服端往往不是一個的,這樣就需要我們處理多個請求,如果用一個執行緒去做,有可能就會出現有的請求長期等待,有的請求長期佔用資源....等等問題。另外,WinSock提供了兩種套接字模式:鎖定和非鎖定。當我們使用鎖定套接字的時候,例如accpet、send、recv等等,如果沒有資料需要處理,這些函式都不會返回,也就是說,你的應用程式會阻塞在那些函式的呼叫處。而如果使用非阻塞模式,呼叫這些函式,不管你有沒有資料到達,他都會返回。所以有可能我們在非阻塞模式裡,呼叫這些函式大部分的情況下會返回失敗,所以就需要我們來處理很多的意外出錯。

        怎麼解決這些問題呢?

  對應阻塞問題,WinSock提供了五種套接字I/O模型來解決這些問題。他們分別是select(選擇),WSAAsyncSelect(非同步選擇),WSAEventSelect (事件選擇,overlapped(重疊) , completion port(完成埠) 。

        這裡就講講(非同步選擇)WSAAsyncSelect:

          使用非同步選擇模式,我們可以註冊各自需要的網路非同步事件,然後定義一個訊息函式來處理這些事件,這樣所有的事件就可以交個視窗的這個訊息函式去處理了。流程如下:

            1.定義訊息事件:

                  #define NETWORK_EVENT WM_USER+100 //定義網路事件

            2.訊息函式定義

                  afx_msg LRESULT OnNetEvent(WPARAM wParam, LPARAM lParam);//非同步事件回撥函式

            3.訊息對映

                    ON_MESSAGE(NETWORK_EVENT,OnNetEvent)

            4.非同步選擇

               函式原型:

                     int PASCAL FAR WSAAsyncSelect (

                                 SOCKET s,

                                  HWND hWnd,
                                  unsigned int wMsg,

                                   long lEvent );

          s 標識一個需要事件通知的套介面的描述符.

          hWnd 標識一個在網路事件發生時需要接收訊息的視窗控制代碼.

          wMsg 在網路事件發生時要接收的訊息.

          lEvent 位遮蔽碼,用於指明應用程式感興趣的網路事件集合.
              

            詳細介面說明見下:

             使用

                  例如我們在初始化伺服器的時候,獲得每個請求的socket客戶端都是使用阻塞式的介面accept:

                  現在我們要改為非阻塞式,即有阻塞請求的時候才呼叫網路訊息函式去處理,於是:

                if(WSAAsyncSelect(m_socketServer,

                                              m_hWnd,

                                             NETWORK_EVENT,

                                              FD_ACCEPT) !=0)

                {

                    MessageBox(L"註冊網路非同步事件失敗!");

                    WSACleanup();

                    return FALSE;

                 }

               這裡的註冊的事件只有一個FD_ACCEPT,這裡還有很多的組合,如下:

               值                             意義

          FD_READ      欲接收讀準備好的通知.

          FD_WRITE    欲接收寫準備好的通知.

          FD_OOB       欲接收帶邊資料到達的通知.

          FD_ACCEPT 欲接收將要連線的通知.

          FD_CONNECT 欲接收已連線好的通知.

          FD_CLOSE      欲接收套介面關閉的通知.

      5.訊息函式實現

               當網路事件來的時候,就會呼叫到訊息函式,如下:

      LRESULT CTRDoorServerDlg::OnNetEvent(WPARAM wParam, LPARAM lParam)//非同步事件回撥函式
{
       //呼叫Winsock API函式,得到網路事件型別 (以FD_開頭)

    int iEvent = WSAGETSELECTEVENT(lParam);

       //呼叫Winsock API函式,得到發生此事件的客戶端套接字

       SOCKET sClient= (SOCKET)wParam;
 

      switch(iEvent)

      {

         case FD_ACCEPT: //客戶端連線請求事件 

             ....................
              break;

         case FD_CLOSE: //客戶端斷開事件:

              ..........................

              break;

         case FD_READ: //網路資料包到達事件 
               ......................

              break;

         case FD_WRITE: //傳送網路資料事件

               ........................

             break;

          default:
             break;

       }
   return 0;
}        

當然伺服器主要就是處理連線,如果想處理客戶端的網路事件的話,則可以在當伺服器每次得到一個連線請求的時候加入程式碼:

 在上面的函式OnNetEvent中:

          case FD_ACCEPT: //客戶端連線請求事件 

             {

                  sockaddr_in   remoteAddr;     
                   int   nAddrLen   =   sizeof(remoteAddr);  

                    //   接受一個新連線   
                    SOCKET socketClient   = accept(m_socketServer,   (SOCKADDR*)&remoteAddr,   &nAddrLen);

                    if(m_socketClient == INVALID_SOCKET)  
                  {  
                       return FALSE;
                 }

                  if(WSAAsyncSelect(socketClient, m_hWnd, NETWORK_EVENT,

                    FD_READ|FD_WRITE) !=0) //給先得客戶端註冊了讀寫事件
                   {
                     return FALSE;
                   }  

             }
              break;

     這裡socketClient 可以設計為成員陣列也可以,呵呵,看具體情況而定吧。

     這樣客戶端的讀寫事件也就交個訊息函式:OnNetEvent 了。

多執行緒處理:

    上面我們看到,每個客戶端的網路事件函式讓一個訊息函式去處理,這樣主執行緒的任務可就太大了,當客戶端多了的時候就會有點吃不消的了,呵呵,那麼讓我們小修改一下,就可以讓另外的執行緒去搞定它了,如下:

      執行緒函式:

        DWORD  WINAPI SocketThread(PVOID pvParam)
        {
        SOCKET* sock= (SOCKET*)pvParam;
          //為空時返回
           if(NULL == sock)
                 return 0;

            while (true) {
                         char buff[1024];
                         int iRecvLen=recv(sock,buff,1024,0);//接受資料並處理

                          //do something.............

                        Sleep(100);
                   }
                 return(0);
             }

        //建立執行緒

     在上面的函式OnNetEvent中,加入程式碼:

      case FD_ACCEPT: //客戶端連線請求事件
  {
       sockaddr_in   remoteAddr;    
       int   nAddrLen   =   sizeof(remoteAddr);  

       //   接受一個新連線  
       m_socketClient   = accept(m_socketServer,   (SOCKADDR*)&remoteAddr,   &nAddrLen);

      if(m_socketClient == INVALID_SOCKET)  
      {  
          return FALSE;
       }

      SOCKET * s=&m_socketClient;
      //建立執行緒
      DWORD dwThreadID;
        CreateThread(NULL, 0, RecvMsgThread, s, 0, &dwThreadID);

  }
  
  break;

  這樣所有的事情就可以叫給執行緒去處理了。

當然,要學好網路程式設計還要學習很多東西,得不斷努力呀,一起加油,請多指教!!!!!

相關推薦

基於Socket執行非同步阻塞模式程式設計

      剛開始接觸socket的程式設計的時候,遇到了很多的問題,費了很大勁搞懂。其實往往都是一些比較基本的知識,但是都是很重要的,只要對其熟練的掌握後,相信對基於網路的程式設計會有很大的提高,呵呵。       就拿基於C/S結構的例子來說,我們先看看伺服器和客戶端的流

執行非同步下載圖片

一開始從慕課網學習到的非同步載入顯示圖片,今天終於有時間按照記憶寫了份非同步載入圖片程式碼;有很多小細節需要注意的。 步驟: 1、先解析完JSon資料,並存放在List中間 2、非同步下載圖片,放在Item.xml中 3、listView.se

CompletableFuture的執行非同步監聽實現

大家好,我是烤鴨:今天給大家說的是多執行緒併發的非同步監聽的情況。這裡不得不說一下CompletableFuture這個類,普通我們執行多執行緒的時候只需要另外啟動一條執行緒。說一下執行緒的3種方式:extends Thread,implements Runnable,imp

執行非同步

1、簡單的一般執行緒 Func<int, int, int> Add = (a, b) => a + b; Thread t = new Thread(() => { Co

Windows下基於socket執行併發通訊的實現

    本文介紹了在Windows 作業系統下基於TCP/IP 協議Socket 套介面的通訊機制以及多執行緒程式設計知識與技巧,並給出多執行緒方式實現多使用者與服務端(C/S)併發通訊模型的詳細演算法,最後展現了用C++編寫的多使用者與伺服器通訊的應用例項並附有程式。 關

一位牛人的執行非同步呼叫文章 ~轉自部落格園的“小顧問”

首先申明:這篇文章不是我寫的,我看到的一位牛人的,自己慢慢的消化了……摘要:本章討論與智慧客戶端應用程式中多執行緒的使用有關的問題。為了最大限度地提高智慧客戶端應用程式的響應能力,需要仔細考慮如何和何時使用多執行緒。執行緒可以大大提高應用程式的可用性和效能,但是當您確定它們將如何與使用者介面互動時,需要對其進

Android進階——執行非同步任務小結

引言 眾所周知,無論是在任何的程式語言和作業系統中。多執行緒、多程序和非同步同步始終都是經久不衰的話題。當然在我們實際的Android專案需求中也是如此,很多的業務需求都通過多執行緒及非同步任務以便使用者能夠在使用App中得到優秀的體驗。而很多App在使用過程

Visual C++網路程式設計經典案例詳解 第3章 執行非同步套接字程式設計 實現執行同步 互斥物件 使用API函式操作互斥物件

互斥物件和臨界區物件和事件物件作用一樣 用於實現執行緒同步 互斥物件可以線上程中使用 CreateMutex()建立並返回互斥物件 原型如下 HANDLE CreateMutex(   LPSECURITY_ATTIRIBUTES lpMutexAttributes,  

Visual C++網路程式設計經典案例詳解 第3章 執行非同步套接字程式設計 實現執行同步 互斥物件 程式的唯一執行

互斥物件可在程序中使用 使用者在程序建立互斥物件實現程式例項唯一執行 建立控制檯工程 #include<windows.h>                                //包含標頭檔案 #include<stdio.h> in

Visual C++網路程式設計經典案例詳解 第3章 執行非同步套接字程式設計 程序間通訊 命名管道 命名管道例項

vc新增控制檯工程 名字命名管道例項 新增原始檔 名字 伺服器 #include<windows.h>                                //包含標頭檔案 #include<stdio.h> int main() {  

02-node.js 單執行,‘ 非同步阻塞io

1、基本概念     同步:多個任務順序執行     非同步:多個任務並排執行 2、node的併發實現原理     Node JS是單執行緒應用程式,但它通過事件和回撥概念,支援併發。 由於Node JS每一個API是非同步的,作為一個單獨的執行緒,它使用非同步函

基於執行本地快取實現跨域重複呼叫的高效能

>> 最近工作了,寫部落格的時間越來越少了,思考的時間越來越少,學習沉澱的時間也越來越少。忙裡偷閒,記錄一些在平時工作中一些有亮點的小tip,記錄一些實用的技能,也多虧平時接觸到的有能力有想法的同事~ 前言 對於大體量網際網路公司的應用,更多場景下需要考慮效能

【玩轉cocos2d-x之二十三】執行同步03-圖片非同步載入

cocos2d-x中和Android,Windows都一樣,如果在主執行緒中處理一些耗時操作,那麼主執行緒就會出現阻塞現象,表現在介面上就是卡住,未響應等情況。為了避免這種情況的出現,我們需要在後

python併發程式設計程序、執行非同步協程

一、多執行緒   多執行緒就是允許一個程序記憶體在多個控制權,以便讓多個函式同時處於啟用狀態,從而讓多個函式的操作同時執行。即使是單CPU的計算機,也可以通過不停地在不同執行緒的指令間切換,從而造成多執行緒同時執行的效果。   多執行緒相當於一個併發(concunrr

執行——原子、原子,自旋鎖互斥鎖

nonatomic:非原子屬性,執行緒不安全的,效率高 atomic:原子屬性,執行緒安全的,效率相對低。 原子屬性是一種單(執行緒)寫多(執行緒)讀的多執行緒技術,不過可能會出現髒資料 atomi

Socket網路程式設計基於TCP執行通訊

第一步:編寫啟動服務端的執行緒類 package socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.

Objective-C高階程式設計:iOS與OS X執行記憶體管理

這篇文章主要給大家講解一下GCD的平時不太常用的API,以及文末會貼出GCD定時器的一個小例子。 需要學習的朋友可以通過網盤免費下載pdf版 (先點選普通下載-----再選擇普通使用者就能免費下載了)http://putpan.com/fs/cy1i1beebn7s0h4u9/ 1.G

[讀書筆記]iOS與OS X執行記憶體管理 [GCD部分]

3.2 GCD的API 蘋果對GCD的說明:開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中。 “Dispatch Queue”是執行處理的等待佇列。通過dispatch_async函式等API,在Block

jfinalQ開發教程08-qiao-util.jar:執行定時任務

多執行緒 多執行緒是java面試中最愛問的一個問題,當然如果工作多年沒準備去面試,正好讓你手寫程式碼,那就只能呵呵了~ QThreadUtil com.uikoo9.util.function.QThreadUtil對java自帶的多執行緒做了封裝,其實java自帶多執行緒已經

執行原子操作記憶體柵欄(二)

        這裡記錄下各種鎖的使用和使用場景,在多執行緒場景開發時,我們經常遇到多個執行緒同時讀寫一塊資源爭搶一塊資源的情況,比如同時讀寫同一個欄位屬性,同時對某個集合進行增刪改查,同時對資料庫進行讀寫(這裡