最近看了完成端口,很迷惑,GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE)),其中PerHandleData是在 CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);种关联的,但是PerIoData却没有关联,我看到唯一可能有点线索的大概就是投递WSARecv 参数里面 PerIOData里的WSAOVERLAPPED了,难道完成端口是通过WSAOVERLAPPED找到PerIOData的指针,再返回,这也太智能了,感觉上也很难做到。谢谢解答我是参考这个网址上的程序。http://hi.baidu.com/winnyang/blog/item/d9948a33acf21afe1b4cffd6.html /* 完成端口服务器 接收到客户端的信息,直接显示出来*/#include "winerror.h"
#include "Winsock2.h"
#pragma comment(lib, "ws2_32")#include "windows.h"
#include <iostream>
using namespace std;
/// 宏定义
#define PORT 5050
#define DATA_BUFSIZE 8192#define OutErr(a) cout << (a) << endl \
<< "出错代码:" << WSAGetLastError() << endl \
<< "出错文件:" << __FILE__ << endl \
<< "出错行数:" << __LINE__ << endl \#define OutMsg(a) cout << (a) << endl;
/// 全局函数定义
///////////////////////////////////////////////////////////////////////
//
// 函数名 : InitWinsock
// 功能描述 : 初始化WINSOCK
// 返回值 : void
//
///////////////////////////////////////////////////////////////////////
void InitWinsock()
{
// 初始化WINSOCK
WSADATA wsd;
if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
OutErr("WSAStartup()");
}
}///////////////////////////////////////////////////////////////////////
//
// 函数名 : BindServerOverlapped
// 功能描述 : 绑定端口,并返回一个 Overlapped 的Listen Socket
// 参数 : int nPort
// 返回值 : SOCKET
//
///////////////////////////////////////////////////////////////////////
SOCKET BindServerOverlapped(int nPort)
{
// 创建socket
SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);// 绑定端口
struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(nPort);
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
{
OutErr("bind Failed!");
return NULL;
}// 设置监听队列为200
if(listen(sServer, 200) != 0)
{
OutErr("listen Failed!");
return NULL;
}
return sServer;
}
/// 结构体定义
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[DATA_BUFSIZE];
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
typedef struct
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
DWORD WINAPI ProcessIO(LPVOID lpParam)
{
HANDLE CompletionPort = (HANDLE)lpParam;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;while(true)
{ if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
{
cout << "closing socket" << PerHandleData->Socket << endl;
closesocket(PerHandleData->Socket);
delete PerIoData;
delete PerHandleData;
continue;
}
else
{
OutErr("GetQueuedCompletionStatus failed!");
}
return 0;
}
// 说明客户端已经退出
if(BytesTransferred == 0)
{
cout << "closing socket" << PerHandleData->Socket << endl;
closesocket(PerHandleData->Socket);
delete PerIoData;
delete PerHandleData;
continue;
} // 取得数据并处理
cout << PerHandleData->Socket << "发送过来的消息:" << PerIoData->Buffer << endl; // 继续向 socket 投递WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE;
WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}return 0;
}void main()
{
InitWinsock();HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);// 根据系统的CPU来创建工作者线程
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);for(int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
{
HANDLE hProcessIO = CreateThread(NULL, 0, ProcessIO, CompletionPort, 0, NULL);
if(hProcessIO)
{
CloseHandle(hProcessIO);
}
}// 创建侦听SOCKET
SOCKET sListen = BindServerOverlapped(PORT);
SOCKET sClient;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
while(true)
{
// 等待客户端接入
//sClient = WSAAccept(sListen, NULL, NULL, NULL, 0);
sClient = accept(sListen, 0, 0);
cout << "Socket " << sClient << "连接进来" << endl;
PerHandleData = new PER_HANDLE_DATA();
PerHandleData->Socket = sClient; // 将接入的客户端和完成端口联系起来
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0); // 建立一个Overlapped,并使用这个Overlapped结构对socket投递操作
PerIoData = new PER_IO_OPERATION_DATA();
ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE; // 投递一个WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}DWORD dwByteTrans;
PostQueuedCompletionStatus(CompletionPort, dwByteTrans, 0, 0);
closesocket(sListen);
}
#include "Winsock2.h"
#pragma comment(lib, "ws2_32")#include "windows.h"
#include <iostream>
using namespace std;
/// 宏定义
#define PORT 5050
#define DATA_BUFSIZE 8192#define OutErr(a) cout << (a) << endl \
<< "出错代码:" << WSAGetLastError() << endl \
<< "出错文件:" << __FILE__ << endl \
<< "出错行数:" << __LINE__ << endl \#define OutMsg(a) cout << (a) << endl;
/// 全局函数定义
///////////////////////////////////////////////////////////////////////
//
// 函数名 : InitWinsock
// 功能描述 : 初始化WINSOCK
// 返回值 : void
//
///////////////////////////////////////////////////////////////////////
void InitWinsock()
{
// 初始化WINSOCK
WSADATA wsd;
if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
OutErr("WSAStartup()");
}
}///////////////////////////////////////////////////////////////////////
//
// 函数名 : BindServerOverlapped
// 功能描述 : 绑定端口,并返回一个 Overlapped 的Listen Socket
// 参数 : int nPort
// 返回值 : SOCKET
//
///////////////////////////////////////////////////////////////////////
SOCKET BindServerOverlapped(int nPort)
{
// 创建socket
SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);// 绑定端口
struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(nPort);
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
{
OutErr("bind Failed!");
return NULL;
}// 设置监听队列为200
if(listen(sServer, 200) != 0)
{
OutErr("listen Failed!");
return NULL;
}
return sServer;
}
/// 结构体定义
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[DATA_BUFSIZE];
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
typedef struct
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
DWORD WINAPI ProcessIO(LPVOID lpParam)
{
HANDLE CompletionPort = (HANDLE)lpParam;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;while(true)
{ if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
{
cout << "closing socket" << PerHandleData->Socket << endl;
closesocket(PerHandleData->Socket);
delete PerIoData;
delete PerHandleData;
continue;
}
else
{
OutErr("GetQueuedCompletionStatus failed!");
}
return 0;
}
// 说明客户端已经退出
if(BytesTransferred == 0)
{
cout << "closing socket" << PerHandleData->Socket << endl;
closesocket(PerHandleData->Socket);
delete PerIoData;
delete PerHandleData;
continue;
} // 取得数据并处理
cout << PerHandleData->Socket << "发送过来的消息:" << PerIoData->Buffer << endl; // 继续向 socket 投递WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE;
WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}return 0;
}void main()
{
InitWinsock();HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);// 根据系统的CPU来创建工作者线程
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);for(int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
{
HANDLE hProcessIO = CreateThread(NULL, 0, ProcessIO, CompletionPort, 0, NULL);
if(hProcessIO)
{
CloseHandle(hProcessIO);
}
}// 创建侦听SOCKET
SOCKET sListen = BindServerOverlapped(PORT);
SOCKET sClient;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
while(true)
{
// 等待客户端接入
//sClient = WSAAccept(sListen, NULL, NULL, NULL, 0);
sClient = accept(sListen, 0, 0);
cout << "Socket " << sClient << "连接进来" << endl;
PerHandleData = new PER_HANDLE_DATA();
PerHandleData->Socket = sClient; // 将接入的客户端和完成端口联系起来
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0); // 建立一个Overlapped,并使用这个Overlapped结构对socket投递操作
PerIoData = new PER_IO_OPERATION_DATA();
ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE; // 投递一个WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}DWORD dwByteTrans;
PostQueuedCompletionStatus(CompletionPort, dwByteTrans, 0, 0);
closesocket(sListen);
}
解决方案 »
- VC++访问Oracle数据库问题
- vs中编译错误,帮忙看看!
- 请教:关于CString 数组的问题。谢谢
- 怎样用菜单的对话框来实现自己的类对象的初始化--调用此类的构造函数--并且为构造函数传递用户输入的参数(在对话框中)
- 点击一个文件,由注册的文件打开,这是怎么做到的?
- "动态链结库不能静态调用"这句话对吗?
- 公司让我做ISA的实时控制,有谁能提供帮助,我将不胜感激!!
- 如何选择VC的书籍
- 《标准C++宝典》和《C++大学教程》哪本书好一些?有人看了吗?
- 怎样使对话框及其上的控件的大小不随分辨率变化?
- 请教:如何在CFormView中添加背景图片而不覆盖其他控件?
- 不想工作了,想好好休息休息,正在犹豫中
正好也在看这个 《windows网络编程技术》第八章中也有介绍
而PerIoData包含有一个OVERLAPPED成员,
所以可以将OVERLAPPED指针转换成PER_IO_OPERATION_DATA的指针PerIoData
这是设计上的技巧,
成员结构体指针转换最好是使用CONTAINING_RECORD这只是简单例子,
很多东西还要扩展
使用内存池,工作队列
AcceptEx替换accept
WSARecv传入的参数都是分开的,都是PER_IO_OPERATION_DATA结构里的值,但是最后却以PER_IO_OPERATION_DATA结构题一起返回,感觉上比较奇怪,完成端口是如何知道怎样填充你的PER_IO_OPERATION_DATA结构的呢
1、告诉你是哪一个IO操作完成了,你可以通过OVERLAPPED指针来判断:哪一个IO操作传入了这个指针,就是哪个IO操作完成了
2、这个指针我用完了,你可以销毁了至于这个指针所指向的数据内容,与你的应用无关,你不用去理会
这种机制的适应性很强,对于某些特殊的应用来说,它可以通过维护一个OVERLAPPED指针链表来跟踪每一个IO操作的完成次序等细节
但对于大多数应用来说,它只关心我收到了什么,发出去了什么,它就没必要去维护一个OVERLAPPED指针链表
最简单的方法就是将OVERLAPPED指针“扩展”,定义一个包含OVERLAPPED结构的自定义结构CIOCPBuffer
将IO操作相关的信息,通常包括操作类型、发送或受到的数据的缓冲区等等,一块扔到CIOCPBuffer中去这样,只要每次调用WSARecv、WSASend时创建一个CIOCPBuffer,将操作类型、数据缓冲区设置好,然后将OVERLAPPED成员的指针传入
而在GetQueuedCompletionStatus得到OVERLAPPED指针后,可以简单地通过OVERLAPPED指针得到CIOCPBuffer指针
这样就可以简单地获取这次IO的操作类型,以及传输的数据
ListenSocket放到单独线程中循环WSAAccept即可,不要关联完成端口,完成端口只处理收发包,不处理监听
IOCP不是为了让你能够响应大量的connect请求——这一点很容易做到,根本不需要使用IOCP机制IOCP的本质是用适量的线程数来处理大量的IO请求,避免因使用大量线程而频繁切换线程上下文数据所导致的系统资源浪费
2 IOCP本来就是为了完成高并发的连接。它的目的就是为了同时处理成百上千个套接字。
一切都要看需求现实的典型需求只有两种:1、无用户状态,短连接应用,例如域名服务器
2、有用户状态,长连接应用,例如游戏服务器前者需要频繁创建套接字,但IO负担并不大,所以,它适合AcceptEx,但没必要用IOCP
后者对IO负担很大,但无需频繁创建套接字,所以,它应该使用IOCP,但不必使用AcceptEx所以,偶“严重不推荐在IOCP服务器中使用AcceptEx”
你自己用最WSAAccept写个监听程序,再写个客户端开多线程对服务端保持每秒10个以上的连接请求,连续跑几天
最后统计下连接请求总次数和成功次数,再看看你服务端的内存消耗有多大变化——我做过这样的测试,你有没有???
对于长连接应用而言,使用SOCKET池带来的效率提升非常有限
相对使用AccpetEx带来的逻辑复杂度而言,可以忽略大概微软对WSAccept的实现,或者对SOCKET资源管理本来就内含了一定的优化措施
所以,凡事都要试过再下结论!