網路程式設計(55)—— Windows下使用WSASocket基於Completion Routine進行IO重疊
一、引言
上一文中我們介紹了使用基於事件進行IO重疊的方法,本文主要介紹另外一種,基於回撥函式void CALLBACK CompletionRoutine(DWORD dwError,DWORDszRecvBytes,LPWSAOVERLAPPED lpOverlapped,DWORD flags)進行IO重疊。首先,我們先介紹一種執行緒的狀態——alertable wait狀態。
什麼是alertable wait狀態?
alertable wait狀態是執行緒等待接收作業系統訊息的狀態,我們之前接觸過的WSAWaitForMultipleEvents函式就可以觸發執行緒這種狀態。我們先來回顧下WSAWaitForMultipleEvents的函式原型:
DWORDWSAWaitForMultipleEvents(
__in DWORD cEvents,
__in const WSAEVENT *lphEvents,
__in BOOL fWaitAll,
__in DWORD dwTimeout,
__in BOOL fAlertable
);
它的第5個引數fAlertable就是設定alertablewait狀態的開關,當它設定為True時就會啟用執行緒的alertable wait狀態。我們定義的回撥函式CompletionRoutine,只有在執行緒進入alertablewait狀態後才會被作業系統呼叫。
二、定義CompletionRoutine回撥函式
我們先來回顧下WSARecv
的原型(WSASend類似):
int WSARecv( __in SOCKET s, __inout LPWSABUF lpBuffers, __in DWORD dwBufferCount, __out LPDWORD lpNumberOfBytesRecvd, __inout LPDWORD lpFlags, __in LPWSAOVERLAPPED lpOverlapped, __in LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
在上一文中,採用基於事件的IO重疊時,將WSARecv
最後一個引數設定成了NULL。而實際上他就是我們定義的回撥函式
CompletionRoutine的函式指標。而CompletionRoutine函式的後三個引數分別來自WSARecv
的第4~6個引數。當執行緒進入
alertable wait狀態後,作業系統就會執行CompletionRoutine函式。
三、應用例項
上述是使用CompletionRoutine的理論部分,下面我們通過例項對CompletionRoutine進行學習。使用CompletionRoutine進行資料接收的例子:
// WSASocketCompletionRoutineServ.cpp : 定¡§義°?控?制?臺¬¡§應®|用®?程¨¬序¨°的Ì?入¨?口¨²點Ì?。¡ê
//
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include <WinSock2.h>
#include <string.h>
#pragma comment(lib,"ws2_32.lib")
#define BUF_SIZE 30
void ErrorHandler(const char* message);
void CALLBACK CompletionRoutine(DWORD dwError,DWORDszRecvBytes,LPWSAOVERLAPPED lpOverlapped,DWORD flags);
char buf[BUF_SIZE];
DWORD recvBytes = 0;
WSABUF wsaBuf;
int _tmain(int argc, _TCHAR* argv[])
{
SOCKETservSock,clntSock;
SOCKADDR_INservAddr,clntAddr;
int clntAddrSz;
WSAOVERLAPPEDoverLapped;
HANDLEhEvent;
DWORDret,flags=0;
WSADATAwsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
//創ä¡ä建¡§支¡ì持?IO復¡ä用®?的Ì?套¬¡Á接¨®字Á?
servSock=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
if(servSock==INVALID_SOCKET)
ErrorHandler("WSASocket Error");
memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family=AF_INET;
servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
servAddr.sin_port=htons(atoi("8888"));
if(bind(servSock,(SOCKADDR*)&servAddr,sizeof(servAddr))==SOCKET_ERROR)
ErrorHandler("bind error");
if(listen(servSock,5)==SOCKET_ERROR)
ErrorHandler("listen error");
clntAddrSz=sizeof(clntAddr);
clntSock=accept(servSock,(SOCKADDR*)&clntAddr,&clntAddrSz);
memset(&overLapped,0,sizeof(overLapped));
wsaBuf.buf=buf;
wsaBuf.len=BUF_SIZE;
hEvent=WSACreateEvent();
overLapped.hEvent=hEvent;
if(WSARecv(clntSock,&wsaBuf,1,&recvBytes,&flags,&overLapped,CompletionRoutine)==SOCKET_ERROR)
{
if(WSAGetLastError()==WSA_IO_PENDING)
puts("background recieve data");
}
ret=WSAWaitForMultipleEvents(1,&hEvent,false,WSA_INFINITE,true);
if(ret==WAIT_IO_COMPLETION)
puts("Overlapped I/O Compeleted");
else
ErrorHandler("WSARecv Error");
WSACloseEvent(hEvent);
closesocket(servSock);
closesocket(clntSock);
WSACleanup();
return 0;
}
void CALLBACK CompletionRoutine(DWORD dwError,DWORDszRecvBytes,LPWSAOVERLAPPED lpOverlapped,DWORD flags)
{
if(dwError!=0)
{
ErrorHandler("CompletionRoutine Error");
}
else
{
recvBytes=szRecvBytes;
printf("Recieve Message:%s\n",buf);
}
}
void ErrorHandler(const char* message)
{
fputs(message,stderr);
fputc('/n',stderr);
exit(1);
}
第79~90行,是對CompletionRoutine函式的定義,在函式中作業系統將實際接收的位元組szRecvBytes等資訊傳遞給我們,在函式裡對收到的buf進行列印
第17~19行,分別聲明瞭用於接收資料的buf,實際接收的位元組數recvBytes,和用於WSARecv呼叫的wsaBuf,由於在回撥函式CompletionRoutine中也會用到這些變數,所以它們被宣告成了全域性變數。
第55~59行,初始化進行IO重疊的相關變數,包括wsaBuf、hEvent和overLapped。
第61行,呼叫WSARecv進行資料的接收,並將定義好的CompletionRoutine函式傳給其最後一個引數。
下面是使用WSASend函式的一個例子,由於上上面的例子原理上相同,這裡就不在贅述。
// WSASocketCompletionRoutineClnt.cpp : 定¡§義°?控?制?臺¬¡§應®|用®?程¨¬序¨°的Ì?入¨?口¨²點Ì?。¡ê
//
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include <WinSock2.h>
#include <string.h>
#pragma comment(lib,"ws2_32.lib")
#define BUF_SIZE 30
void ErrorHandler(const char* message);
void CALLBACK CompletionRoutine(DWORD dwError,DWORDszRecvBytes,LPWSAOVERLAPPED lpOverlapped,DWORD flags);
char buf[BUF_SIZE]="Hello world";
DWORD recvBytes = 0;
int _tmain(int argc, _TCHAR* argv[])
{
SOCKETservSock;
SOCKADDR_INservAddr;
WSAOVERLAPPEDoverLapped;
HANDLEhEvent;
WSABUFwsaBuf;
DWORDret,flags=0;
WSADATAwsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
//創ä¡ä建¡§支¡ì持?IO復¡ä用®?的Ì?套¬¡Á接¨®字Á?
servSock=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
if(servSock==INVALID_SOCKET)
ErrorHandler("WSASocket Error");
memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family=AF_INET;
servAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
servAddr.sin_port=htons(atoi("8888"));
connect(servSock,(SOCKADDR*)&servAddr,sizeof(servAddr));
memset(&overLapped,0,sizeof(overLapped));
wsaBuf.buf=buf;
wsaBuf.len=BUF_SIZE;
hEvent=WSACreateEvent();
overLapped.hEvent=hEvent;
if(WSASend(servSock,&wsaBuf,1,&recvBytes,flags,&overLapped,CompletionRoutine)==SOCKET_ERROR)
{
if(GetLastError()==WSA_IO_PENDING)
puts("background data send");
}
ret=WSAWaitForMultipleEvents(1,&hEvent,false,WSA_INFINITE,true);
if(ret==WAIT_IO_COMPLETION)
puts("Overlapped I/O Compeleted");
else
ErrorHandler("WSASend Error");
WSACloseEvent(hEvent);
closesocket(servSock);
WSACleanup();
return 0;
}
void CALLBACK CompletionRoutine(DWORD dwError,DWORDszRecvBytes,LPWSAOVERLAPPED lpOverlapped,DWORD flags)
{
if(dwError!=0)
{
ErrorHandler("CompletionRoutine Error");
}
else
{
recvBytes=szRecvBytes;
printf("Send Message:%s\n",buf);
}
}
void ErrorHandler(const char* message)
{
fputs(message,stderr);
fputc('/n',stderr);
exit(1);
}
Github位置:https://github.com/HymanLiuTS/NetDevelopment克隆本專案:git clone git@github.com:HymanLiuTS/NetDevelopment.git獲取本文原始碼:
git checkout NL55
相關推薦
網路程式設計(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
網路程式設計(53)—— Windows下使用WSAAsyncSelect實現視窗處理socket訊息
一、引言 上一文中我們介紹了使用WSAEventSelect實現非同步通知IO的方法,本文我們主要討論下使用WSAAsyncSelect處理socket的方法。本文的主要目標,是建立一個帶介面的回聲服務端,接收並返回客戶端傳過來的字串,並在介面上顯示該字串。為
網路程式設計(46)—— windows核心物件的兩種狀態
一、 什麼是核心物件? 我們知道程序、執行緒、檔案、互斥、訊號量這些都是作業系統級別的資源。我們在使用這些資源時,實際上都是由作業系統進行建立和管理的。作業系統為了管理這些資 源,會在其內部建立一個數據塊,也可以理解為一個結構體物件。這個資料塊就是核心物件。
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
python------Socket網路程式設計(二)粘包問題
一.socket網路程式設計 粘包:服務端兩次傳送指令在一起,它會把兩次傳送內容合在一起傳送,稱為粘包,從而出現錯誤。 解決方法:(比較low的方法) 有些需要實時更新的,用sleep有延遲,不能這樣解決問題。 解決方法之高階方法: 客戶端: 二.傳送檔案 ftp s
網路程式設計(一):埠那些事兒
TCP和UDP協議都存在一個叫做埠的東西,但埠卻不是IP協議的一部分。 埠被設計出來主要是為了給協議棧和應用對應: 協議棧用埠號將資料分配給不同的應用層程式 應用層程式用埠號去區分不同的連線,參見之前提到過的“四元組” TCP和UDP協議都使用了埠號(Port num