Socket的速率控制

标签: socket 速率 控制 | 发表时间:2014-05-17 05:48 | 作者:songwater
出处:http://blog.csdn.net

(一)、目标

做一个以精确速率向外输出数据的数据源,要完成这个目标,最基础的是:

1、找到一种精确的计时器,在精确的时间范围内控制数据源以指定的速度向外发送数据。

2、通过对套接字选项和线程优先级的设置减少网络因素对发送速度造成的影响,从而提高发送精度,保证数据的实际发送量尽可能的达到指定的理论发送量。

     针对第一个要求,通过寻找到一种时间精度达到微秒级的精确计数器来保证,在硬件支持的情况下可以通过WindowsAPI获取时钟频率以及震荡次数,通过在事件两端分别调用函数得到震荡次数的差值并结合时钟频率可以计算出精确的时间间隔,通过指定的传输速度和精确的延时可以计算出需要发送的数据量。对于第二个要求通过设置数据源所在线程的优先级,以及对套接字选项的设置来减小协议本身对数据传输的过多控制而造成的时延,从而使实际的数据发送量尽可能的接近理论值。

(一)、设置线程优先级

首先在主函数中创建线程函数DWORDWINAPI ThreadProc(LPVOIDlpParam),在线程函数中实现数据源的功能,线程创建成功后对线程优先级进行设置,windows下对线程优先级进行设置的API函数为BOOLWINAPI SetThreadPriority( __in HANDLE hThread, __in intnPriority),其中nPriority定义了线程的优先级。


线程优先级

标志

优先级值

1

IDLE(最低)

THREAD_PRIORITY_IDLE

如果进程优先级为realtime则调整为16,其他情况为1

2

LOWEST(低)

THREAD_PRIORITY_LOWEST

-2(在原有基础上-2)

3

BELOW(低于标准)

THREAD_PRIORITY_BELOW_NORAL

-1(在原有基础上-1)

4

NORMAL(标准)

THREAD_PRIORITY_NORMAL

不变(取进程优先级)

5

ABOVE(高于标准)

THREAD_PRIORITY_ABOVE_NORMAL

+1(在原有基础上+1)

6

HIGHEST(高)

THREAD_PRIORITY_HIGHEST

+2(在原有基础上+2)

7

CRITICAL(最高)

THREAD_PRIORITY_TIME_CRITICAL

如果进程优先级为realtime则调整为31,其他情况为15

将线程优先级设置为最高级,即THREAD_PRIORITY_TIME_CRITICAL,排除本地其他进程的干扰,使得操作系统能够优先调度。

(二)、使用高精度计数器

要实现精确的发送速度,需要有精确的时间控制,在一般情况下使用以下两种方式就满足要求了。一是用SetTimer函数建立一个定时器后,在程序中通过处理有定时器发送到线程的消息队列中的WM_TIMER消息,而得到定时的效果。二是利用GetTickCount函数可以返回计算机启动后的时间,通过两次调用GetTickCount函数,然后控制他们的差值来取得定时的效果。但是以上两种方法都是毫秒级的,在Windows内部有一个高精度运行计时器(high-resolution performance counter),利用它可以获得高精度的定时间隔,其精度可以达到微秒级,其精度与CPU的时钟频率有关。利用API函数QueryPerformanceFrequency能够得到这个定时器的频率。利用API函数QueryPerformanceCounter能够得到定时器的当前值,根据要延时的时间和定时器的频率,能够算出定时器要经过的周期数,循环指定的周期数,就达到了高精度定时的目的。以下是这个API函数的原型:

  • BOOLQueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

作用:返回硬件支持的高精度计数器的频率。

返回值:如果硬件支持高精度计数器则返回频率值,若硬件不支持返回零。

  • BOOLQueryPerformanceCounter(LARGE_INTEGER*lpPerformanceCount);

    作用:得到高精度计时器的值。

    返回值:如果安装的硬件支持高精度计时器则函数返回计数器的值,如果硬件不支持参数返回零。


采用这种方法的步骤如下:

  1. 首先调用QueryPerformanceFrequency函数取得高精度运行计数器的频率f,单位是每秒多少次(n/s),此数一般很大,在实验主机上此频率值为2241054。

  2. 在循环之外先调用一次QueryPerformanceCounter,得到计数器的值n1,在send之前调用循环调用QueryPerformanceCounter,得到计数器的值n2,两次数值的差值通过计数器的频率f换算成时间间隔,t=(n2-n1)/f。当t达到指定的大小后跳出循环,并将此时的n2赋给n1,重新进入循环计时,send发送的数据大小根据指定的速度与定时时间确定。

这种方法存在一个致命的问题,就是忙等,循环过程会占满CPU资源。因此需要调整一下算法,循环过程引入休眠机制,降低CPU占用,但是休眠机制由于操作系统调度的不确定性,会造成循环结束后的实际时间和预设时间会有差别。没有关系,我们在循环结束后调用QueryPerformanceCounter,重新计算本次和上次循环的精确时间差,运用这个时间来计算send发送的数据大小,这样保证输出的数据始终恒定。


(三)设置套接字选项

SO_SNDBUF:通过设置该选项可以改变发送缓冲区的大小,根据应用需求,设置合理的缓冲区大小能够有效提高数据发送的效率。

TCP_NODELAY:在默认情况下,Windows协议栈发送数据采用Nagle算法,这样做虽然提高了网络的吞吐量,但是实时性却降低了,如果设置了TCP_NODELAY选项,就会禁用Nagle算法,应用程序调用send发送的数据包会被立即投送到网络,而没有延迟。考虑到程序中需要做到精确发送的要求,所以将TCP_NODELAY选项设置为TRUE。

SO_DONTROUTE:通过设置该选项,可以绕过路由表中的网关所在的表项,设置了该套接字选项的socket可以将数据包不经网关发送,而是发往直接相连的主机。该套接字选项的合法的值是整数形式的布尔标志值,这里设为TRUE。

在Windows下,对套接字选项进行设置的API函数为

intsetsockopt(

__in SOCKET s,

__in int level,

__in int optname,

__in const char* optval,

__in int optlen

);

获取当先套接字选项值的API函数为

intgetsockopt(

__in SOCKET s,

__in int level,

__in int optname,

__out char* optval,

__inout int* optlen

);

通过以上两个函数可以查看并重新设置套接字选项的值。


当指定传输速度为64kb/s时,速度的波动控制在了0.002kb/s-0.02kb/s。

当指定传输速度为100kb/s时,速度的波动在0.001kb/s-0.02kb/s以内。

当指定传输速度为1000kb/s时,速度的波动控制在了0.015kb/s-0.2kb/s之内。


思考与改进:

    Socket的通信,数据从发送方到接收方,影响速率的因素很多,主要包括以下几个方面:

1、操作系统调度。

2、其他网络进程对网络带宽资源的争抢。

3、协议本身,如果是TCP协议,则速率控制受到TCP协议流量控制和拥塞控制机制,链路层流量控制机制的影响。

4、协议包在发送和接收途中受到其他网络数据的挤占及丢失等。


   我们只能在发送端的应用层以尽可能精确的速率向传输层送速率,然而数据经过传输层、IP层、链路层到物理层最终到网络上,不可控的因素太多。如果能够在网卡驱动层检测实际的进程数据流量,然后根据网卡流量来反馈控制数据投送量,将更为精确地实现速率的控制,具体实现需要进一步研究。



 
作者:songwater 发表于2014-5-16 21:48:11 原文链接
阅读:75 评论:0 查看评论

相关 [socket 速率 控制] 推荐:

Socket的速率控制

- - CSDN博客互联网推荐文章
做一个以精确速率向外输出数据的数据源,要完成这个目标,最基础的是:. 1、找到一种精确的计时器,在精确的时间范围内控制数据源以指定的速度向外发送数据. 2、通过对套接字选项和线程优先级的设置减少网络因素对发送速度造成的影响,从而提高发送精度,保证数据的实际发送量尽可能的达到指定的理论发送量.      针对第一个要求,通过寻找到一种时间精度达到微秒级的精确计数器来保证,在硬件支持的情况下可以通过WindowsAPI获取时钟频率以及震荡次数,通过在事件两端分别调用函数得到震荡次数的差值并结合时钟频率可以计算出精确的时间间隔,通过指定的传输速度和精确的延时可以计算出需要发送的数据量.

有关socket read

- - 五四陈科学院
以下内容由 [五四陈科学院]提供. 实际开发中,网络程序最可能遇到的就是数据没收到、收到错误数据这样诡异的问题.. 很多时候,都是由于对socket read的细节理解的不一致,导致了程序前后的矛盾. 下面详细阐述整个read的过程. read函数是负责从fd中读取内容.. 当读成功时, read返回实际所读的字节数.

ZeroMQ(java)中监控Socket

- - CSDN博客架构设计推荐文章
基本上ZeroMQ(java)中基本的代码都算是过了一遍了吧,不过觉得它在日志这一块貌似基本没有做什么工作,也就是我们通过日志来知道ZeroMQ都发生了什么事情. 而且由于ZeroMQ中将连接的建立和重连接都进行了隔离,用户不需要做什么事情来维护连接,当然这样做的好处是使程序员的编码工作变少了,但是当然也有不好的地方,那就是用户失去了对ZeroMQ整个运行阶段的控制.

java socket参数详解:BackLog

- - 开源软件 - ITeye博客
 java socket参数详解:BackLog. 输入连接指示(对连接的请求)的最大队列长度被设置为 backlog 参数. 如果队列满时收到连接指示,则拒绝该连接. backlog参数必须是大于 0 的正值. 如果传递的值等于或小于 0,则假定为默认值. 经过测试这个队列是按照 FIFO(先进先出)的原则.

HTML5 Web socket和socket.io - wishyouhappy

- - 博客园_首页
   HTML5的新特性,用于双向推送消息(例如网页聊天,手机推送消息等). client利用regular http请求webpage. 请求的webpage 执行javascript脚本,open a connection to server.. 有新的信息时服务器和客户端可以相互发送信息(Real-time traffic from the server to the client .

tcp/ip ,http,socket的关系

- - 行业应用 - ITeye博客
  物理层、数据链路层、网络层、传输层、会话层、表示层和应用层.   通过初步的了解,我知道IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层,.   三者从本质上来说没有可比性,.   socket则是对TCP/IP协议的封装和应用(程序员层面上).   也可以说,TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,.

关于socket的一些初步研究

- iDesperadO - keakon的涂鸦馆
这些天在研究Tornado的源码,说实话它的代码过于艰深了,需要绕很多弯才能弄清. 我想其中的问题主要是我不太懂socket,于是就花了些时间学习socket,算是有了些收获,顺便记录在此. 实际上UNIX的设计者很喜欢用类似的方式来处理一类任务,其中数据传输就都被抽象成文件,包括磁盘文件、管道、FIFO和终端等.

socket连接和http连接的区别

- - 互联网 - ITeye博客
相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助. HTTP协议:简单对象访问协议,对应于应用层  ,HTTP协议是基于TCP连接的. tcp协议:    对应于传输层. ip协议:     对应于网络层 . TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据.

Java Socket常见异常处理

- - BlogJava-qileilove
java网络编程Socket通信中,通常会遇到以下异常情况:.   第1个异常是 java.net.BindException:Address already in use: JVM_Bind.   该异常发生在服务器端进行new ServerSocket(port)(port是一个0,65536的整型值)操作时.