windows Socket程式設計之重疊IO模型
上一篇文章我們講了EventSelect網路模型,它已經解決了等待資料到來的這一大部分時間,但是它還有一小部分時間沒有節省下來。那就是把資料從網絡卡的緩衝區拷貝到我們應用程式的緩衝區裡邊。而這一篇的重疊IO模型就是將這一小部分的時間也給節省了下來。
首先,我們在主執行緒裡邊初始化網路環境,然後建立監聽的socket,接下來,執行繫結,監聽的操作,然後,建立一個工作者執行緒來對客戶進行服務。執行以上操作之後呢,是一個死迴圈。在這個迴圈裡邊,我們首先呼叫accept函式來對一個客戶進行連線操作。然後將該函式返回的客戶端的socket儲存到我們定義的一個全域性socket數組裡邊進去。然後對我們自定義的結構體單IO操作分配一個空間,其宣告如下:
注意,該結構體是我們自己定義的,其中第一個引數overlapped結構體是最重要的,必須把它放在第一個宣告處。因為我們要的是overlapped的地址,而其它的則是我們自己進行擴充套件的,大家也可以進行自己的擴充套件。那什麼叫做單IO操作呢,比如說你請了一個人來專門打理你的店鋪,你每天呢都會發一個資訊給這個人,資訊的內容就是叫他每天干一些指定的的事情,而這個資訊就是我們這個單IO操作這個資料結構所指定的內容。typedef struct { WSAOVERLAPPED overlap;<span style="white-space:pre"> </span>//OVERLAPPED結構,該結構裡邊有一個event事件物件 WSABUF Buffer;<span style="white-space:pre"> </span>//WSABUF結構,裡邊有一個buf的大小,還有一個指標指向buf char szMessage[MSGSIZE];<span style="white-space:pre"> </span>//訊息資料 DWORD NumberOfBytesRecvd;<span style="white-space:pre"> </span>//儲存接收到的位元組數 DWORD Flags;<span style="white-space:pre"> </span>//標誌位 }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
我們在堆上給我們的單IO結構分配完空間之後呢,我們來對它進行初始化,我們把Buffer裡面的指標指向szMessage,把裡面的大小指定為szMessage的大小。然後,呼叫WSACreateEvent建立一個事件物件,將這個事件物件賦予給overlappped裡邊的事件物件。
最後,在迴圈的末尾我們呼叫WSARecv,來對資料進行接收,其宣告如下:
int WSARecv(
SOCKET s, <span style="white-space:pre"> </span>//客戶連線的socket
LPWSABUF lpBuffers, <span style="white-space:pre"> </span>//WSABUFFER指標
DWORD dwBufferCount, <span style="white-space:pre"> </span>//Buffer的個數,一般這裡給個1
LPDWORD lpNumberOfBytesRecvd, <span style="white-space:pre"> </span>//接收的位元組數
LPDWORD lpFlags, <span style="white-space:pre"> </span>//標誌位
LPWSAOVERLAPPED lpOverlapped, <span style="white-space:pre"> </span>//overlaopped結構地址
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine <span style="white-space:pre"> </span>//沒啥用,為NULL
);
注意,該函式是非同步的,也就是說它呼叫完就直接進行返回了,不用等待。所以整個迴圈,只有剛開始接收到一個客戶的連線之後,我們就暢通無阻的往下執行一遍,然後再回到迴圈的開始繼續等待客戶的連線。接下來,看下工作者執行緒。
工作者執行緒,也是一個死迴圈,它和我們的EventSelect一樣,剛開始也是呼叫了WSAWaitForMultipleEvents,來監控我們的socket陣列哪一個有訊號了,由於這個函式最多隻能監控64個socket,所以我們的服務端只能同時進行64個客戶的資料收發。呼叫完該函式之後,它會返回一個索引值,我們將呼叫WSAResetEvent,將我們全域性event數組裡邊那個有訊號的手動重置為無訊號狀態。因為我們用WSACreateEvent建立的事件物件是以手動重置的方式建立的。如果,不重置成無訊號的狀態,那麼就像上面我們舉得那個例子一樣,我們請的那個人,他第二天檢視資訊的時候,還會繼續的執行昨天的工作。
接下來,是我們重疊IO模型裡邊和EventSelect裡邊最大的不同點,我們會呼叫WSAGetOverlappedResult,來判斷重疊IO呼叫是否成功,其宣告如下:
BOOL WSAGetOverlappedResult(
SOCKET s, //有訊號的那個socket
LPWSAOVERLAPPED lpOverlapped, //overlapped結構地址
LPDWORD lpcbTransfer, //接收的位元組數
BOOL fWait, //TRUE表示操作完成就返回
LPDWORD lpdwFlags //標誌位
);
這個函式的第三個引數和我們的WSARecv的第四個引數是一樣的,作業系統會改寫這個值,若該值為0表示客戶端斷開連線或該資料傳輸失敗了。如果沒有失敗,我們就將資料儲存到我們那個單IO結構裡邊的szMessage數組裡邊。然後再次呼叫WSARecv,告訴作業系統繼續幫我們監控這個socket。以下是重疊IO的例項程式碼:
#include <winsock2.h>
#include <stdio.h>
#define PORT 6000
#define MSGSIZE 1024
#pragma comment (lib, "Ws2_32.lib")
BOOL WinSockInit()
{
WSADATA data = {0};
if(WSAStartup(MAKEWORD(2, 2), &data))
return FALSE;
if ( LOBYTE(data.wVersion) !=2 || HIBYTE(data.wVersion) != 2 ){
WSACleanup();
return FALSE;
}
return TRUE;
}
typedef struct
{
WSAOVERLAPPED overlap;
WSABUF Buffer;
char szMessage[MSGSIZE];
DWORD NumberOfBytesRecvd;
DWORD Flags;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
int g_iTotalConn = 0;
SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];
LPPER_IO_OPERATION_DATA g_pPerIODataArr[MAXIMUM_WAIT_OBJECTS];
DWORD WINAPI WorkerThread(LPVOID);
void Cleanup(int);
int main()
{
SOCKET sListen, sClient;
SOCKADDR_IN local, client;
DWORD dwThreadId;
int iaddrSize = sizeof(SOCKADDR_IN);
// 初始化環境
WinSockInit();
// 建立監聽socket
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 繫結
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));
// 監聽
listen(sListen, 3);
// 建立工作者執行緒
CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
while (TRUE)
{
// 接受連線
sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
g_CliSocketArr[g_iTotalConn] = sClient;
// 分配一個單io操作資料結構
g_pPerIODataArr[g_iTotalConn] = (LPPER_IO_OPERATION_DATA)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(PER_IO_OPERATION_DATA));
//初始化單io結構
g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE;
g_pPerIODataArr[g_iTotalConn]->Buffer.buf = g_pPerIODataArr[g_iTotalConn]->szMessage;
g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();
// 開始一個非同步操作
WSARecv(
g_CliSocketArr[g_iTotalConn],
&g_pPerIODataArr[g_iTotalConn]->Buffer,
1,
&g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd,
&g_pPerIODataArr[g_iTotalConn]->Flags,&g_pPerIODataArr[g_iTotalConn]->overlap,
NULL);
g_iTotalConn++;
}
closesocket(sListen);
WSACleanup();
return 0;
}
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
int ret, index;
DWORD cbTransferred;
while (TRUE)
{
//判斷是否有訊號
ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
continue;
index = ret - WSA_WAIT_EVENT_0;
//手動設定為無訊號
WSAResetEvent(g_CliEventArr[index]);
//判斷該重疊呼叫到底是成功,還是失敗
WSAGetOverlappedResult(
g_CliSocketArr[index],
&g_pPerIODataArr[index]->overlap,
&cbTransferred,
TRUE,
&g_pPerIODataArr[g_iTotalConn]->Flags);
//若呼叫失敗
if (cbTransferred == 0)
Cleanup(index);//關閉客戶端連線
else
{
//將資料儲存到szMessage裡邊
g_pPerIODataArr[index]->szMessage[cbTransferred] = '\0';
//這裡直接就轉發回去了
send(g_CliSocketArr[index], g_pPerIODataArr[index]->szMessage,cbTransferred, 0);
// 進行另一個非同步操作
WSARecv(g_CliSocketArr[index],
&g_pPerIODataArr[index]->Buffer,
1,
&g_pPerIODataArr[index]->NumberOfBytesRecvd,
&g_pPerIODataArr[index]->Flags,
&g_pPerIODataArr[index]->overlap,
NULL);
}
}
return 0;
}
void Cleanup(int index)
{
closesocket(g_CliSocketArr[index]);
WSACloseEvent(g_CliEventArr[index]);
HeapFree(GetProcessHeap(), 0, g_pPerIODataArr[index]);
if (index < g_iTotalConn - 1)
{
g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];
g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];
g_pPerIODataArr[index] = g_pPerIODataArr[g_iTotalConn - 1];
}
g_pPerIODataArr[--g_iTotalConn] = NULL;
}
相關推薦
windows Socket程式設計之重疊IO模型
上一篇文章我們講了EventSelect網路模型,它已經解決了等待資料到來的這一大部分時間,但是它還有一小部分時間沒有節省下來。那就是把資料從網絡卡的緩衝區拷貝到我們應用程式的緩衝區裡邊。而這一篇的重疊IO模型就是將這一小部分的時間也給節省了下來。 首先,我們在主執行緒裡邊
Windows Socket程式設計之UDP實現大檔案的傳輸
前言:本文實現以下功能:在客戶端,使用者選擇本地的某個檔案,併發送到伺服器端。在伺服器端,接收客戶端傳輸的資料流,並按IP 地址儲存在伺服器端(文件名重複的,可以覆蓋)。如果傳輸過程中伺服器端發現客戶端斷開,伺服器端應刪除檔案,並在螢幕上提示,如“IP:1.2.3.4 發來a
windows Socket程式設計之UDP的服務端和客戶端
上一篇講了TCP的服務端和客戶端,本篇文章來介紹一下UDP的服務端和客戶端。 相比TCP來說,UDP相對比較簡單,剛開始的時候,和TCP一樣都需要先進行網路環境的初始化,即呼叫WSAStartup函式。然後呢,我們也需要建立一個socket,這個socket和TCP的那個s
Windows Socket程式設計之TCP實現大檔案的傳輸
前言: 本文實現以下功能: 在客戶端,使用者選擇本地的某個檔案,併發送到伺服器端。 在伺服器端,接收客戶端傳輸的資料流,並按IP 地址儲存在伺服器端(文 件名重複的,可以覆蓋)。 如果傳輸過程中伺服器端發現客戶端斷開,伺服器端應刪除檔案,並在螢幕 上提示,如“IP:1.
IOCP程式設計之重疊IO
其實這個標題有點“標題黨”的味道,為了大家搜尋方便我故意冠以IOCP程式設計之名,其實重疊IO程式設計並不一定需要IOCP,而IOCP程式設計就一定需要重疊IO。是不是已經被這句話給繞暈了?總之是為了更好的應用IOCP,所以要理解重疊IO。這篇文章的核心就是討論重疊IO的來龍
Socket程式設計模型之重疊IO(Overlapped I/O)模型
一、原理 Winsock2的釋出使得Socket I/O有了和檔案I/O統一的介面。我們可以通過使用Win32檔案操縱函式ReadFile和WriteFile來進行Socket I/O。伴隨而來的,用於普通檔案I/O的重疊I/O模型和完成埠模型對Socket I/O也適用
網路伺服器程式設計——重疊IO模型
4.3.4重疊I/O模型 非同步IO和同步IO的區別: 同步IO中,執行緒啟動一個IO操作然後就立即進入等待狀態,直到IO操作完成後才醒來繼續執行。 非同步IO中,執行緒傳送一個IO請求到核心,然後繼續處理其他的事情,核心完成IO請求後,將會通知執行緒IO操作完成了。重疊IO屬於非同步I
Windows網路程式設計之面向非連線的Socket程式設計
byzxy,Java/C++程式設計交流群:168424095 面向非連線的Socket通訊是基於UDP的。 UDP是User Datagram Protocol的簡稱,中文名是使用者資料包協議,
windows環境下Socket程式設計的幾種模型
阻塞模型, 這個模型是講解計算機網路時被作為例子介紹的,也是最簡單的。其基本原理是:首先建立一個socket連線,然後對其進行操作,比如,從該socket讀資料。因為網路傳輸是要一定的時間的,即使網路通暢的情況下,接受資料的操作也要花費時間。對於一個簡單的單執行緒程式,接收資料的過程是無法處理其他操作的。
Socket程式設計之TCP的簡單實現
Client import java.io.*; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.Socket; public class Client { pub
C# socket 程式設計之 accept() 函式返回值解析
accept() 函式會返回一個新的套接字,這個新的套接字在伺服器端與客戶端進行通訊。 伺服器端的繫結監聽是一個套接字,與客戶端通訊的是另一個套接字(accept函式返回的套接字,注意這裡不是返回客戶端的套接字,返回的套接字是新建立在伺服器上的,與客戶端收發訊息用的) 下面這段程式碼,是
python下socket程式設計之TCP連線狀態
1. 引言 python作為一門膠水語言,可以在各個領域上作為快速開發的工具,大大提高開發者處理事務的效率。在網際網路行業中,不管是對網路伺服器的開發,還是對網路客戶端,例如爬蟲的開發中,都會涉及到底層的執行原理,那就是socket程式設計,那麼今天,我們將對python下的socke
Windows核心程式設計之執行緒
執行緒組成兩部分: 1. 一個執行緒的核心物件,作業系統用它管理執行緒。 2. 一個執行緒棧,用於維護執行緒執行時所需的所有函式引數和區域性變數。 何時建立執行緒?舉例: 作業系統的Windows Indexing Services,磁碟碎片整理程式等,都是使用多執行緒進行效能優化的
windows核心程式設計之程序
什麼是程序? 程序是一個正在執行程式的例項。由兩部分組成:一個核心物件,用於管理程序以及一個地址空間,包含所有可執行檔案或DLL模組的程式碼和資料,此外還包含動態記憶體分配。 在分析程序之前,先看下windows程式是如何建立的? Windows應用程式分為CUI和GUI程式,即控
網路程式設計之——七層模型與TCP三段握手與四次斷開
轉載請註明出處:https://blog.csdn.net/l1028386804/article/details/83046311 一、C/S架構 客戶端/服務端架構 二、OSI七層架構 七層模型,亦稱OSI(Open System Interconnection)參考模型,是
android程式設計3 socket程式設計之udp傳送
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Windows平臺程式設計之OnCreate函式的說明
OnCreate是一個訊息響應函式,是響應WM_CREATE訊息的一個函式,而WM_CREATE訊息是由Create函式呼叫的。 在view類中,Create 是虛擬函式由框架呼叫,是用來“生成一個視窗的子視窗”。 而OnCreate
java Socket程式設計之TCP基本原理
通訊原理: 1.伺服器程式建立一個ServerSocket,呼叫accept方法等待客戶機來連線。 2.客戶端程式建立一個Socket,請求與伺服器建立連線。 3.伺服器接收客戶機的連線請求,同時建立一個新的Socket與客戶端建立連線。伺服器繼續等待新的請求。 關鍵類: ServerS
2018.12.02 Socket程式設計之初識Socket
Socket程式設計主要分為TCP/UDP/SCTP三種,每一種都有各自的優點,所以會根據實際情況決定選用何種Socket,今天開始我將會逐步學習Socket程式設計,並將學習過程記錄於此。 今天學習的是TCP程式設計。 TCP基本客戶端與服務端的套接字函式: Client: socket/conn
Socket程式設計 之 一種死鎖現象
剛接觸socket程式設計的過程中,很容易出現死鎖的現象。下面我來介紹一種死鎖的原因和解決的方法。 先來看這段程式碼: /*客戶端傳送一個資訊到服務端*/ Socket socket = new Socket("127.0.0.1", 8081); outputStream