游戏服务器之多线程发送(下)

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;  
            }  
        }  
    }  
}  

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2016-09-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏计算机编程

SNS项目笔记<四>--RXjs简要用法

在命令行输入ionic g provider youProviderName 在创建好后,系统会自动导入从@angular/http里导入Http这个类,方便后...

1354
来自专栏Netkiller

高级运维工程师面试题(更新中)

高级运维工程师 服务器硬件 RAID 磁盘阵列 简述 RAID? RAID 0 5 6 10 50 都适用于那些场景? 数据库适用那种 RAID? RAID 1...

7674
来自专栏沃趣科技

Oracle数据库性能障碍分析利器:SYSTEMSTATE DUMP介绍

作者 孟庆辉 沃趣科技数据库工程师 当数据库出现严重的性能问题或者hang了的时候,我们非常需要通过systemstate dump来知道进程在做什么,在等待...

3637
来自专栏高性能服务器开发

+从零实现一款12306刷票软件1.2

当然,这里需要说明一下的就是,由于全国的火车站点信息文件比较大,我们程序解析起来时间较长,加上火车站编码信息并不是经常变动,所以,我们我们没必要每次都下载这个s...

1572
来自专栏数据和云

【深度好文】有关延迟块清除和一致性读

在《ORA-1555错误解决一例》一文中,当时尝试模拟UNDO段头事务表被覆盖的情况下出现ORA-01555错误,没有成功。实际上没有成功的原因是事务数虽然多,...

3395
来自专栏数据和云

讲真,你该做备份的有效性校验了

编辑手记:最近刷爆了朋友圈的GitLab删库事件惊醒了一大批运维人员,五重备份全部失效,这是真的吗?嗯,经过无数次确认,的确所有备份都失效。而我同样相信,存在类...

4256
来自专栏用户2442861的专栏

Tomcat源码 Connector(2)

 Connector是Tomcat最核心的组件之一,负责处理一个WebServer最核心的连接管理、Net IO、线程(可选)、协议解析和处理的工作。 一、...

951
来自专栏一个会写诗的程序员的博客

《Springboot开发问题纪要》java.lang.IllegalArgumentException: Request header is too large问题日志:java.lang.Ille

org.apache.coyote.http11.AbstractHttp11Protocol

882
来自专栏北京马哥教育

一些Centos Python生产环境的部署命令

拿到一台干净的centos之后, 初始化Python环境, 一些命令和问题记录而已 可以搞成脚本自动初始化, 当然, 用docker更好 基础环境 1. 创建...

2954
来自专栏Golang语言社区

mac系统下搭建go语言环境

1,首先查看是否安装go,或者安装版本 yishiyaonie:GO liuhanlin$ go versiongo version go1.5.1 darwi...

4698

扫码关注云+社区