首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >关于access字段名,下面叙述错误的是_accepted是什么意思

关于access字段名,下面叙述错误的是_accepted是什么意思

作者头像
全栈程序员站长
发布于 2022-11-11 05:39:05
发布于 2022-11-11 05:39:05
71500
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

最基础的IOCP例子, 没有使用扩展函数AcceptEx: IOCP模型

* 关于iocp的核心就一点:

GetQueuedCompletionStatus 将携带返回2个重要的参数, 一个lpCompletionKey, 一个lpOverlapped.

lpCompletionKey : 是 CreateIoCompletionPort((HANDLE)clientSocket , hIOCP,(ULONG_PTR)自定义的结构,0); 跟

iocp绑定的一个自定义参数;

lpOverlapped : 是传递给 WSASend / WSARecv 的参数;

这2个参数最终会被GetQueuedCompletionStatus 携带回来.

同样的 , AcceptEx 也要传递一个Overlapped结构,现在问题来了,如果只调用了AcceptEx ,

GetQueuedCompletionStatus 是不会返回的, 因为只有跟 iocp 关联(CreateIoCompletionPort)的HANDLE / SOCKET 才会

被触发, 因此只需要把 监听套接字 跟iocp 关联即可;

下面代码使用了AccpetEx 和一个用于获取地址的扩展函数[此函数可以先忽略].

总体来说就是预先分配一些socket , 以及相关的内存块[到时有客户进来后,直接使用此内存块接受数据];

不再让accept系统调用来创建socket了.

所有需要注意的点都写在注释里了.

下面代码里没有使用 CancelIo 之类的函数,如果实际需要直接用 CancelIoEx 来取消无关线程的Overlapped操作,

另:在发送数据[WSASend] 完成后 , 需要检查是否发送完成, 如果没有发完需要继续发送.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <mswsock.h>
#include <deque>
#include <vector>
#define IO_ACCEPT 1
#define IO_READ  2
#define IO_WRITE 3
#define BUFFSIZE 4096
#define SPINCOUNT 4000
struct Per_IO_Data;
std::deque<Per_IO_Data*> io_pool;
CRITICAL_SECTION io_pool_cs;
LPFN_ACCEPTEX FuncAcceptEx = NULL;
LPFN_GETACCEPTEXSOCKADDRS FuncGetAddr  = NULL;
struct Per_Sock_Data{
SOCKET sock;
SOCKADDR_IN addr;
HANDLE iocp;
Per_Sock_Data():sock(INVALID_SOCKET), iocp(INVALID_HANDLE_VALUE){}
};
struct Per_IO_Data{
OVERLAPPED ol;
WSABUF wsabuf;
char *buf;
int ioMode; //读 写 接受
SOCKET sAcceptSock; //acceptex 预先创建的socket
int nTotalBytes;
int nSendBytes;
Per_IO_Data(): buf(NULL) ,ioMode(-1) , sAcceptSock(INVALID_SOCKET)
,nSendBytes(0), nTotalBytes(0)
{
memset(&ol, 0 , sizeof(ol));
}
~Per_IO_Data(){
if(buf) delete buf;
}
};
void resetPerIOData(Per_IO_Data * pdata , int mode = -1){
if(!pdata) return;
memset(&pdata->ol, 0 ,sizeof(OVERLAPPED));
if(pdata->buf)
memset(&pdata->buf,0,sizeof (char) * BUFFSIZE);
pdata->wsabuf.buf=pdata->buf;
pdata->ioMode = mode;
pdata->wsabuf.len = BUFFSIZE;
pdata->nSendBytes = 0 ;
pdata->nTotalBytes = 0 ;
pdata->sAcceptSock = INVALID_SOCKET;
}
Per_IO_Data * getNewPerIOData(int mode = -1){
Per_IO_Data * pdata = new Per_IO_Data;
if(!pdata) return NULL;
pdata->buf = new char[BUFFSIZE];
pdata->ioMode = mode;
pdata->wsabuf.buf  = pdata->buf;
pdata->wsabuf.len = BUFFSIZE;
return pdata;
}
bool doAccept(SOCKET listenfd){
if(listenfd == INVALID_SOCKET) return  false;
SOCKET sock = WSASocket(AF_INET,SOCK_STREAM,0,0,0,WSA_FLAG_OVERLAPPED);
if(sock == INVALID_SOCKET)
return false;
Per_IO_Data *pData = getNewPerIOData(IO_ACCEPT);
if(!pData) { closesocket(sock); return false;}
pData->sAcceptSock = sock;
DWORD nBytes = 0;
BOOL ret = FuncAcceptEx(listenfd,
pData->sAcceptSock,
pData->buf,
0,                     //唯一这个参数注意一下, 自己找msdn
sizeof(SOCKADDR_IN) + 16,
sizeof(SOCKADDR_IN) + 16,
&nBytes,
&pData->ol);
if(!ret && WSAGetLastError() != ERROR_IO_PENDING){
closesocket(sock);
EnterCriticalSection(&io_pool_cs);
io_pool.push_back(pData);
LeaveCriticalSection(&io_pool_cs);
return false;
}
return true;
}
void handleIO( Per_IO_Data * pData, Per_Sock_Data * pSock , DWORD nBytesTransfered){
if(!pData || !pSock) return;
DWORD flag = 0;
if(IO_READ == pData->ioMode ){
memset(&pData->ol, 0 , sizeof(OVERLAPPED)); //清一下OVERLAPPED,虽然对socket没啥用,还是习惯问题
pData->ioMode = IO_WRITE; //把这块数据直接发出去
pData->wsabuf.len = nBytesTransfered;
pData->nSendBytes = 0;
pData->nTotalBytes = nBytesTransfered;
WSASend(pSock->sock,&pData->wsabuf,1,NULL,flag,&pData->ol,NULL); //复制到发送缓冲区
//每次创建Per_IO_Data 太浪费了, 因此把之前用过的内存块存在全局deque中
//如果太多了deque中太多了 自己想办法解决去删除一些,方法很多.
//如果觉得麻烦,直接 new Per_IO_Data即可
Per_IO_Data * pNewIOData = NULL;
EnterCriticalSection(&io_pool_cs);
if(!io_pool.empty()){
pNewIOData = io_pool.front();
io_pool.pop_front();
}
LeaveCriticalSection(&io_pool_cs);
if(NULL == pNewIOData){
pNewIOData = getNewPerIOData(IO_READ);
}
else{
pNewIOData->ioMode = IO_READ;
}
//继续接受
flag = 0;
WSARecv(pSock->sock , &pData->wsabuf,1,NULL,&flag,&pData->ol,NULL);
}
else if(IO_WRITE == pData->ioMode){
// 检查是否发送完毕, 如果没有发送完毕 , 继续发送剩余的数据,
// 如果发完了就进入 io_pool
pData->ioMode = IO_WRITE;
pData->nSendBytes += nBytesTransfered;
if(pData->nSendBytes < pData->nTotalBytes){
flag = 0;
pData->wsabuf.len = pData->nTotalBytes - pData->nSendBytes;
pData->wsabuf.buf = pData->buf  + pData->nSendBytes;
WSASend(pSock->sock,&pData->wsabuf,1,NULL,flag,&pData->ol,NULL); //复制到发送缓冲区
}
else{
//复制到发送缓存区后, 将回到这里,此时为了节省开销, 把pData重置一下仍进io_pool中
resetPerIOData(pData,IO_READ);
EnterCriticalSection(&io_pool_cs);
io_pool.push_back(pData);
LeaveCriticalSection(&io_pool_cs);
}
}
else if(IO_ACCEPT == pData->ioMode){ //核心点
//会到这里说明有连接进来了 , 由于listenfd 关联到了IOCP,
//因此对应的 key 也将一起传递进来 ,也就是这个pSock, pSock->sock就是监听套接字
//而这个pData就是在doAccept 中创建的那个内存块,内部携带了预先创建的sAcceptSocket
//但是这个sAcceptSocket 并没有继承监听socket的属性
//因此继承属性,比如缓冲区大小
setsockopt(pData->sAcceptSock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
(char *)&pSock->sock, sizeof(pSock->sock) );
//为此新的socket创建一个key , 用于之后的操作
Per_Sock_Data * pNewSock = new Per_Sock_Data;
pNewSock->sock = pData->sAcceptSock;
//用扩展函数来获取客户端的地址 GetAcceptExSockaddrs
//里面很多参数都是和 AcceptEx中相同的
SOCKADDR_IN * localAddr = NULL,  *remoteAddr = NULL;
int localLen = 0 , remoteLen = 0;
FuncGetAddr(
pData->buf,
0,  //此参数注意一下,翻一下msdn , 与AcceptEx那个要注意的参数相同
sizeof(SOCKADDR_IN)+16,
sizeof(SOCKADDR_IN)+16,
(SOCKADDR **)&localAddr,
&localLen,
(SOCKADDR **)&remoteAddr,
&remoteLen
);
//把客户端的地址复制进去
memcpy(&pNewSock->addr,remoteAddr,remoteLen);
//把新的key与iocp关联;
//为了方便,我把iocp放到了与监听套接字关联的结构体中, 可以直接做为全局变量更方便
CreateIoCompletionPort((HANDLE)pNewSock->sock,pSock->iocp,(ULONG_PTR)pNewSock,0);
//重置一下,把ioMode修改一下
resetPerIOData(pData,IO_READ);
pData->sAcceptSock = INVALID_SOCKET; //防止歧义
//开始接受数据
flag = 0;
WSARecv(pNewSock->sock,&pData->wsabuf,1,NULL,&flag,&pData->ol,NULL);
//继续接受一个连接,我在一开始的时候只调用了一次,因此只能接受一个连接
doAccept(pSock->sock);
}
else{
cout << "???????????? 走错地方了把" << endl;
}
}
unsigned iocp_thread(void *arg){
HANDLE iocp = *(HANDLE*)arg;
Per_IO_Data * pData = NULL;
Per_Sock_Data *pSock = NULL;
DWORD nBytesTrans = 0;
BOOL ret = FALSE;
while(1){
pSock = NULL;
pData = NULL;
ret = GetQueuedCompletionStatus(iocp,&nBytesTrans,(PULONG_PTR)&pSock,
(LPOVERLAPPED *)&pData,INFINITE);
//自己做 ret ==FALSE 的错误处理
if(0 == nBytesTrans && ( IO_READ == pData->ioMode  || IO_WRITE == pData->ioMode)){
cout << "peer closed :" << inet_ntoa(pSock->addr.sin_addr)  << endl;
closesocket(pSock->sock);
delete pSock;
delete pData;
}
else
handleIO(pData,pSock, nBytesTrans);
}
return  0;
}
int main(int argc, char *argv[])
{
//起手式都一样
WSADATA wsadata;
WSAStartup(MAKEWORD(2,2) , &wsadata);
SOCKET listenfd = socket(AF_INET, SOCK_STREAM, 0); //自带overlapped属性 == wsasock(... , overlapped)
int openReuse = 1;
int openReuseLen = sizeof(openReuse);
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(char*)&openReuse,openReuseLen);
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr.S_un.S_addr = INADDR_ANY;
bind(listenfd,(SOCKADDR*)&serv_addr,sizeof(struct sockaddr_in));
listen(listenfd, SOMAXCONN);
GUID GuidAcceptEx = WSAID_ACCEPTEX;
GUID GuidGetAddr = WSAID_GETACCEPTEXSOCKADDRS;
DWORD dwbytes = 0;
//2个扩展函数, 没什么好说的, 固定模式, 看一边msdn,照抄即可
WSAIoctl(listenfd,SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,sizeof(GuidAcceptEx),
&FuncAcceptEx,sizeof(FuncAcceptEx),
&dwbytes,0,0);
WSAIoctl(listenfd,SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidGetAddr,sizeof(GuidGetAddr),
&FuncGetAddr,sizeof(FuncGetAddr),
&dwbytes,0,0);
InitializeCriticalSectionAndSpinCount(&io_pool_cs, SPINCOUNT);
SYSTEM_INFO sysinfo = {0};
GetSystemInfo(&sysinfo);
int thread_num = sysinfo.dwNumberOfProcessors * 2 + 2;//线程数
HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,thread_num);
//把监听sock 关联到iocp , 至此任何客户端的connect, 都由iocp来完成,就像其他socket一样
Per_Sock_Data * pSock = new Per_Sock_Data; //给监听套接字绑定一个key
pSock->iocp = iocp;
pSock->sock = listenfd;
memcpy(&pSock->addr,&serv_addr,sizeof(serv_addr));
CreateIoCompletionPort((HANDLE)listenfd, iocp,(ULONG_PTR)pSock,0);
//接下来起线程
vector<HANDLE> thread_handles;
vector<unsigned int> tids;
for(int i = 0; i < thread_num ; ++i){
unsigned tid = 0;
HANDLE t = (HANDLE)_beginthreadex(0,0,iocp_thread,(void*)&iocp,0 , &tid);
thread_handles.push_back(t);
tids.push_back( tid);
}
doAccept(listenfd); //只接受一个客户端 , for(int i = 0 ; i < 10000; ++i) doAccept ...
//接下来主线程没事了 , 该创建的都创建了,可以干其他事了
while(1){
Sleep(10000);
cout << "main 睡觉 : " << io_pool.size() << endl;
}
return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/187774.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年9月29日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
IOCP反射服务器
这两天学习了一下IOCP网络模型。 主要参考了这两片文章:http://blog.csdn.net/neicole/article/details/7549497/和http://blog.csdn.net/piggyxp/article/details/6922277 IOCP是我见过的最复杂的网络模型了,在Windows里肯定就是boss了,而且一开始我感觉IOCP甚至比epoll还要复杂(其实epoll也不复杂,全部都不复杂,只是不懂的人觉得复杂~)。当仔细研究一下之后,觉得也就 也像我很纠结的公事
_gongluck
2018/03/08
9140
IOCP反射服务器
关于windows完成端口(IOCP)的一些理解(一)
系列目录 关于windows完成端口(IOCP)的一些理解(一) 关于windows完成端口(IOCP)的一些理解(二) 关于windows完成端口(IOCP)的一些理解(三) 关于windows完成端口(IOCP)的一些理解(四) 关于windows完成端口(IOCP)的一些理解(五) 关于windows完成端口(IOCP)的一些理解(六) 本人很多年前接触完成端口以来,期间学习和练习了很多次,本以为自己真正地理解了其原理,最近在看网狐的服务器端源码时又再一次拾起完成端口的知识,结果发现以前理解的其实很多
范蠡
2018/04/13
7.6K0
关于windows完成端口(IOCP)的一些理解(五)
系列目录 关于windows完成端口(IOCP)的一些理解(一) 关于windows完成端口(IOCP)的一些理解(二) 关于windows完成端口(IOCP)的一些理解(三) 关于windows完成端口(IOCP)的一些理解(四) 关于windows完成端口(IOCP)的一些理解(五) 关于windows完成端口(IOCP)的一些理解(六) #include "StdAfx.h" #include "IOCPModel.h" #include "MainDlg.h" // 每一个处理器上产生
范蠡
2018/04/18
1.9K0
IOCP一:AcceptEx「建议收藏」
IOCP底层机理还没有透彻的理解,现将部分内容记录如下 2014.7.22 16:50
全栈程序员站长
2022/09/06
1.3K0
关于windows完成端口(IOCP)的一些理解(二)
1 不知道你是否记得前面中说过每消耗一个预先准备客户端的socket,就要补上一个。这个代码现在看来就应该放在连接成功事件里面了: DWORD ThreadFunction() { OVERLAPPED *pOverlapped = NULL; PER_SOCKET_CONTEXT *pSocketContext = NULL; DWORD dwBytesTransfered = 0; BOOL bR
范蠡
2018/04/13
1.6K0
WinSock Socket 池
之前在WinSock2.0 API 中说到,像DisConnectEx 函数这样,它具有回收SOCKET的功能,而像AcceptEx这样的函数,它不会自己在内部创建新的SOCKET,需要外部传入SOCKET作为传输数据用的SOCEKT,使用这两个函数,我们可以做到,事先创建大量的SOCKET,然后使用AcceptEx函数从创建的SOCKET中选择一个作为连接用的SOCKET,在不用这个SOCKET的时候使用DisConnectEx回收。这样的功能就是一个SOCKET池的功能。
Masimaro
2018/08/31
1.3K0
c++ 网络编程(十)TCP/IP LINUX/windows 异步通知I/O模型与重叠I/O模型 附带示例代码
原文链接:https://www.cnblogs.com/DOMLX/p/9662931.html
徐飞机
2018/09/30
1.7K0
c++ 网络编程(十)TCP/IP LINUX/windows    异步通知I/O模型与重叠I/O模型  附带示例代码
c++ 网络编程(九)TCP/IP LINUX/windows--使用IOCP模型 多线程超详细教程 以及 多线程实现服务端
原文链接:https://www.cnblogs.com/DOMLX/p/9661012.html
徐飞机
2018/09/30
3.3K0
c++ 网络编程(九)TCP/IP LINUX/windows--使用IOCP模型      多线程超详细教程 以及 多线程实现服务端
windows完成端口(六)
系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) windows完成端口(六) // 最后释放掉所有资源 void CIOCPModel::_DeInitialize() { // 删除客户端列表的互斥量 DeleteCriticalSection(&m_csContextList); // 关闭系统退出事件句柄 RELEASE_HANDL
范蠡
2018/04/24
1.7K0
WinSock 完成端口模型
之前写了关于Winsock的重叠IO模型,按理来说重叠IO模型与之前的模型相比,它的socket即是非阻塞的,也是异步的,它基本上性能非常高,但是它主要的缺点在于,即使我们使用历程来处理完成通知,但是我们知道历程它本身是在对应线程暂停,它借用当前线程的线程环境来执行完成通知,也就是说要执行完成通知就必须暂停当前线程的工作。这对工作线程来说也是一个不必要的性能浪费,这样我们自然就会想到,另外开辟一个线程来执行完成通知,而本来的线程就不需要暂停,而是一直执行它自身的任务。处于这个思想,WinSock提供了一个新的模型——完成端口模型。
Masimaro
2018/08/31
1K0
windows完成端口(二)
系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) windows完成端口(六) 1 不知道你是否记得前面中说过每消耗一个预先准备客户端的socket,就要补上一个。这个代码现在看来就应该放在连接成功事件里面了: DWORD ThreadFunction() { OVERLAPPED *pOverlapped = NULL; PER_SOCKET_CON
范蠡
2018/04/24
1.7K0
windows完成端口(五)
系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) windows完成端口(六) #include "StdAfx.h" #include "IOCPModel.h" #include "MainDlg.h" // 每一个处理器上产生多少个线程(为了最大限度的提升服务器性能,详见配套文档) #define WORKER_THREADS_PER_PROCESSOR 2 // 同时投
范蠡
2018/04/24
2K0
关于windows完成端口(IOCP)的一些理解(三)
系列目录 关于windows完成端口(IOCP)的一些理解(一) 关于windows完成端口(IOCP)的一些理解(二) 关于windows完成端口(IOCP)的一些理解(三) 关于windows完成端口(IOCP)的一些理解(四) 关于windows完成端口(IOCP)的一些理解(五) 关于windows完成端口(IOCP)的一些理解(六) 1 现在还剩下最后一个问题,就是工作线程如何退出。当然你可以在每次判断标识位前先判断一个退出标识。但是如果工作线程正好被GetQueuedCompletionStat
范蠡
2018/04/18
1.8K0
IOCP使用acceptEX进行异步接收
代码部分疑惑说明 说明:①WSAAcceptEx函数作用是投递accept操作到完成端口内核,只有该函数可以完成此功能
全栈程序员站长
2022/11/11
8300
windows完成端口(四)
系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) windows完成端口(六) 下面给出上文中使用到的对完成端口模型封装的类的全部代码: /* ========================================================================== Purpose: * 这个类CIOCPModel是本代码的核心类, 用于说明W
范蠡
2018/04/24
1.8K0
很幽默的讲解六种Socket I/O模型
信息来源:幻影论坛     作  者: flyinwuhan (制怒·三思而后行)
ternturing
2018/09/12
8530
很幽默的讲解六种Socket I/O模型
完成端口与线程池的关系_端口触发
关于IOCP网上到处都是资料,说的也很详细。我在这里就不再多说了,这只是本人在学习IOCP时的笔记,和配合AcceptEx写的一个极小的服务端程序。由于刚刚接触ICOP加上本人刚毕业不到一年,所以里面的理解或观点可能有误,还请大家多多批评!
全栈程序员站长
2022/11/11
1.1K0
完成端口与线程池的关系_端口触发
如何产生ioexception_结合实例论述控制过程
在普通IOCP的基础上注意两点: 1.记得把监听socket绑定到端口 2.在Accept处理过程中,抛出接受连接的AcceptEx请求,绑定客户端socket到端口和抛出recv请求
全栈程序员站长
2022/11/11
4380
C语言网络编程:从入门到精通,一篇文章彻底搞懂
Socket接口主要工作在传输层和网络层,屏蔽了底层硬件和路由细节,让开发者可直接通过“端口+IP”定位目标进程。
C语言中文社区
2025/07/17
1680
C语言网络编程:从入门到精通,一篇文章彻底搞懂
libuv源码学习笔记:tcp-echo-server
事件循环是 libuv 功能的核心部分,它负责对 I/O 进行轮询,并基于不同的事件源执行它们的回调函数。libuv 的设计目标之一是为了让异步 I/O 操作变得简单易用,同时保持高性能。
晨星成焰
2024/10/20
3460
libuv源码学习笔记:tcp-echo-server
相关推荐
IOCP反射服务器
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档