码了5年代码,积累了些东西

完成端口网络模型使用总结及注意事项

背景:windows系统上,如果启用KeepAlive心跳检查(不管设定的时间多长)的套接字数量达到2000以上,那么底层tcp协议栈会不断的检查已经创建的套接字的状态,从而导致系统CPU占用率上升;一种比较好的方案,是应用级层面做一定程度的条件判断,针对有必要的套接字启用keepAlive检查,一旦检查通过,就自动取消keepAlive,原则是尽量减少启用了keepAlive的套接字数量。

第一部分 完成端口网络模型相关函数集及数据结构

函数:(epoll相比,不需要关心IOCP机制是“edge triggle”还是“level triggle”,底层已经帮助处理了)

1、CreateIoCompletionPort()

2、GetQueuedCompletionStatus()

3、PostQueuedCompletionStatus()

4、WSASocket()

5、WSASend()

6、WSARecv()

7、HasOverlappedCompleted()

8、CancelIo()

9、WSAGetOverlappedResult()

依赖的库:

“Ws2_32.lib”链接库

数据结构:

OverLapped

 

第二部分 初始化过程及连接断开过程

1、在处理监听环节,比较好的处理方式是在监听套接字的初始化过程中通过“WSAEventSelect()”函数设置监听套接字触发“FD_ACCEPT”事件;创建一个独立的线程来监听连接,在监听线程中使用“WSAWaitForMultipleEvents”和“WSAEnumNetworkEvents”来等待和接收“FD_ACCEPT”事件被触发,使用“WSAAccept()”函数来创建客户端的连接套接字;该模型的好处在于避免在应用层代码调用“Sleep()”来降低监听线程的造成的CPU占用率,由“WSAWaitMultipleEvents()”在操作系统内核级来解决CPU占用率问题

2、在调用“”接收连接时,需要处理“SYN  flood”攻击,关键点在于防止服务器底层的“TCP协议栈”代码在处理三次握手过程中维持过多的“半连接”状态以及相应重复发送“SYN+ACK”包操作导致的内存资源和高CPU占用率;可以通过修改系统注册表的方式来更改系统的“TCP协议栈”的行为

3、通过函数WSARecv的返回接收字节数,可以判断TCP连接是否断开,UDP断开判断类似

 

第二部分TCP套接字、交互实体、应用层实体之间的关联处理

1、服务器端一般情形下一个套接字代表了一个交互客户端实体,同时一个交互客户端实体对应了一个应用层实体

2、完成端口机制通过“CreateIoCompletionPort()”函数直接完成“套接字与交互客户端实体”之间的关联

 

第三部分UDP地址、交互实体、应用层实体之间的关联处理

1、服务器端一个接收地址代表了一个交互客户端实体,一般情况下一个交互客户端实体对应了一个应用层实体

2、完成端口机制通过“CreateIoCompletionPort()”函数直接完成“套接字与交互客户端实体”之间的关联

 

第四部分 应用层状态与网络层状态关联处理

1、网络状态包括“是否有连接请求”、“某个套接字发送数据是否完成”、“某个套接字是否有待接收数据”、“对方网络是否关闭”

2、应用层状态包括“某个客户端是否已经登录”、“某个客户端的某种类的数据是否发送成功”、“某个客户端是否接收到某种类的数据”、“某个客户端是否成功发送某个文件”、“某个客户端是否接收到某个文件”

3、完全由应用层处理两者之间的关联关系

4、操作系统一级提供两者之间的关联

 

 

 

第五部分 内存分配及锁定问题

1、提交重叠的收发操作都会导致用户空间的内存被锁定,如果发送操作导致的内存被锁定往往无法避免,但是接收操作的内存被锁定可以通过调整传递给“WSARecv”的缓存的大小来控制,即调用“WSARecv”的目的只是为了出发IOCP过程,并且知道待接收数据的大小,但是并不提供真正的内存空间,当“接收操作完成”后,再通过“同步的方式”来接收完整的数据,降低接收操作过程中的内存被锁定

2、服务器设计中一个非常重要的问题的是用于数据收发操作的缓冲区的内存分配问题,系统提供的“malloc”等函数是线程同步的,当有多个线程要进行内存分配的时候,这些请求操作是串行执行的,这种情况会影响服务器的性能;一种解决方案是给每个交互客户端创建一个私有的内存堆,从该堆上分配缓冲区内存,微软给出来的例子代码中也是这么做的。

3、setsockopt(g_sdListen,SOL_SOCKET, SO_SNDBUF, (char *)&nZero, sizeof(nZero));

setsockopt(g_sdListen,SOL_SOCKET, SO_RCVBUF, (char *)&nZero, sizeof(nZero));这两句的目的在于防止数据在“用户空间”和“套接字空间”之间拷贝,加快收发速度;设置监听的套接字后,监听到的套接字的收发缓冲区也会为0

 

第六部分 异步收发顺序控制及应用层数据处理过程的协调问题

1、得依靠“队列”这样的数据结构来管理各操作给出来的状态,并根据这些状态的顺序为依据执行顺序。原则是保证收发数据的处理代码部分不被并发执行即可,即要做到对于同一个套接字的处理代码,尽量不要被并发执行,即同一个时刻只有一个线程在处理当前套接字的数据,目的也是减少多线程同步处理;通过协调处理代码与异步请求操作的先后执行顺序来做到,一般情况下是“处理数据的代码”先运行,异步请求操作后运行!!同时,对于那种需要同时发出多个异步请求的服务器要很小心的设计!!!!由于现实情况是应用层收发的数据往往是有一定顺序的,所以“字节流收发层”应该向应用层提供有顺序的收发操作接口,保证数据被收发的顺序性。由于调用“GetQueuedCompletionStatus”的工作线程即可以被套接字的“发送操作完成”唤醒也可以被“接收操作完成”唤醒,如果容许同一个套接字并发的执行收发操作,那么可以通过“lpOverlapped”参数的内存地址本身来区分当前完成的是“接收操作”还是“发送操作”,而“lpCompletionKey”参数的作用只是快速的索引到交互客户端实体,需要在交互客户端实体内保存“Overlapped”结构与之关联的是“接收操作”还是“发送操作”;工作线程内比较好的处理过程是通过“lpCompletionKey”找到交互客户端实体-->通过“lpOverlapped”在交互客户端实体内找到对应的操作,并触发更上层的数据处理过程。“字节流收发层”向外提供的发送接口应该提供两种版本的,以后总是将应用层数据拷贝到“字节流收发层”管理的内存中,另外一个版本是由应用层管理重叠发送的内存,只有确认发送完成后,才能销毁该内存。

 第七部分 异步操作的线程触发处理

1、IOCP使用的一个重要问题是创建多少个并发线程来处理“完成事件”;默认情况下会创建与CPU个数相等的线程;假如创建的所有并发线程都在处理“完成事件”,由于没有空闲的线程可用,后来的“完成事件”无法马上处理;但是从整个操作系统的角度考虑,其他进程也会创建线程,导致整个系统中的线程数增多,不一定能保证之前并发的处理线程在处理完成后,马上能被系统调度处理挂起的“完成事件”;但是如果创建多个并发线程的话,从系统整体角度而言,能提高“服务器”自身能被操作系统调度的几率;理想的情况是系统中只有自己的“服务器”处于活动状态。对于自定义的线程可以谨慎的使用“SetThreadPriory”来提高线程的优先级,前提是比较清楚Windows内部调度线程的规则。windows切换线程的时间间隔为大约20ms,可以使用GetThreadTime函数来获取线程执行函数的运行时间,对于计算服务器处理单个客户端所用的时间效率怎么样,比较有用。

 

2、不建议在多个线程中调用“WSASend()”函数针对同一个套接字并发重叠发送数据,因为无法保证在应用层上有顺序的数据被有序的发送。其实在“字节流传输层”可以容许并发重叠发送数据,应用层依靠协议的应答来保证数据的有序发送;另外一种方案是依靠队列来发送有序数据

 

3、在工作线程中,一般的处理顺序是调用“GetQueuedCompletionStatus()”-->处理完成事件-->调用“WSARecv”或者“WSASend”触发进入IOCP过程,但是这两个函数有可能立即完成,对于这种情况的处理可以是使用“goto”语句跳转到“处理完成事件部分”,前提是函数“GetQueuedCompletionStatus”不会被触发,否则“完成事件处理”代码会被运行两回;另外一种处理是不做任何处理,期待下回等待成功,接着运行“完成事件处理”代码,该方案依赖的前提是下回“GetQueuedCompletionStatus()”等待成功,至于这种情况下,该函数是否会等待成功,需要验证。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值