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

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 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

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

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

32340
来自专栏张戈的专栏

Haproxy安装部署文档及多配置文件管理方案

最近我在负责一个统一接入层的建设项目,涉及到 Haproxy 和 ospf 的运维部署,本文分享一下我在部署 Haproxy 之后整理的运维部署规范,并实现了H...

946120
来自专栏数据和云

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

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

35350
来自专栏云端漫步

harbor源码分析之构建工具make(五)

make是一个构建工具,现在前端的构建工具有很多.像gulp,grunt等等,它们常被用在前端项目中.在后端领域常使用make来做构建这件事情.

26410
来自专栏沈唁志

PHP开发规范之使用phpcbf脚本自动修正代码格式

在前段时间的文章:在PhpStorm中安装使用PHP_CodeSniffer编码规范检查工具中提到过phpcbf脚本

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

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

org.apache.coyote.http11.AbstractHttp11Protocol

13320
来自专栏Golang语言社区

mac系统下搭建go语言环境

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

51980
来自专栏Netkiller

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

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

1.2K40
来自专栏乐沙弥的世界

RMAN 配置、监控与管理

一个通道代表一个到设备(磁盘或磁带)的数据流并且在目标数据库或辅助数据库实例上产生一个相应的服务器会话(server session)

13910
来自专栏耕耘实录

Oracle 11gR2 中使用expdp导出数据

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢。

18430

扫码关注云+社区

领取腾讯云代金券