1. 程式人生 > >網路程式設計(53)—— Windows下使用WSAAsyncSelect實現視窗處理socket訊息

網路程式設計(53)—— Windows下使用WSAAsyncSelect實現視窗處理socket訊息

一、引言

       上一文中我們介紹了使用WSAEventSelect實現非同步通知IO的方法,本文我們主要討論下使用WSAAsyncSelect處理socket的方法。本文的主要目標,是建立一個帶介面的回聲服務端,接收並返回客戶端傳過來的字串,並在介面上顯示該字串。為此,我們將採用MFC的程式設計環境,建立如下的對話方塊程式:


二、WSAAsyncSelect函式

        WSAAsyncSelect也是windows下一種非同步的select,它可以註冊IO事件,當發生註冊的IO事件時,它會發送一個我們自定義的訊息給我們的視窗,而我們在視窗的訊息處理函式中就可以處理這些訊息了。它的原型如下:

int WSAAsyncSelect(
  __in SOCKET s,
  __in HWND hWnd,
  __in unsigned int wMsg,
  __in long lEvent
);

s —— 用於監視的socket。

hWnd —— 用來接收socket訊息的視窗。

wMsg —— 是我們自定義的訊息,一般情況下使用“WM_USER+ 數字”的形式進行定義。

lEvent —— 進行監視的IO事件,包含如下幾種:

FD_READ:套接字可讀通知。

FD_WRITE:可寫通知。

FD_ACCEPT:伺服器接收連線的通知。

FD_CONNECT:有客戶連線通知。

FD_OOB:外帶資料到達通知。

FD_CLOSE:套接字關閉通知。

FD_QOS:服務質量發生變化通知。

FD_GROUP_QOS:組服務質量發生變化通知。

FD_ROUTING_INTERFACE_CHANGE:與路由器介面發生變化的通知。

FD_ADDRESS_LIST_CHANGE:本地地址列表發生變化的通知。

返回值 —— 正常情況下返回0,出現錯誤時返回錯誤碼。

        我們知道Windows系統中訊息都會攜帶兩個引數wParam和lParam,我們在進行視窗的訊息處理時往往會利用這兩個引數。WSAAsyncSelect傳送的socket訊息也不例外,它的wParam引數攜帶的是發生IO事件的socket。而lParam低位元組表明已發生的事件。高位元組包含錯誤程式碼。我們可以使用WSAGETSElECTERROR巨集來讀取lParam的高位元組獲取錯誤碼,使用WSAGETSELECTEVENT巨集讀取lParam的低位元組來獲取發生的事件型別。

三、程式設計步驟

3.1 自定義訊息

        我們先自定義一個socket訊息以用WSAAsyncSelect傳送:

#define WM_SOCKET WM_USER+1

3.2 註冊服務端的socket

       我們先經過一般步驟建立服務端的socket,並然後用WSAAsyncSelect進行註冊:

    WSAStartup(MAKEWORD(2,2),&m_wsaData);
    m_servSock=socket(AF_INET,SOCK_STREAM,0);
   
    memset(&m_servAddr,0,sizeof(m_servAddr));
    m_servAddr.sin_family=AF_INET;
    m_servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    m_servAddr.sin_port=htons(atoi("8888"));
 
    bind(m_servSock,(SOCKADDR*)&m_servAddr,sizeof(m_servAddr));
 
    listen(m_servSock,5);
 
    WSAAsyncSelect(m_servSock,this->m_hWnd,WM_SOCKET,FD_ACCEPT|FD_READ);

        最後一句程式碼中,我們使用WSAAsyncSelect註冊了服務端的socketm_servSock,WSAAsyncSelect的第二個引數填寫了我們視窗的m_hWnd值,第三個引數是我們自定義的訊息,而第四個引數填寫的是註冊的IO事件,分別表示接受連結和有資料可讀。註冊完成之後,一旦有新的客戶端連線,系統就會發送一個WM_SOCKET訊息給我們的視窗。

3.3 處理socket訊息

        為了能夠處理socket訊息,我們先引入視窗的訊息處理函式,從類嚮導中新增虛擬函式OnWndMsg,在OnWndMsg中我們進行socket訊息的處理:

BOOL CWSAAsyncSelectServDlg::OnWndMsg(UINT message,WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    switch(message)
    {
        case WM_SOCKET:
            {
                SOCKETsock = (SOCKET) wParam;
                if(m_servSock == sock)
                {
                    //獲?取¨?
                    m_clntAddrSz=sizeof(m_clntAddr);
                                         m_clntSock=accept(m_servSock,(SOCKADDR*)&m_clntAddr,&m_clntAddrSz);
                    WSAAsyncSelect(m_clntSock,this->m_hWnd,WM_SOCKET,FD_READ);
                    //將?m_clntSock和¨ªm_clntAddr存ä?到Ì?映®3射¦?中D
                    m_sockMap.SetAt(m_clntSock,m_clntAddr);
                    PrintClientConnectMsg();
                   
                }
                else
                {
                    m_strLen= recv(sock,buf,BUF_SIZE - 1,0);
                    send(sock,buf,m_strLen,0);
                    buf[m_strLen]=0;
                    PrintClientSendMsg(sock);
                }
            }
            break;
        default:
            break;
    }
 
    return CDialogEx::OnWndMsg(message, wParam, lParam,pResult);
}
        從上述函式的WM_SOCKETswitch分支可以看到,我們通過wParam獲取到了發生IO事件的socket,如果有需要還可以通過lParam獲取到錯誤碼或者事件型別。獲取到socket之後,先判斷socket是不是服務端的m_servSock。如果是,表明有新的客戶端連結,接收連線並列印客戶端資訊;如果不是則是由客戶端傳來了資料,呼叫recv接收資料並send給客戶端,最後列印資料。

        下面是用來列印客戶端資訊和客戶端資料的兩個函式:

void CWSAAsyncSelectServDlg::PrintClientConnectMsg(void)
{
    CString str;
    CTimelocTime=CTime::GetCurrentTime();    //獲?取¨?當Ì¡À前¡ã時º¡À間?
    CStringstrTime=locTime.Format("%Y-%m-%d%H:%M:%S");//將?當Ì¡À前¡ã時º¡À間?轉Áa換?成¨¦CString類¤¨¤型¨ª
    str.Format("%s Client %s:%d Connectted",strTime,inet_ntoa(m_clntAddr.sin_addr),m_clntAddr.sin_port);
    this->m_list.AddString(str);
}
 
 
void CWSAAsyncSelectServDlg::PrintClientSendMsg(SOCKET sock)
{
    CString str;
    m_clntAddr=m_sockMap[sock];
    CTimelocTime=CTime::GetCurrentTime();  
    CStringstrTime=locTime.Format("%Y-%m-%d%H:%M:%S");//將?當Ì¡À前¡ã時º¡À間?轉Áa換?成¨¦CString類¤¨¤型¨ª
    str.Format("%s Client %s:%d say:%s",strTime,inet_ntoa(m_clntAddr.sin_addr),m_clntAddr.sin_port,buf);
    this->m_list.AddString(str);
}

執行程式,效果如下,服務端不單返回了客戶端傳過來的資訊,還對資訊進行了列印。

客戶端1:


客戶端2:

 

 服務端:


Github位置:https://github.com/HymanLiuTS/NetDevelopment克隆本專案:git clone git@github.com:HymanLiuTS/NetDevelopment.git獲取本文原始碼:
git checkout NL53

相關推薦

網路程式設計53—— Windows使用WSAAsyncSelect實現視窗處理socket訊息

一、引言        上一文中我們介紹了使用WSAEventSelect實現非同步通知IO的方法,本文我們主要討論下使用WSAAsyncSelect處理socket的方法。本文的主要目標,是建立一個帶介面的回聲服務端,接收並返回客戶端傳過來的字串,並在介面上顯示該字串。為

網路程式設計55—— Windows使用WSASocket基於Completion Routine進行IO重疊

一、引言         上一文中我們介紹了使用基於事件進行IO重疊的方法,本文主要介紹另外一種,基於回撥函式void CALLBACK CompletionRoutine(DWORD dwError,DWORDszRecvBytes,LPWSAOVERLAPPED lpO

網路程式設計52—— Windows使用WSAEventSelect實現非同步通知IO

一、同步IO和非同步IO         同步IO是指發生IO事件的時間點和相關函式返回的時間點一致。如使用send函式傳送資料時,所有的資料傳送到輸出緩衝區後,send函式才會返回,這種IO方式就是同步IO。非同步IO指函式先於IO事件返回。還是以send函式為例,呼叫s

網路程式設計46—— windows核心物件的兩種狀態

一、 什麼是核心物件?         我們知道程序、執行緒、檔案、互斥、訊號量這些都是作業系統級別的資源。我們在使用這些資源時,實際上都是由作業系統進行建立和管理的。作業系統為了管理這些資 源,會在其內部建立一個數據塊,也可以理解為一個結構體物件。這個資料塊就是核心物件。

遊戲網路程式設計——WebSocket入門及實現自己的WebSocket協議

(一)WebSocket簡介 短連線:在傳統的Http協議中,客戶端和伺服器端的通訊方式是短連線的方式,也就是伺服器端並不會保持一個和客戶端的連線,在訊息傳送後,會斷開這個連線,客戶端下次通訊時,必須再建立和伺服器的新連線,這就是短連線。在短連結的情況下,客戶

Socket網路程式設計1 ———— 基於TCP協議的客戶-伺服器socket例項

Socket網路程式設計(1) ———— 基於TCP協議的客戶-伺服器socket例項 1、TCP網路程式設計架構 2、程式碼段 伺服器: #include <stdio.h> #include <stdlib.h&

c++ 網路程式設計TCP/IP LINUX/windows 多執行緒超詳細教程 以及 多執行緒實現服務端

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <process.h> #include <winsock2.h> #include <win

c++ 網路程式設計TCP/IP LINUX/windows 多執行緒超詳細教程 以及 多執行緒實現服務端

原文作者:aircraft 原文連結:https://www.cnblogs.com/DOMLX/p/9661012.html  先講Linux下(windows下在後面可以直接跳到後面看): 一.執行緒基本概念 前面我們講過多程序伺服器,但我們知道它開銷很大

Windows網路程式設計訊息選擇模型

概述 之前介紹過,系統提供了幾種網路模型用於非同步的網路互動,訊息選擇模型就是其中一種。 這種模型的使用需要在呼叫完socket()函式以後呼叫WSAAsyncSelect(),這個函式的宣告如下: int WSAAsyncSelect(SOCKET s,HWND h

Windows網路程式設計:非阻塞模式非同步模式

前面幾篇文章介紹的無論是TCP通訊還是UDP通訊都是阻塞式的,它們在執行recv或recvfrom時會線上程中等待,直到接收到資訊為止,所以在應用的時候一般都需要開闢子執行緒,在子執行緒裡專門做這類事情,不然它會影響主執行緒的執行。  系統提供三種網路模型

Windows網路程式設計:原始套接字開發

在呼叫socket()函式時,如果將第二個引數填為SOCK_RAW,代表建立的是原始套接字型別,第三個引數可以選擇IPPROTO_ICMP、IPPROTO_TCP、IPPROTO和IPPROTO_RAW。 #include <winsock2.h> #pragma co

Windows網路程式設計:IP Helper

IP Helper是Windows系統與IP配置和管理的重要介面,通過IP Helper 可以獲得很多跟網路配置相關的資訊。比如說本機IP、閘道器設定、網絡卡數量和連線資訊。 #include <windows.h> #include "iphlpapi.h" /* 全域

Windows網路程式設計:多執行緒訊息處理

對於服務端來說,呼叫accept()函式同意客戶端連線的請求後,需要處理完與這個客戶端的通訊後回到accept()繼續等待下一個客戶端的連線,如果一個客戶端請求連線時服務端並沒有在accept()處等待,客戶端是無法成功連上服務端的,因此併發客戶端連線的服務端必然是多執行緒的。 服務

Windows網路程式設計:建立UDP連線和收發訊息

UDP訊息的傳送和接收需要UDP連線,所以,上面的TCP連線已經不適用了,具體的區別主要有: 建立Socket時引數不同建立服務端時不需要listen和accept操作建立客戶端時不需要connect操作伺服器需要bind操作,客戶端不需要。 傳送和接收UDP訊息要用到sendt

Windows網路程式設計:建立TCP連線和收發訊息

先看服務端: // ConsoleApplication3.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #define _WINSOCK_DEPRECATED_NO_WARNINGS //這個宣告要在stdafx.h的後面,但要

Windows網路程式設計Socket簡介

Socket簡介 Socket被稱為套接字,描述了IP和埠等資訊,是一個通訊鏈的控制代碼。 微軟專門開發了一套支援多種網路協議的網路程式設計介面,叫做Winsock,Winsock是Windos SDK的一部分,全稱Windows Sockets API。它對多種協議做了封裝,S

Windows網路程式設計:TCP/IP協議

概述 這個協議是一個四層協議: 應用層,主要協議有HTTP、FTP等 傳輸層,主要協議有TCP、UDP等 網路層,主要協議有IP等 鏈路層,主要協議有ICMP等 下層中的協議總是為上層中的協議服務的,比如說應用層的HTTP、FTP協議都是基於T

從零開始學習音視訊程式設計技術35 windows編譯並除錯ffmpeg

前面介紹了Linux下編譯ffmpeg的方法,考慮到大部分時候測試ffmpeg功能都是使用的windows系統(至少我是這樣的),因此將戰場重新轉移到windows上。    前面寫了那麼多的程式碼,但都只是簡單的呼叫了ffmpeg的API,並不知道他內部是如何實現的。如果可

Windows安裝RabbitMQ服務

百度網盤 http lang gin 配置 ble localhost 語言 load 一:安裝RabbitMQ需要先安裝Erlang語言開發包,百度網盤地址:http://pan.baidu.com/s/1jH8S2u6。直接下載地址:http://erlang.org/

python學習-網路程式設計

udp的接收和傳送資料程式碼: udp的傳送資料程式碼如下: import socket def main():     #建立套接字     udp_socket = socket.socket(socket.AF_I