基于重叠IO模型的 回显TCP服务器设计
时间:2022-04-22
本文章向大家介绍基于重叠IO模型的 回显TCP服务器设计,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
---------------------1 套接字对象----------------------
为每个套接字创建一个SOCKET_OBJ对象,记录与之相关的信息。
typedef struct _SOCKET_OBJ{
SOCKET s;
int nOutstandingOps;//重叠IO数量
LPFN_ACCEPTEX lpfnAcceptEx; //扩展AcceptEx指针
}SOCKET_OBJ,*PSOCKET_OBJ;
所有重叠IO提交到特定的套接字上,释放对应套接字对象,必须保证此套接字再没有重叠IO,即nOutstandingOps=0
申请套接字对象,释放套接字对象的函数
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);
}
---------------------2 缓冲区对象---------------------
缓冲区对象SOCKET_OBJ,记录重叠IO的所有属性
typedef struct _BUFFER_OBJ{
OVERLAPPED ol;//重叠结构
char *buff;//使用的缓冲区
int nLen;//buff长度
PSOCKET_OBJ pSocket;//次io所属的套接字对象
int nOperation;//提交的操作类型
#define OP_ACCEPT 1
#define OP_READ 2
#define OP_WRITE 3
SOCKET sAccept;//保存AcceptEx接受客户套接字
_BUFFER_OBJ *pNext;
}BUFFER_OBJ,*PBUFFER_OBJ;
pNext将BUFFER_OBJ对象练成一个链表
事件句柄数组 和 链表地址
HANDLE g_events[WSA_MAXIMUM_WAIT_EVENTS];
int g_nBufferCount;//数量
PBUFFER_OBJ g_pBufferHead,g_pBufferTail;//地址
调用重叠IO函数之前,都要申请BUFFER_OBJ对象,记录信息。IO完成后,再释放BUFFER_OBJ对象
申请BUFFER_OBJ对象 的函数:
PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket,ULONG nLen){
if(g_nBufferCount > WSA_MAXIMUM_WAIT_EVENTS)
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;
}
释放BUFFER_OBJ对象函数:
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_pBuffer Tail = pTest;
bFind = TRUE;
}
}
//释放它占用的空间
if(bFind)
{
g_pBufferCount--;
::CloseHandle(pBuffer->ol.hEvent);
::GlobalFree(pBuffer->buff);
::GlobalFree(pBuffer);
}
}
提交重叠IO,传递参数有重叠结构IO和缓冲区指针buff。在IO完成后,得到的是受信事件对象的句柄。根据这个句柄找到对应的BUFFER_OBJ对象。
查找BUFFER_OBJ对象的代码:
PBUFFER_OBJ FindBufferObj(HANDLE hEvent){
PBUFFER_OBJ pBuffer = g_pBufferHead;
while(pBuffer != NULL){
if(pBuffer->ol.hEvent == hEvent)
break;
pBuffer = pBuffer->pNext;
}
return pBuffer;
}
更新时间句柄数组g_events中的内容:
void RebuildArray(){
PBUFFER_OBJ pBuffer = g_pBufferHead;
int i= 1;
while(pBuffer != NULL){
g_events[i++] = pBuffer->ol.hEvent;
pBuffer = pBuffer->pNext;
}
}
---------------------3 提交重叠IO---------------------
投递IO之后,线程在重叠IO事件上等待,一旦IO事件对象受信,等待函数就会返回
提交接受连接的BUFFER_OBJ对象代码:
BOOL PostAccept(PBUFFER_OBJ pBuffer){
PSOCKET_OBJ pSocket = pBuffer->pSocket;
if(pSocket->lpfnAcceptEx != NULL){
//设置IO类型,增加套接字上的重叠IO计数
pBuffer->nOperation = OP_ACCEPT;
pSocket->nOutstandingOps++;
//投递此重叠IO
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)
return FALSE;
}
return TRUE;
}
return FALSE;
};
接收数据的BUFFER_OBJ对象代码:
BOOL PostRecv(PBUFFER_OBJ pBuffer){
//设置IO类型,增加套接字上的重叠IO计数
pBuffer->nOperation = OP_ACCEPT;
pBuffer->pSocket->nOutstandingOps++;
//投递此重叠IO
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)
return FALSE;
}
return TRUE;
}
发送数据额BUFFER-obj对象代码:
BOOL PostSend(PBUFFER_OBJ pBuffer){
//设置IO类型,增加套接字上的重叠IO计数
pBuffer->nOperation = OP_ACCEPT;
pBuffer->pSocket->nOutstandingOps++;
//投递此重叠IO
DWORD dwBytes;
DWORD dwFlags;
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)
return FALSE;
}
return TRUE;
}
---------------------4 主函数---------------------
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 适配器模式与装饰器模式的区别
- java堆内存详解
- springBoot 入门(一)—— 使用idea创建第一个springBoot项目
- “dddb超级”工具包——高效、快速开发JavaWeb项目后端结构
- 在Java Web中设计的编解码
- 怎么让用一行代码实现页面的定时强制刷新?脚本刷流量再也不用愁了!
- 什么是Javac
- springBoot 入门(二)—— 使用 spring.profiles.active来区分配置
- Kubernetes 1.19.0——deployment(2)
- JavaWeb新手进阶经典项目 & 半小时高效开发 & 海量知识点涵盖 (二)
- Java web 开发 Session超时设置
- JavaWeb第四讲 会话跟踪技术HttpSession、Cookie、url、隐藏表单域
- springBoot 入门(三)—— 使用 RestController
- SSM第一讲 Spring概述和基础知识详解
- springBoot 入门(四)—— 使用 纯注解方式的junit整合测试