windows網路編程經典入門

來源:互聯網
上載者:User

 

對於一個windows網路編程初學者,下面方法是經典入門。
    初學者建議不要用MFC提供的類,而用windows API做一個簡單伺服器和用戶端,這樣有助於對socket編程機制的理解。
    為了簡單起見,應用程式是基於MFC的標準對話方塊。
    Winsock用WINDOWS API實現:
        (1)伺服器端有兩個線程:
        主線程 — 你需要編寫以下函數來實現

        #define NETWORK_EVENT   USER_MESSAGE+100   file://定義網路事件
        sockaddr_in   clientaddr; file://暫時存放用戶端IP地址
       
        file://自己定義訊息映射函數,將上面定義的網路事件映射到處理函數
        file://OnNetEvent為網路事件處理函數,它在下面定義
        ON_MESSAGE(NETWORK_EVENT, OnNetEvent); 

        在你對話方塊中的初始化函數中調用下面的初始化網路的子函數
        BOOL InitNetwork()    file://初始化網路
        {
            file://初始化TCP協議
            BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData);
    if(ret != 0)
  {
         MessageBox("初始化通訊端失敗!");
      return FALSE;
  }

            file://建立伺服器端通訊端
            SOCKET serverSocket
                   = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(serverSocket == INVALID_SOCKET)
  {
  MessageBox("建立通訊端失敗!");
  closesocket(m_Socket);
  WSACleanup();
  return FALSE;
  }

           file://綁定到本地一個連接埠上
           sockaddr_in localaddr;
 localaddr.sin_family = AF_INET;
 localaddr.sin_port = htons(1688);
 localaddr.sin_addr.s_addr = 0;
           if(bind(serverSocket ,(const struct sockaddr*)&localaddr, 
                   sizeof(sockaddr)) == SOCKET_ERROR)
 {
  MessageBox("綁定地址失敗!");
  closesocket(m_Socket);
  WSACleanup();
  return FALSE;
 }

          file://註冊網路非同步事件,m_hWnd為應用程式的主對話方塊或主視窗的控制代碼
          WSAAsyncSelect(serverSocket, m_hWnd, NETWORK_EVENT,
                  FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE);

            listen(serverSocket, 5); file://設定偵聽模式
 
            return TRUE;
        }

        file://定義網路事件的響應函數
        void OnNetEvent(WPARAM wParam, LPARAM lParam)
        {
           file://調用API函數,得到網路事件類型
           int iEvent = WSAGETSELECTEVENT(lParam);
         
           file://得到發出此事件的用戶端通訊端
           SOCKET pSock = (SOCKET)wParam;

           switch(iEvent)
           {
           case FD_ACCEPT: file://用戶端串連請求
             {
                OnAccept();

                break;
             }
           case FD_CLOSE:  file://用戶端斷開事件:
             {
                OnClose(pSock);
                break;
             }
           case FD_READ:   file://網路資料包到達事件
             {
                OnReceive(pSock);
                break;
             }
    case FD_WRITE:  file://發送網路資料事件
      {
                OnSend(pSock);
         break;
             }
      default: break;
           }
        }

        void OnAccept(SOCET pSock)  file://響應用戶端串連請求函數
        {
           int len = sizeof(sockaddr);

           file://調用API函數,接受串連,並返回一個新通訊端
           file://還可以獲得用戶端的IP地址
           SOCKET clientSocket = accept(serverSocket,
                      (struct sockaddr*)&clientaddr, &len);

           file://為新的socket註冊非同步事件,注意沒有Accept事件
           if(WSAAsyncSelect(clientSocket ,m_hWnd, IP_EVENT,
                  FD_CLOSE | FD_READ | FD_WRITE) == SOCKET_ERROR)
 {
         MessageBox("註冊非同步事件失敗!");
         return;
 }
 
          file://自編函數,將此用戶端的相關資訊儲存下來:通訊端、
           // IP地址、登陸時間           
           saveClientSocket(clientSocket,clientAddr,currentTimer);
        }

        void OnClose(SOCET pSock)
        {
         file://自編函數,結束與相應的用戶端的通訊,釋放相應資源並做相應處理
           endClientSocket(pSock);
        }

        void OnSend(SOCET pSock)
        {
           file://自編函數,在給用戶端發資料時做一些預先處理
           handleOnSend(pSock);
        }

        void OnReceive(SOCET pSock)
        {
           recv(...);        file://調用API函數,讀出網路緩衝區中的資料包

           file://自編函數,將此資料包和發出此資料的用戶端
           file://clientSocket封裝成一條網路訊息
           buildNetMsg(...);

           file://自編函數,將此網路訊息放入一個訊息佇列中,由背景工作執行緒去處理
           saveNetMsg(...); 
           SetEvent(...);    file://用事件對象觸發背景工作執行緒
        }       
       
        用戶端登陸後,隨即把自己的電腦名稱發給伺服器,伺服器接到後,把它儲存下來。這樣伺服器就可以顯示所有線上用戶端的資訊了,包括:用戶端電腦名、IP地址、登陸時間等。
       
        注意: 用戶端沒有OnAccept()函數,但有OnConnect()函數。

        背景工作執行緒 —
        在你的應用程式初始化時,建立並啟動一個背景工作執行緒

        AfxBeginThread(WorkThread,this,THREAD_PRIORITY_NORMAL);
        file://this可能為應用程式的主對話方塊或主視窗的控制代碼

        UINT WorkThread(LPVOID pParam)
        {   
            while(1)
           {      
              file://等待多重事件到來
              int ret = WaitForMultipleObject(...);  
      
              switch(ret)
              {
              case OBJECT_0:
                {  
                   if(bNewNetMsg) file://查看網路訊息佇列是否有新的網路訊息
                   {
                      readNetMsg(...);    file://如有新的網路訊息,則讀出
                      handleNetMsg(...);  file://處理此網路訊息
                   }
                   break;
                }
              case OBJECT_0 + 1:
                {
                   file://做退出處理
                   break;
                }
              default: break;
           }

           return 0;
        }
        
    用戶端為單線程,登陸伺服器時,用connect()函數給伺服器發串連請求;
            用戶端沒有OnAccept()函數,但有OnConnect()函數。
            在OnConnect()函數裡做發串連請求時的預先處理;
            在OnReceive()函數裡響應並處理網路資料;
            在OnClose()函數裡響應伺服器的關閉事件;
            在OnSend()函數裡做發資料時的預先處理;

    如果你還想實現各用戶端之間的線上交流(即所謂的聊天室),你在用戶端還可以基於UDP協議
再做一套多點對多點的區域網路組播模型模型,以後在和你聊,你先把上面的程式實現。
        
    以上的I/O非同步模型基於Windows的訊息機制,另外還可以用事件模型、重疊模型或完成連接埠模型,
    建議你參考Windows網路編程指南之類的書。

    如果你能對上面的機制很熟練,你肯定已經對Winsock編網路程式的機制有一定理解,接下來你可以進行更精彩的編程, 不僅可以在網上傳輸普通資料,而且還
以傳輸語音、視頻資料,你還可以自己做一個聊天室,和你的同學在實驗室的區域網路裡可以共同分享你的成果。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.