`
deepfuture
  • 浏览: 4333469 次
  • 性别: Icon_minigender_1
  • 来自: 湛江
博客专栏
073ec2a9-85b7-3ebf-a3bb-c6361e6c6f64
SQLite源码剖析
浏览量:79428
1591c4b8-62f1-3d3e-9551-25c77465da96
WIN32汇编语言学习应用...
浏览量:68376
F5390db6-59dd-338f-ba18-4e93943ff06a
神奇的perl
浏览量:101498
Dac44363-8a80-3836-99aa-f7b7780fa6e2
lucene等搜索引擎解析...
浏览量:281206
Ec49a563-4109-3c69-9c83-8f6d068ba113
深入lucene3.5源码...
浏览量:14608
9b99bfc2-19c2-3346-9100-7f8879c731ce
VB.NET并行与分布式编...
浏览量:65559
B1db2af3-06b3-35bb-ac08-59ff2d1324b4
silverlight 5...
浏览量:31320
4a56b548-ab3d-35af-a984-e0781d142c23
算法下午茶系列
浏览量:45206
社区版块
存档分类
最新评论

WIN网络编程-重叠I/0服务器设计

阅读更多

///////////////////////////////////////////////////////
// OverlappedServer.cpp文件

//基于重叠I/0模型的简单的回显TCP服务器,它接受客户端连接之后,将从客

//户端接收到的数据再发送给客户端。本例采用了单线程。

//使用重叠I/O模型必须使用WSASocket函数创建套接字,传送数据使用

//WSASend、WSARecv、WSARecvFrom

//WSASocket函数创建套接字,这个套接字允许多个I/O同时占用。并使用pBuff

//er->pSocket->nOutstandingOps来统计其上的I/O数量。

//其中SOCKET_OBJ结构用来记录与套接字相关的信息,BUFFER_OBJ记录I/O信息。

//程序每投递一个I/O请求,都要申请一个BUFFER_OBJ对象,使用嵌在对象中

//的OVERLAPPED结构。所有未完成的I/O请求对应的BUFFER_OBJ对象组成了一个

//表,表头指针为全局变量g_pBufferHead。

//提交重叠I/O是重叠模型的关键,通过调用AcceptEx、WSARecv和WSASend函

//数实现。提交这些I/O后,线程便在重叠结构中的事件对象上等等,一旦I/O完成

//事件受信,等待函数返回。

//WSAGetLastError函数报告了WSA_IO_PENDING出错状态,表示I/O操作正在进行。

//AccepEx函数将几个套接字函数的功能集合成在了一起。如果它投递的请求成功完成,则执行了3个操作:

//1、接受了新的连接

//2、新连接的本地地址和远程地址都会返回

//3、接收到了远程主机发来的第一块数据

//

#include "../common/initsock.h"

#include<Mswsock.h>
#include <stdio.h>
#include <windows.h>

CInitSock theSock;

#define BUFFER_SIZE 1024

typedef struct _SOCKET_OBJ
{
SOCKETs;//套节字句柄
intnOutstandingOps;//记录此套节字上的重叠I/O数量

LPFN_ACCEPTEXlpfnAcceptEx;//扩展函数AcceptEx的指针(仅对监听套节字而言)
} SOCKET_OBJ, *PSOCKET_OBJ;

//重叠I/O都要提交到特定套节字上。在这些I/O完成之前,对方关闭了连接,

//或者连接发生错误,释放对应的SOCEKET_OBJ对象。

typedef struct _BUFFER_OBJ
{
OVERLAPPEDol;//重叠结构
char*buff;//send/recv/AcceptEx所使用的缓冲区
intnLen;//buff的长度
PSOCKET_OBJ pSocket;//此I/O所属的套节字对象

intnOperation;//提交的操作类型
#define OP_ACCEPT1
#define OP_READ2
#define OP_WRITE3

SOCKETsAccept;//用来保存AcceptEx接受的客户套节字(仅对监听套节字而言)
_BUFFER_OBJ *pNext;
} BUFFER_OBJ, *PBUFFER_OBJ;

HANDLEg_events[WSA_MAXIMUM_WAIT_EVENTS];//I/O事件句柄数组
intg_nBufferCount;//上数组中有效句柄数量
PBUFFER_OBJ g_pBufferHead, g_pBufferTail;//记录缓冲区对象组成的表的地址

// 申请套节字对象和释放套节字对象的函数
PSOCKET_OBJ GetSocketObj(SOCKET s)
{
PSOCKET_OBJ pSocket =(PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
if(pSocket != NULL)
{
pSocket->s =s;
}
return pSocket;
}
void FreeSocketObj(PSOCKET_OBJ pSocket)
{
if(pSocket->s !=INVALID_SOCKET)
::closesocket(pSocket->s);
::GlobalFree(pSocket);
}

PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket,ULONG nLen)
{
if(g_nBufferCount >WSA_MAXIMUM_WAIT_EVENTS - 1)
return NULL;

PBUFFER_OBJ pBuffer =(PBUFFER_OBJ)::GlobalAlloc(GPTR, sizeof(BUFFER_OBJ));
if(pBuffer != NULL)
{
pBuffer->buff =(char*)::GlobalAlloc(GPTR, nLen);
pBuffer->ol.hEvent= ::WSACreateEvent();
pBuffer->pSocket= pSocket;
pBuffer->sAccept= INVALID_SOCKET;

//将新的BUFFER_OBJ添加到列表中
if(g_pBufferHead == NULL)
{
g_pBufferHead= g_pBufferTail = pBuffer;
}
else
{
g_pBufferTail->pNext= pBuffer;
g_pBufferTail= pBuffer;
}
g_events[++ g_nBufferCount] =pBuffer->ol.hEvent;
}
return pBuffer;
}

void FreeBufferObj(PBUFFER_OBJ pBuffer)
{
// 从列表中移除BUFFER_OBJ对象
PBUFFER_OBJ pTest = g_pBufferHead;
BOOL bFind = FALSE;
if(pTest == pBuffer)
{
g_pBufferHead = g_pBufferTail =NULL;
bFind = TRUE;
}
else
{
while(pTest != NULL&& pTest->pNext !=pBuffer)
pTest =pTest->pNext;
if(pTest != NULL)
{
pTest->pNext= pBuffer->pNext;
if(pTest->pNext== NULL)
g_pBufferTail= pTest;
bFind =TRUE;
}
}
// 释放它占用的内存空间
if(bFind)
{
g_nBufferCount --;
::CloseHandle(pBuffer->ol.hEvent);
::GlobalFree(pBuffer->buff);
::GlobalFree(pBuffer);
}
}

PBUFFER_OBJ FindBufferObj(HANDLE hEvent)
{
PBUFFER_OBJ pBuffer = g_pBufferHead;
while(pBuffer != NULL)
{
if(pBuffer->ol.hEvent== hEvent)
break;
pBuffer =pBuffer->pNext;
}
return pBuffer;
}

void RebuildArray()
{
PBUFFER_OBJ pBuffer = g_pBufferHead;
int i = 1;
while(pBuffer != NULL)
{
g_events[i++] =pBuffer->ol.hEvent;
pBuffer =pBuffer->pNext;
}
}

BOOL PostAccept(PBUFFER_OBJ pBuffer)
{
PSOCKET_OBJ pSocket =pBuffer->pSocket;
if(pSocket->lpfnAcceptEx !=NULL)
{
// 设置I/O类型,增加套节字上的重叠I/O计数
pBuffer->nOperation= OP_ACCEPT;
pSocket->nOutstandingOps++;

//投递此重叠I/O
DWORD dwBytes;
pBuffer->sAccept=
::WSASocket(AF_INET,SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
BOOL b =pSocket->lpfnAcceptEx(pSocket->s,
pBuffer->sAccept,
pBuffer->buff,
BUFFER_SIZE -((sizeof(sockaddr_in) + 16) * 2),
sizeof(sockaddr_in)+ 16,
sizeof(sockaddr_in)+ 16,
&dwBytes,
&pBuffer->ol);
if(!b)
{
if(::WSAGetLastError()!= WSA_IO_PENDING)
returnFALSE;
}
return TRUE;
}
return FALSE;
};

BOOL PostRecv(PBUFFER_OBJ pBuffer)
{
// 设置I/O类型,增加套节字上的重叠I/O计数
pBuffer->nOperation =OP_READ;
pBuffer->pSocket->nOutstandingOps++;

// 投递此重叠I/O
DWORD dwBytes;
DWORD dwFlags = 0;
WSABUF buf;
buf.buf = pBuffer->buff;
buf.len = pBuffer->nLen;
if(::WSARecv(pBuffer->pSocket->s,&buf, 1, &dwBytes,&dwFlags,&pBuffer->ol, NULL) !=NO_ERROR)
{
if(::WSAGetLastError() !=WSA_IO_PENDING)
returnFALSE;
}
return TRUE;
}

BOOL PostSend(PBUFFER_OBJ pBuffer)
{
// 设置I/O类型,增加套节字上的重叠I/O计数
pBuffer->nOperation =OP_WRITE;
pBuffer->pSocket->nOutstandingOps++;

// 投递此重叠I/O
DWORD dwBytes;
DWORD dwFlags = 0;
WSABUF buf;
buf.buf = pBuffer->buff;
buf.len = pBuffer->nLen;
if(::WSASend(pBuffer->pSocket->s,
&buf,1, &dwBytes, dwFlags,&pBuffer->ol, NULL) !=NO_ERROR)
{
if(::WSAGetLastError() !=WSA_IO_PENDING)
returnFALSE;
}
return TRUE;
}

BOOL HandleIO(PBUFFER_OBJ pBuffer)
{
PSOCKET_OBJ pSocket =pBuffer->pSocket; //从BUFFER_OBJ对象中提取SOCKET_OBJ对象指针,为的是方便引用
pSocket->nOutstandingOps--;

// 获取重叠操作结果
DWORD dwTrans;
DWORD dwFlags;
BOOL bRet =::WSAGetOverlappedResult(pSocket->s,&pBuffer->ol,&dwTrans, FALSE, &dwFlags);
if(!bRet)
{
//在此套节字上有错误发生,因此,关闭套节字,移除此缓冲区对象。
//如果没有其它抛出的I/O请求了,释放此缓冲区对象,否则,等待此套节字上的其它I/O也完成
if(pSocket->s !=INVALID_SOCKET)
{
::closesocket(pSocket->s);
pSocket->s= INVALID_SOCKET;
}

if(pSocket->nOutstandingOps== 0)
FreeSocketObj(pSocket);

FreeBufferObj(pBuffer);
return FALSE;
}

// 没有错误发生,处理已完成的I/O
switch(pBuffer->nOperation)
{
case OP_ACCEPT://接收到一个新的连接,并接收到了对方发来的第一个封包
{
//为新客户创建一个SOCKET_OBJ对象
PSOCKET_OBJpClient = GetSocketObj(pBuffer->sAccept);

//为发送数据创建一个BUFFER_OBJ对象,这个对象会在套节字出错或者关闭时释放
PBUFFER_OBJpSend = GetBufferObj(pClient, BUFFER_SIZE);
if(pSend ==NULL)
{
printf("Too much connections! \n");
FreeSocketObj(pClient);
returnFALSE;
}
RebuildArray();

//将数据复制到发送缓冲区
pSend->nLen= dwTrans;
memcpy(pSend->buff,pBuffer->buff, dwTrans);

//投递此发送I/O(将数据回显给客户)
if(!PostSend(pSend))
{
//万一出错的话,释放上面刚申请的两个对象
FreeSocketObj(pSocket);
FreeBufferObj(pSend);
returnFALSE;
}
//继续投递接受I/O
PostAccept(pBuffer);
}
break;
case OP_READ:// 接收数据完成
{
if(dwTrans> 0)
{
//创建一个缓冲区,以发送数据。这里就使用原来的缓冲区
PBUFFER_OBJpSend = pBuffer;
pSend->nLen= dwTrans;

//投递发送I/O(将数据回显给客户)
PostSend(pSend);
}
else//套节字关闭
{

//必须先关闭套节字,以便在此套节字上投递的其它I/O也返回
if(pSocket->s!= INVALID_SOCKET)
{
::closesocket(pSocket->s);
pSocket->s= INVALID_SOCKET;
}

if(pSocket->nOutstandingOps== 0)
FreeSocketObj(pSocket);

FreeBufferObj(pBuffer);
returnFALSE;
}
}
break;
caseOP_WRITE:// 发送数据完成
{
if(dwTrans> 0)
{
//继续使用这个缓冲区投递接收数据的请求
pBuffer->nLen= BUFFER_SIZE;
PostRecv(pBuffer);
}
else//套节字关闭
{
//同样,要先关闭套节字
if(pSocket->s!= INVALID_SOCKET)
{
::closesocket(pSocket->s);
pSocket->s= INVALID_SOCKET;
}

if(pSocket->nOutstandingOps== 0)
FreeSocketObj(pSocket);

FreeBufferObj(pBuffer);
returnFALSE;
}
}
break;
}
return TRUE;
}


void main()
{
// 创建监听套节字,绑定到本地端口,进入监听模式
int nPort = 4567;
SOCKET sListen =
::WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);

//plisten可被多个重叠I/O占用
SOCKADDR_INsi;
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, 200);

// 为监听套节字创建一个SOCKET_OBJ对象
PSOCKET_OBJ pListen = GetSocketObj(sListen);

// 加载扩展函数AcceptEx
GUID GuidAcceptEx = WSAID_ACCEPTEX;
DWORD dwBytes;
WSAIoctl(pListen->s,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,
sizeof(GuidAcceptEx),
&pListen->lpfnAcceptEx,
sizeof(pListen->lpfnAcceptEx),
&dwBytes,
NULL,
NULL);

// 创建用来重新建立g_events数组的事件对象
g_events[0] = ::WSACreateEvent();

// 在此可以投递多个接受I/O请求
for(int i=0; i<5; i++)
{
PostAccept(GetBufferObj(pListen,BUFFER_SIZE));
}
::WSASetEvent(g_events[0]);

while(TRUE)
{
int nIndex =
::WSAWaitForMultipleEvents(g_nBufferCount+ 1, g_events, FALSE, WSA_INFINITE, FALSE);
if(nIndex ==WSA_WAIT_FAILED)
{
printf("WSAWaitForMultipleEvents()failed \n");
break;
}
nIndex = nIndex -WSA_WAIT_EVENT_0;
for(int i=0;i<=nIndex; i++)
{
int nRet =::WSAWaitForMultipleEvents(1, &g_events[i], TRUE,0, FALSE);
if(nRet ==WSA_WAIT_TIMEOUT)
continue;
else
{
::WSAResetEvent(g_events[i]);
//重新建立g_events数组
if(i== 0)
{
RebuildArray();
continue;
}

//处理这个I/O
PBUFFER_OBJpBuffer = FindBufferObj(g_events[i]);
if(pBuffer!= NULL)
{
if(!HandleIO(pBuffer))
RebuildArray();
}
}
}
}
}

分享到:
评论

相关推荐

    网络编程实用教程(第三版).zip

    1.1 网络编程相关的基本概念 1 1.1.1 网络编程与进程通信 1 1.1.2 Internet中网间进程的标识 3 1.1.3 网络协议的特征 7 1.2 三类网络编程 10 1.2.1 基于TCP/IP协议栈的网络编程 10 1.2.2 基于WWW应用的...

    windows网络编程(PDF)

    第一部分 传统网络API第1章 NetBIOS11.1 Microsoft NetBIOS21.1.1 LANA编号21.1.2 NetBIOS名字41.1.3 NetBIOS特性61.2 NetBIOS编程基础71.3 常规NetBIOS例程81.3.1 会话服务器:异步回调模型151.3.2 会话服务器:...

    Visual C++/Turbo C串口通信编程实践及源代码-3

    4.2.3 overlapped异步i/o重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 windows api串行通信函数 97 4.4 win32 api串口通信编程的一般流程和特殊实例 116 4.4.1 win32 api串口通信...

    Windows网络编程

    目者者录译者序前言第一部分 传统网络API第1章 NetBIOS11.1 Microsoft NetBIOS21.1.1 LANA编号21.1.2 NetBIOS名字41.1.3 NetBIOS特性61.2 NetBIOS编程基础71.3 常规NetBIOS例程81.3.1 会话服务器:异步回调模型151.3...

    windows网络编程

    目者者录译者序前言第一部分 传统网络API第1章 NetBIOS11.1 Microsoft NetBIOS21.1.1 LANA编号21.1.2 NetBIOS名字41.1.3 NetBIOS特性61.2 NetBIOS编程基础71.3 常规NetBIOS例程81.3.1 会话服务器:异步回调模型151.3...

    WINDOWS网络编程技术

    本书适合中、高级程序设计人员以及网络设计与管理人员参考。目者者录译者序前言第一部分 传统网络API第1章 NetBIOS11.1 Microsoft NetBIOS21.1.1 LANA编号21.1.2 NetBIOS名字41.1.3 NetBIOS特性61.2 NetBIOS编程基础...

    《WINDOWS网络编程技术》

    本书专门讨论Windows网络编程技术,覆盖Windows 95/98/NT 4/2000/CE平台,内容包括NetBIOS和Windows重定向器方法、Winsock方法、客户端远程访问服务器方法,本书论述深入浅出、用大量实例详解了微软网络API函数的...

    Visual C++/Turbo C串口通信编程实践及源代码-2

    4.2.3 overlapped异步i/o重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 windows api串行通信函数 97 4.4 win32 api串口通信编程的一般流程和特殊实例 116 4.4.1 win32 api串口通信...

    Visual C++/Turbo C串口通信编程实践 及源代码-1

    4.2.3 overlapped异步i/o重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 windows api串行通信函数 97 4.4 win32 api串口通信编程的一般流程和特殊实例 116 4.4.1 win32 api串口通信...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 第二版 电子版

    4.2.3 OVERLAPPED异步I/O重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 Windows API串行通信函数 97 4.4 Win32 API串口通信编程的一般流程和特殊实例 116 4.4.1 Win32 API串口通信编程的...

Global site tag (gtag.js) - Google Analytics