网络编程-点点滴滴

考虑的方案:
粘包问题:
客户端打包发送,不使用SO_RCVBUF选项,
启用TCP_NODELAY选项,Nagle算法禁用。
使用Nagle算法,通过TCP发送的数据不会马上被发送,而是等待一段时间,当数据包大小达到缓冲区的大小再一次发送,这样就可能导致粘包现象。
(如果数据包长时间未达到缓冲区大小,是不是应该有个时间限制吧,猜测而已,不了解Nagle算法)此方法一般试用于每次发送数据比较小而且比较频繁的情况,

使用此方案,需要控制发送包的大小,如果用来做服务器,特别是游戏服务器,一般发包也不会太大,而且交互次数较频繁,效率比较低(使用Nagle算法的优势可以看到),但每个包的大小固定,至少发送数据包的时候,不会造成粘包。
而且不使用接收缓冲区,数据包发送后马上接收,也不会有几个包黏在一起,不用考虑粘包的问题。
一般看到的方案是,使用SO_RCVBUF,TCP_NODELAY不使用,发包的时候带个包头,发包进行打包处理,接包的时候根据包长度进行粘包处理。


IOCP发包重构问题
当投递大量的WSARecv 的时候,就可能会产生包重构的问题,如果不是比较大的数据包或者传输文件,还是每次接收后投递一个WSARecv,(同事的建议),投递多个会导致包重构问题,下面是一种解决方法,两者权衡下。

方案1
安排序列号,在每个ClientContext上两个序号,一个是某个套接字发包的序号(序号1),从接收到第一个包开始计数;另一个是套接字上读包的序号(序号2),这个包是计算当前读包的序列,从此套接字的第一个包开始读,依次递增;
同时,每个包也有一个编号(编号3),在投递WSARecv的时候,将编号1的值赋予编号3。
当读包的时候,对某个套接字上的包的序号(编号2)与将要读的包的序号(编号3)进行比较,
    如果相等,则此包就是即将被读取的包;否则,包没有按顺序接收,将包放到ClientContext的未正确接收缓冲区包中,并对未正确接收缓冲区包按编号2进行排序,从未正确接收缓冲区包的第一个包开始读。
    
    
防止攻击问题:
    在使用IOCP的情况下,可以在底层对监听的套接字使用WSAWaitForMultipleEvents,然后对WSA_WAIT_TIMEOUT事件进行处理,SO_CONNECT_TIME来检查是否连接超时,但据同事测试,WIN SERVER 2003以上的版本在系统底层已经做了处理,可能是根据listen
    的backlog来进行处理,具体原理还有待研究。所以这样问题在底层的话也不需额外进行处理了。
    
数据打包问题
    在游戏服务器编程中,有时候数据打包也是有必要的,特别是服务器之间的通信。一个服务器可能连接其他的多个服务器(不同服务),服务器以帧来计算的话,如果一帧的时间大概为10ms,客户端响应的时间大概为100ms,所以在1帧内进行的操作并不多,
    那么可以在一定帧数时间范围内,打包数据,然后再一起发送,(只是大概了解这个过程,但没有实际测试过,先几下,以后研究下),可以在发送数据的时候设定一个开关,可选择性的数据打包。
    
网络延迟问题:

 

 

2010-07--03   10:18:20

 

    现有的服务器是有采用IOCP模型的,虽然是有内存池,收发包都是在IOCP的线程中处理的,没有单独开线程,其中还需求进行拆解包处理,其中又有一次拷贝的过程,虽然使用了环形缓冲,但大量的拷贝,性能也会有比较明显的影响;

    将来考考虑的优化:网络层的数据直接用环形缓冲区来进行读写,同时也可以以处理粘包的问题,读写分别开一个线程。之前有过此类的尝试,但是发生了缓冲区溢出的情况,也就是接受数据的时候,缓冲区的大小(当前位置到BUFFER结尾)不够写,所以得先预写下,是否有足够的空间来写,不足则重新分配,当然,缓冲区开始的时候分配的足够,否则的话,长时间的运行,大量的重分配过程也就导致了内存碎片,也就违反了当初设计环形缓冲的初衷。

    另外,网络层还得扩展到其他的平台,尝试着写UNIX下面的服务器,这个是必须的。不然还停留在现在的层次上,

    现在的想法是经常变,工作中基本很难涉及到比较深的层次,也许现在的水平还达不到那种程度,只有靠业余时间来充实了,还真有些迷茫。

    最近研究了下天龙的代码,肢体来说还不错,但还是缺少某些东西,虽然可以架设起来,要完善还是得花不少时间和精力。

    3D的东西也开始去了解一下,从OGRE+CEGUI开始,毕竟是开源的东西,而且商用也不少了,可以学习借鉴一下。

     那么,开始吧。

 

2010-07-23    10:17:34

在参看天龙服务器的时候,发现recv接受字节数是可以控制的,也看了下WSARecv同样也有 这样的参数控制每次接收的字节数,如此,就可以直接将数据写到缓冲区中,不必再经过一次拷贝,也可以避免做接收的时候缓冲区溢出的情况了,将优化网络底层库列入计划中。

原因分析:

之前考虑的是总是按照系统层的缓冲区来接受数据,但是可能缓冲区没有那么大的可写的空间了,从而会在不可用的内存中写数据,导致内存溢出;如果换另外一个角度,以环形缓冲区为基本,有多少数据可写,则从系统缓冲中读入多少数据,这样也许可以解决目前的问题。

 

关于压力测试:

做过一些测试,基本方法是启动一个客户端,通过多个线程来同时来连接客户端,一个线程相当于一个客户端连接,线程数量是可控制的,设置参数一般为100,1000,2000,5000 。有一些测试数据,也未曾记录下来,只是大概了解了下。因为属于ECHO模型的,还算稳定,少数线程的时候,连续测试几个小时,基本上不会有问题,但数量达到2000+的时候,有时候会终止,不能继续首发消息了,原因可能有多种吧,一个是线程,一个进程创建太多的线程,而且没有等全部创建完毕再进行消息收发;另外是环形缓冲,目前的情况下是比较稳定,但也不代表没有问题,也不是最优;另外就是SOCKET的一些参数可能设置不正确,或者是还有待优化,因为没有做过大量的性能测试,这方面的经验也不够,只是在网上找了些资料,大体如下:

 1)测试通信处理能力:   
  a)多连接,小数据量测试   
  客户端采用多线程与SERVER连接,采用多台终端同时与SERVER连接,每台终端同时建立不少于1000个连接,观察SERVER通信是否正常   
  b)常连接、大数据量测试   
  每个客户端与SERVER建立一条长连接,同时发送大数据量数据(如每次1M数据),观察SERVER能否正确接收   
    
  2)数据处理能力   
  a)每次连接时启动计时,在一定的时间内观察SERVER是否正确返回,统计交易成功率   
  b)碎片包测试:客户端把一个完整交易包分割成多个碎片包发送,观察SERVER能否正确组合和响应   
    
  3)可靠性测试   
  多台客户端同时与SERVER连接并自动发送交易数据,交易数据有单包,多包,碎片等模式,连接通信7X24小时,统计SERVER的交易成功率。

 

这个还是蛮重要的 ,下一个版本需要多做这方面的工作,切记!

 

 

2010-08-30   23:39:09

 

实现了SELECT的ZERO_COPY,但是由于FD_SETSIZE 的限制 WIN下为64(可以修改WinSocke2的头文件),LINUX下为1024,还远远达不到要求,可以利用SELECT和线程池来解决问题。另外,研究一下LOOK-FREE技术。
还有篇技术文章可以参考下  http://www.kegel.com/c10k.html

 

你可能感兴趣的:(网络编程-点点滴滴)