1. 程式人生 > >一個簡單的IOCP伺服器例項

一個簡單的IOCP伺服器例項

//////////////////////////////////////////////////////////
// initsock.h檔案

#include <winsock2.h>
#pragma comment(lib, "WS2_32")	// 連結到WS2_32.lib

class CInitSock		
{
public:
	CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
	{
		// 初始化WS2_32.dll
		WSADATA wsaData;
		WORD sockVersion = MAKEWORD(minorVer, majorVer);
		if(::WSAStartup(sockVersion, &wsaData) != 0)
		{
			exit(0);
		}
	}
	~CInitSock()
	{	
		::WSACleanup();	
	}
};
/////////////////////////////////////////////////
// IOCPDemo.cpp檔案			除錯通過


#include "initsock.h"
#include <stdio.h>
#include <windows.h>

// 初始化Winsock庫
CInitSock theSock;

#define BUFFER_SIZE 1024

typedef struct _PER_HANDLE_DATA		// per-handle資料
{
	SOCKET s;			// 對應的套節字控制代碼
	sockaddr_in addr;	// 客戶方地址
} PER_HANDLE_DATA, *PPER_HANDLE_DATA;


typedef struct _PER_IO_DATA			// per-I/O資料
{
	OVERLAPPED ol;			// 重疊結構
	char buf[BUFFER_SIZE];	// 資料緩衝區
	int nOperationType;		// 操作型別
#define OP_READ   1
#define OP_WRITE  2
#define OP_ACCEPT 3
} PER_IO_DATA, *PPER_IO_DATA;


DWORD WINAPI ServerThread(LPVOID lpParam)
{
	// 得到完成埠物件控制代碼
	HANDLE hCompletion = (HANDLE)lpParam;

	DWORD dwTrans;
	PPER_HANDLE_DATA pPerHandle;
	PPER_IO_DATA pPerIO;
	while(TRUE)
	{
		// 在關聯到此完成埠的所有套節字上等待I/O完成
		BOOL bOK = ::GetQueuedCompletionStatus(hCompletion, 
			&dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIO, WSA_INFINITE);
		if(!bOK)						// 在此套節字上有錯誤發生
		{
			::closesocket(pPerHandle->s);
			::GlobalFree(pPerHandle);
			::GlobalFree(pPerIO);
			continue;
		}
		
		if(dwTrans == 0 &&				// 套節字被對方關閉
			(pPerIO->nOperationType == OP_READ || pPerIO->nOperationType == OP_WRITE))	
			
		{
			::closesocket(pPerHandle->s);
			::GlobalFree(pPerHandle);
			::GlobalFree(pPerIO);
			continue;
		}

		switch(pPerIO->nOperationType)	// 通過per-I/O資料中的nOperationType域檢視什麼I/O請求完成了
		{
		case OP_READ:	// 完成一個接收請求
			{
				static int count=1;
				pPerIO->buf[dwTrans] = '\0';
				//printf(pPerIO -> buf);
				printf("第%d次接收到資料為:%s\n",count++,pPerIO->buf);
				
				// 繼續投遞接收I/O請求
				WSABUF buf;
				buf.buf = pPerIO->buf ;
				buf.len = BUFFER_SIZE;
				pPerIO->nOperationType = OP_READ;

				DWORD nFlags = 0;
				::WSARecv(pPerHandle->s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);
			}
			break;
		case OP_WRITE: // 本例中沒有投遞這些型別的I/O請求
		case OP_ACCEPT:
			break;
		}
	}
	return 0;
}


void main()
{
	int nPort = 9090;
	// 建立完成埠物件,建立工作執行緒處理完成埠物件中事件
	HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);

	SYSTEM_INFO siSys;
	//獲取CPU數目
	GetSystemInfo(&siSys);
	//建立一定數目的工作者執行緒,本例中以一個處理器一個執行緒搭配
	for(int i = 0; i<(int)siSys.dwNumberOfProcessors*2; i++)//NumberOfProcessors
	{
		::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);
	}
	
	// 建立監聽套節字,繫結到本地地址,開始監聽
	SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN si;
	si.sin_family = AF_INET;
	si.sin_port = ::ntohs(nPort);
	si.sin_addr.S_un.S_addr = INADDR_ANY;
	::bind(sListen, (sockaddr*)&si, sizeof(si));
	::listen(sListen, 5);

	// 迴圈處理到來的連線
	while(TRUE)
	{
		// 等待接受未決的連線請求
		SOCKADDR_IN saRemote;
		int nRemoteLen = sizeof(saRemote);
		SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen);

		// 接受到新連線之後,為它建立一個per-handle資料,並將它們關聯到完成埠物件。
		PPER_HANDLE_DATA pPerHandle = 
							(PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
		pPerHandle->s = sNew;
		memcpy(&pPerHandle->addr, &saRemote, nRemoteLen);
		::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);
	
		// 投遞一個接收請求
		PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
		pPerIO->nOperationType = OP_READ;
		WSABUF buf;
		buf.buf = pPerIO->buf;
		buf.len = BUFFER_SIZE;	
		DWORD dwRecv;
		DWORD dwFlags = 0;
		::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);
	}
}

相關推薦

一個簡單IOCP伺服器例項

////////////////////////////////////////////////////////// // initsock.h檔案 #include <winsock2.h> #pragma comment(lib, "WS2_32") //

一個簡單Python爬蟲例項(爬取的是前程無憂網的部分招聘資訊)

從今天開始學習爬蟲,展示我的第一個例項(用的是Python3寫的,Python2需要加個編碼方式吧,或許還有其他的不相容的地方吧,我就不知道了),把這分享給大家,希望對大家有一些幫助 import urllib,re import urllib.request import xlwt #開啟網頁

go搭建一個簡單web伺服器

Go語言裡面提供了一個完善的net/http包,通過http包可以很 方便的就搭建起來一個可以執行的web服務。同時使用這個包能很簡單地對web的路由,靜態檔案,模版,cookie等數 據進行設定和操

$python爬蟲系列(1)——一個簡單的爬蟲例項

  本文主要實現一個簡單的爬蟲,目的是從一個百度貼吧頁面下載圖片。 1. 概述 本文主要實現一個簡單的爬蟲,目的是從一個百度貼吧頁面下載圖片。下載圖片的步驟如下: 獲取網頁html文字內容; 分析html中圖片的html標籤特徵,用正則解析出所有的

Windows 上靜態編譯 Libevent 2.0.10 並實現一個簡單 HTTP 伺服器

      假設 Visual Studio 2005 的安裝路徑為“D:\Program Files\Microsoft Visual Studio 8\”,Libevent 2.0.10 解壓後的路徑為“D:\libevent-2.0.10-stable”。 編譯生成L

一個簡單web伺服器的java實現

一個簡單的web伺服器在不考慮其效能及健壯性的情況下,通常只需實現的功能包括伺服器的啟動,它用於監聽某一個埠,接收客戶端發來的請求,並將響應結果返回給客戶端。本文將介紹一個簡單web伺服器的實現原理,它本身只能處理某個目錄下的靜態資原始檔(文字、圖片等)。採用java

【Java.JMS】一個簡單的JMS例項

選擇ActiveMQ。 建立一個簡單的Maven工程,pom.xml如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-i

nodeJS從入門到放棄(一)用nodeJS搭建一個簡單伺服器

現在前端招聘要求中,經常會傾向於招懂nodeJS的前端開發人員,說明懂nodeJS的前端是更具競爭力的,所以動起了學nodeJs的打算,作博n篇,鞏固總結+給後面的人鋪路 前端開發人員學習

C語言實現一個簡單伺服器

C/S結構流程圖 服務端 socket函式 為了執行網路I/O,一個程序必須做的第一件事情就是建立一個socket函式 /* family 表示協議族 AF_INET(IPv4協議)、AF_INET6(IPv6協議)、AF_L

ROS的學習(十六)用C++寫一個簡單伺服器(service)和客戶端(client)

      我們將建立一個伺服器節點add_two_ints_server,它將會收到兩個整數,並且返回它們的和。切換目錄到之前建立的beginner_tutorials包下: cd ~/catkin_ws/src/beginner_tutorials      編輯sr

實現一個簡單的Spring例項

 程式碼: 1.配置檔案 applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="h

一個簡單伺服器與客戶端程式

最近在學習《深入理解計算機系統》,看到linux系統級的IO以及,socket通訊,所以乾脆參考書上例程,自己著手寫一個客戶端與伺服器的小程式。 並未使用csapp封裝好的open_clientfd與open_listenfd還有RIO包 主要功能就兩個: 1.伺服器讀客戶

【Android 網路資料解析實現一個簡單的新聞例項(一)】

      一般安卓在學到非同步任務AsyncTask之後都會有個安卓小專案的任務。得到(荔枝新聞,茶百科等)新聞網路介面來解析網路圖片或文字到ListView元件上顯示。其中要使用到的知識大概有:獲取網路資料(HttpUtil),解析網路資料(NewsParse),防止因

一個簡單的 SpringMVC 例項

新增 spring 框架所需的 jar 包。首先使用 Eclipse 新建一個動態的 Web 工程,命名為SpringMVCDome 如果沒有特別的要求可以將 spring-framework-4.2

微信企業號會議助手---一個簡單的SSM例項

前面基本的配置和工具類都差不多搞定了,如果有缺少在程式碼看到也不難理解了,下面來講一個小例子,迅速讓你理解ssm開發。 controller就是spring的控制器,所有前端的ajax的入口就在這,ajax怎麼進來呢? 我就講講一個跳轉到增加會議介面,和

一個簡單的REST例項

首先用ruby script/generate migration create_table_friends建立一個migration檔案, 寫入以下程式碼: class  CreateTableFriends < ActiveRecord::Migration 

j2EE一個簡單的servlet例項

1,手寫先一個jsp頁面核心程式碼如下:  <body> <form name="hello" action="<%=request.getContextPath()%>/Test" method="post"> <p>歡迎進

Nodejs 一個簡單的後臺例項

學了兩天的nodejs,寫了一個簡單的後臺例項,還有配套的與後臺進行互動的簡單網頁。 頁面端使用了jQuery進行控制, 後臺使用nodejs作為操控語言,使用express執行網路操作 整個工程結構如下: 根目錄--------------

採用Java nio 實現的一個簡單伺服器

伺服器程式碼: package server.nio; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream;

一個簡單IOCP伺服器/客戶端類

Any new allocation using VirtualAlloc* will always be rounded up to 64 KB (page file size) boundary so that if you allocate a new VAS region bound to the p