游戏服务器之多线程发送(下)
时间:2022-05-05
本文章向大家介绍游戏服务器之多线程发送(下),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
5、发送缓冲区数据
检查可发送该线程的相关联的所有会话上的发送缓冲区的数据,检查完后,发送会话上的发送缓冲区的数据。
VOID ExecSockDataMgr::CheckSendSessionBuffers(PEXECDATASENDTHREAD pSendThread)
{
int nErr, nRemainSize;
char *pBuffer;
PRUNGATEUSERSESSION *pSessionList = m_SessionList;
PRUNGATEUSERSESSION pSession;
pSendThread->boSendEWouldBlock = false;
pSendThread->boSendFewBuffer = false;
//优化锁处理
//(1)在开始时就分配65535的会话列表大小,在一个逻辑网关上不会有超出这个大小的有效会话数量(所以会话列表的操作不需要加没有加会话列表锁)
//(2)会话的释放会置空会话列表的成员,会话的nSocket 是在最后才初始化的,所以可以判断pSession->nSocket != INVALID_SOCKET 来判断会话的初始化
//后的有效性,根据pSession->boMarkToClose和pSession->boRemoteClosed 来判断会话是否被关闭(所以不需要加会话锁)
//(3)发送失败会标记该会话被关闭
INT_PTR nCount = m_SessionList.count();
for ( INT_PTR nIndex = pSendThread->nThreadIdx; nIndex < nCount; nIndex += m_nSendThreadCount )
{
pSession = pSessionList[nIndex];
if (!pSession)
continue;
if ( pSession->nSocket != INVALID_SOCKET && !pSession->boMarkToClose && !pSession->boRemoteClosed )//检查该会话处于正常状态
{
if ( !pSession->boSendAvaliable )
{
if ( _getTickCount() >= pSession->dwSendTimeOut )//检查会话的发送时间(等到发送的时间再发送)
{
pSession->boSendAvaliable = true;//到了可发送时间则标记可发送
pSession->dwSendTimeOut = 0;
}
else continue;
}
// 这里可以不加会话锁,因为数据接收处理线程回收会话资源是根据关闭标识并延时10s的(这里的锁需要验证),发送缓冲区的数据操作在本线程内
//(如果实在要加锁,对于数据接收处理线程会修改会话,因为不会有其他发送线程使用该会话,所以可以是互斥量)
if ( TRYLOCK_SESSION_SEND( pSession ) )
{
nRemainSize = pSession->SendBuf.nOffset;//该会话的缓冲区的有效数据大小
if ( nRemainSize > 4096 * 1024 )//发送缓冲区过长的会话会被关闭(这是异常现象,一般不会出现)
{
UNLOCK_SESSION_SEND( pSession );
CloseSession( pSession );
nRemainSize = 0;
logWarn("关闭了一个发送数据队列大于4MB的连接。");
continue;
}
if ( nRemainSize )
{
pBuffer = pSession->SendBuf.lpBuffer;
#ifdef LINUX
nErr = ::send( pSession->nSocket, pBuffer, nRemainSize, MSG_NOSIGNAL);//禁止对端连接关闭时,send()函数向系统发送异常消息brokenpipe
#else
nErr = ::send( pSession->nSocket, pBuffer, nRemainSize, 0 );
#endif
if ( nErr > 0 )//发送成功
{
pSession->nSendPacketCount++;
InterlockedExchangeAdd( (LONG*)&m_dwWaitSendUserSize, -nErr );
InterlockedExchangeAdd( (LONG*)&m_dwSendUserSize, nErr );
if ( nErr < nRemainSize )//如果还有剩余发送数据则把剩余发送数据拷贝到该会话的缓冲区的前部
{
pSendThread->boSendFewBuffer = true;
memcpy( pBuffer, &pBuffer[nErr], nRemainSize - nErr );
nRemainSize -= nErr;
pBuffer[nRemainSize] = 0;
pSession->SendBuf.nOffset = nRemainSize;
}
else
{
pBuffer[0] = 0;
pSession->SendBuf.nOffset = 0;
}
}
else if ( !nErr || WSAGetLastError() != WSAEWOULDBLOCK )//对方关闭了套接字
{
pSession->boRemoteClosed = true;
CloseSession( pSession );
InterlockedExchangeAdd( (LONG*)&m_dwWaitSendUserSize, -nRemainSize );
InterlockedExchangeAdd( (LONG*)&m_dwSendUserSize, nRemainSize );
pBuffer[0] = 0;
pSession->SendBuf.nOffset = 0;
}
else//系统发送缓冲区已满则延时发送
{
pSession->boSendAvaliable = false;
pSession->dwSendTimeOut = _getTickCount() + RUNGATE_SENDCHECK_TIMEOUT;//目前300ms作为发送间隔
pSendThread->boSendEWouldBlock = true;
}
}
UNLOCK_SESSION_SEND( pSession );
}
}
else
{
//会话关闭后减少待发送数据统计值
if ( (nRemainSize = pSession->SendBuf.nOffset) )
{
InterlockedExchangeAdd( (LONG*)&m_dwWaitSendUserSize, -nRemainSize );//减少该逻辑网关上的待发送给用户的有效数据大小
pSession->SendBuf.nOffset = 0;
}
}
}
}
- 在Win10上是用Anaconda搭建TensorFlow开发环境
- A Gentle Introduction to Autocorrelation and Partial Autocorrelation (译文)
- A Gentle Introduction to Applied Machine Learning as a Search Problem (译文)
- 技术创新,基于 React Native 的开源项目 | 码云周刊第 17 期
- How to Use the TimeDistributed Layer for Long Short-Term Memory Networks in Python 译文
- ssctf2017_WriteUp
- CENTOS7.2安装CDH5.10和Kudu1.2(一)
- Docker镜像管理
- 360春秋杯3道web题的简单分析
- Vue 2.0 学习总结,精华全在这里了
- 25.2 安装Docker
- 使用JDBC向Kudu表插入中文字符-cast的秘密
- 使用JDBC向Kudu表插入中文字符-双引号的秘密
- Yarn的JobHistory目录权限问题导致MapReduce作业异常
- 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自动化测试(selenium 22)
- linux 实现双网卡绑定单个IP——team篇
- Eclipse华丽转身之控件表格工厂
- 面试常考知识点总结——面试必看
- linux 实现双网卡绑定单个IP——bond篇
- Hacking with iOS: SwiftUI Edition - 愿望清单项目(一)
- 重拾Java Web应用的基础体系结构
- 使用SAP C4C自定义BO实现自定义的Number Range
- 限流算法简介及Guava RateLimiter令牌桶限流介绍
- K8S的名称空间创建&&版本的升级、回滚操作
- SAP Cloud for Customer CLR(Code List Restriction)的一种高级用法
- SAP WebClient UI One Hit Navigation的实现方法
- 【打包构建】Mac下使用expect实现执行sudo命令时自动输入密码
- ASP.NET Core 奇淫技巧之SPA部署
- SAP Cloud for Customer Rule Editor的使用方法和底层工作原理