TCP不php 设置缓冲区区和php 设置缓冲区区有什么区别?

2029人阅读
Author:阿冬哥
Created:&
Blog:http://blog.csdn.net/c/
Copyright 2013&阿冬哥&http://blog.csdn.net/c/
使用以及转载请注明出处
1 设置socket tcp缓冲区大小的疑惑
& & & &疑惑1:通过setsockopt设置SO_SNDBUF、SO_RCVBUF这连个默认缓冲区的值,再用getsockopt获取设置的值,发现返回值是设置值的两倍。为什么?
& & & & 通过网上查找,看到linux的内核代码/usr/src/linux-2.6.13.2/net/core/sock.c,找到sock_setsockopt这个函数的这段代码:
& & & case&SO_SNDBUF:
& & & & & & & & & & & & /* Don't error on this BSD doesn't and if you think
& & & & & & & & & & & & & &about it this is right. Otherwise apps have to
& & & & & & & & & & & & & &play 'guess the biggest size' games. RCVBUF/SNDBUF
& & & & & & & & & & & & & &are treated in BSD as hints */
& & & & & & & & & & & & if (val & sysctl_wmem_max)//val是我们想设置的缓冲区大小的值
& & & & & & & & & & & & & & & & val = sysctl_wmem_//大于最大值,则val值设置成最大值
& & & & & & & & & & & & sk-&sk_userlocks |= SOCK_SNDBUF_LOCK;
& & & & & & & & & & & & if ((val * 2) & SOCK_MIN_SNDBUF)//val的两倍小于最小值,则设置成最小值
& & & & & & & & & & & & & & & & sk-&sk_sndbuf = SOCK_MIN_SNDBUF;
& & & & & & & & & & & & else
& & & & & & & & & & & & & & & & sk-&sk_sndbuf = val * 2;//val的两倍大于最小值,则设置成val值的两倍
& & & & & & & & & & & & /*
& & & & & & & & & & & & &* & & &Wake up sending tasks if we
& & & & & & & & & & & & &* & & &upped the value.
& & & & & & & & & & & & &*/
& & & & & & & & & & & & sk-&sk_write_space(sk);
& & & & & & & & & & & &
& & & & & & & & case&SO_RCVBUF:
& & & & & & & & & & & & /* Don't error on this BSD doesn't and if you think
& & & & & & & & & & & & & &about it this is right. Otherwise apps have to
& & & & & & & & & & & & & &play 'guess the biggest size' games. RCVBUF/SNDBUF
& & & & & & & & & & & & & &are treated in BSD as hints */
& & & & & & & & & & & & if (val & sysctl_rmem_max)
& & & & & & & & & & & & & & & & val = sysctl_rmem_
& & & & & & & & & & & & sk-&sk_userlocks |= SOCK_RCVBUF_LOCK;
& & & & & & & & & & & & /* FIXME: is this lower bound the right one? */
& & & & & & & & & & & & if ((val * 2) & SOCK_MIN_RCVBUF)
& & & & & & & & & & & & & & & & sk-&sk_rcvbuf = SOCK_MIN_RCVBUF;
& & & & & & & & & & & & else
& & & & & & & & & & & & & & & & sk-&sk_rcvbuf = val * 2;
& & & & & & & & & & & &
& & & & 从上述代码可以看出:(1)当设置的值val
& 最大值sysctl_wmem_max,则设置为最大值的2倍:2*sysctl_wmem_max;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& & (2)当设置的值的两倍val*2 & 最小值,则设置成最小值:SOCK_MIN_SNDBUF;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& & (3)当设置的值val & 最大值sysctl_wmem_max,且 val*2 &&SOCK_MIN_SNDBUF, 则设置成2*val。
& & & & 查看linux 手册:
& & & & & & SO_RCVBUF: & & & & & & &
& & & & & & & & & & Sets&or&gets&the&maximum&socket&receive&buffer&in&bytes.&&
& & & & & & & & & & The&kernel doubles&this&value&(to&allow&space&for&bookkeeping&overhead)&when&it & & & & & & & & & & is&set&using&setsockopt(2),&
& & & & & & & & & & and&this&doubled&value&is&returned&by getsockopt(2).
& & & & & & & & & & The&default&value&is&set&by&the /proc/sys/net/core/rmem_default&file,&
& & & & & & & & & & and&the&maximum&allowed&value is&set&by&the&/proc/sys/net/core/rmem_max&file. &
& & & & & & & & & & The&minimum (doubled)&value&for&this&option&is&256.
& & & 查看我的主机Linux 2.6.6 :/proc/sys/net/core/rmem_max:
& & & & & &4194304 //4M
& & & 查看/proc/sys/net/core/wmem_max:
& & & & & &8388608 & //8M
& & &所以,能设置的接收缓冲区的最大值是8M,发送缓冲区的最大值是16M。
& & & & 疑惑2:为什么要有2倍这样的一个内核设置呢?我的理解是,用户在设置这个值的时候,可能只考虑到数据的大小,没有考虑数据封包的字节开销。所以将这个值设置成两倍。
注:overhead,在计算机网络的帧结构中,除了有用数据以外,还有很多控制信息,这些控制信息用来保证通信的完成。这些控制信息被称作系统开销。
2 tcp缓冲区大小的默认值
& & & &建立一个socket,通过getsockopt获取缓冲区的值如下:
& & & & & & & 发送缓冲区大小:SNDBufSize =&16384
&&&&&&&&& & & 接收缓冲区大小:RCVBufSize =&87380&
& & & & 疑惑3:linux手册中,接收缓冲区的默认值保存在/proc/sys/net/core/rmem_default,发送缓冲区保存在/proc/sys/net/core/wmem_default。
[root@cfs_netstorage core]# cat /proc/sys/net/core/rmem_default
[root@cfs_netstorage core]# cat /proc/sys/net/core/wmem_default
& & & & 可知,接收缓冲区的默认值是:M。发送缓冲区的默认值是:2K。为什么建立一个socket时得到的默认值是8???
& & & & 进一步查阅资料发现,&linux下socket缓冲区大小的默认值在/proc虚拟文件系统中有配置。分别在一下两个文件中:
/proc/sys/net/ipv4/tcp_wmem
[root@cfs_netstorage core]# cat /proc/sys/net/ipv4/tcp_wmem
4096 & &16384 & 131072 &//第一个表示最小值,第二个表示默认值,第三个表示最大值。
/proc/sys/net/ipv4/tcp_rmem
[root@cfs_netstorage core]# cat /proc/sys/net/ipv4/tcp_rmem
4096 & &87380 & 174760
& & & &由此可见,新建socket,选取的默认值都是从这两个文件中读取的。可以通过更改这两个文件中的值进行调优,但是最可靠的方法还是在程序中调用setsockopt进行设置。通过setsockopt的设置,能设置的接收缓冲区的最大值是8M,发送缓冲区的最大值是16M(Linux
2.6.6中)。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:4988次
排名:千里之外
(1)(2)(1)(3)tcp socket的发送与接收缓冲区 -
- ITeye技术网站
博客分类:
tcp socket的发送缓冲区实际上是一个结构体struct sk_buff的队列,我们可以把它称为发送缓冲队列,由结构体struct sock的成员sk_write_queue(struct sk_buf_head)表示。sk_write_queue是一个结构体struct sk_buff_head类型,这是一个struct sk_buff的双向链表,其定义如下:
struct sk_buff_head {
struct sk_buff *
struct sk_buff *
//队列长度(即含有几个struct sk_buff)
内核代码中,先在这个队列中创建足够存放数据的struct sk_buff,然后向队列存入应用数据。
结构体struct sock的成员sk_wmem_queued表示发送缓冲队列中已分配的字节数,一般来说,分配一个struct sk_buff是用于存放一个tcp数据报,其分配字节数应该是MSS+协议首部长度。在我的实验环境中,MSS值是1448,协议首部取最大长度MAX_TCP_HEADER,在我的实验环境中为224。经数据对齐处理后,最后struct sk_buff的truesize为1956。也就是队列中每分配一个struct sk_buff,成员sk_wmem_queue的值就增加1956。
struct sock的成员sk_forward_alloc是表示预分配长度。当我们第一次要为发送缓冲队列分配一个struct sk_buff时,我们并不是直接分配需要的内存大小,而是会以内存页为单位进行的预分配。
tcp协议分配struct sk_buff的函数是sk_stream_alloc_pskb。它首先根据传入的参数指定的大小在内存中分配一个struct sk_buff,如果成功,sk_forward_alloc取该大小值,并向上取整到页(4096字节)的整数倍。并累加到struct sock的成员sk_prot,也即表示tcp协议的结构体mytcp_prot的成员memory_allocated中,该成员是一个指针,指向变量tcp_memory_allocated,它表示的是当前整个TCP协议当前为缓冲区所分配的内存(包括读缓冲队列)
当把这个新分配成功的struct sk_buff放入到缓冲队列sk_write_queue后,从sk_forward_alloc中减去该sk_buff的truesize值。第二次分配struct sk_buff时,只要再从sk_forward_alloc中减去新的sk_buff的truesize即可,如果sk_forward_alloc已经小于当前的truesize,则将其再加上一个页的整数倍值,并累加入tcp_memory_allocated。
也就是说,通过sk_forward_alloc使全局变量tcp_memory_allocated保存当前tcp协议总的缓冲区分配内存的大小,并且该大小是页边界对齐的。前面讲到struct sock的成员sk_forward_alloc表示预分配内存大小,用于向全局变量mytcp_memory_allocated累加当前已分配的整个TCP协议的缓冲区大小。之所以要累加这个值,是为了对tcp协议总的可用缓冲区大小作限制。表示TCP协议的结构体mytcp_prot还有几个成员与缓冲区相关。
mysysctl_tcp_mem是一个数组,由mytcp_prot的成员sysctl_mem指向,数组共有三个元素,mysysctl_tcp_mem[0]表示对缓冲区总的可用大小的最低限制,当前总共分配的缓冲区大小低于这个值,则没有问题,分配成功。mysysctl_tcp_mem[2]表示对缓冲区可用大小的最高硬性限制,一旦总分配的缓冲区大小超出这个值,我们只好把tcpsocket的发送缓冲区的预设大小sk_sndbuf减小为已分配缓冲队列大小的一半,但不能小于SOCK_MIN_SNDBUF(2K),但保证这一次的分配成功。mysysctl_tcp_mem[1]介于前面两个值的中间,这是一个警告值,一旦超出这个值,进入警告状态,这个状态下,根据调用参数来决定此次分配是否成功。
这三个值的大小是根据所在系统的内存大小,在初始化时决定的,在我的实验环境中,内存大小为256M,这三个值分配是:96K,128K,192K。它们可以通过/proc文件系统,在/proc/sys/net/ipv4/tcp_mem中进行修改。当然,除非特别需要,一般无需改动这些缺省值。
mysysctl_tcp_wmem也是一个同样结构的数组,表示发送缓冲区的大小限制,由mytcp_prot的成员sysctl_wmem指向,其缺省值分别是4K,16K,128K。可以通过/proc文件系统,在/proc/sys/net/ipv4/tcp_wmem中进行修改。struct sock的成员sk_sndbuf的值是真正的发送缓冲队列的预设大小,其初始值取中间一个16K。在tcp数据报的发送过程中,一旦sk_wmem_queued超过sk_sndbuf的值,则发送停止,等待发送缓冲区可用。因为有可能一批已发送出去的数据还没有收到ACK,同时,缓冲队列中的数据也可全部发出去,已达到清空缓冲队列的目的,所以,只要在网络不是很差的情况下(差到没有办法收到ACK),这个等待在一段时间后会成功的。
全局变量mytcp_memory_pressure是一个标志,在tcp缓冲大小进入警告状态时,它置1,否则置0。mytcp_sockets_allocated是到目前为止,整个tcp协议中创建的socket的个数,由mytcp_prot的成员sockets_allocated指向。可以在/proc/net/sockstat文件中查看,这只是一个供统计查看用的数据,没有任何实际的限制作用。
mytcp_orphan_count表示整个tcp协议中待销毁的socket的个数(已无用的socket),由mytcp_prot的成员orphan_count指向,也可以在/proc/net/sockstat文件中查看。
mysysctl_tcp_rmem是跟mysysctl_tcp_wmem相同结构的数组,表示接收缓冲区的大小限制,由mytcp_prot的成员sysctl_rmem指向,其缺省值分别是4096bytes,87380bytes,174760bytes。它们可以通过/proc文件系统,在/proc/sys/net/ipv4/tcp_rmem中进行修改。struct sock的成员sk_rcvbuf表示接收缓冲队列的大小,其初始值取mysysctl_tcp_rmem[1],成员sk_receive_queue是接收缓冲队列,结构跟sk_write_queue相同。
tcp socket的发送缓冲队列跟接收缓冲队列的大小既可以通过/proc文件系统进行修改,也可以通过TCP选项操作进行修改。套接字级别上的选项SO_RCVBUF可用于获取和修改接收缓冲队列的大小(即strcut sock-&sk_rcvbuf的值),比如下列的代码可用于获取当前系统的接收缓冲队列大小:
int rcvbuf_
int len = sizeof(rcvbuf_len);
if( getsockopt( fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf_len, &len ) & 0 ){
perror("getsockopt: ");
return -1;
printf("the recevice buf len: %d\n", rcvbuf_len );
而套接字级别上的选项SO_SNDBUF则用于获取和修改发送缓冲队列的大小(即struct sock-&sk_sndbuf的值),代码同上,只需改SO_RCVBUF为SO_SNDBUF即可。
获取发送和接收缓冲区的大小相对简单一些,而设置的操作在内核中动作会稍微复杂一些,另外,在接口上也会有所差异,即由setsockopt传入的表示缓冲区大小的参数是实际大小的1/2,即,如果想要设发送缓冲区的大小为20K,则需要这样调用setsockopt:
int rcvbuf_len = 10 * 1024; //实际缓冲区大小的一半。
int len = sizeof(rcvbuf_len);
if( setsockopt( fd, SOL_SOCKET, SO_SNDBUF, (void *)&rcvbuf_len, len ) & 0 ){
perror("getsockopt: ");
return -1;
在内核中,首先内核要判断新设置的值是否超过上限,若超过,则取上限为新值,发送和接收缓冲区大小的上限值分别为sysctl_wmem_max和sysctl_rmem_max的2倍。这两个全局变量的值是相等的,都为(sizeof(struct sk_buff) + 256) * 256,大概为64K负载数据,由于struct sk_buff的影响,实际发送和接收缓冲区的大小最大都可设到210K左右。它们的下限是2K,即缓冲区大小不能低于2K。
另外,SO_SNDBUF和SO_RCVBUF有一个特殊的版本:SO_SNDBUFFORCE和SO_RCVBUFFORCE,它们不受发送和接收缓冲区大小上限的限制,可设置不小于2K的任意缓冲区大小。
浏览: 31112 次
来自: 重庆网速在线测试
网速测试的目的是测试用户上网线路下载文件能达到的速率。
几种上网方式参考速度: * 512k带宽时下载速度:58K/S* 1M带宽时下载速度:90K/S * 1.5M 带宽时下载速度:130K/S * 2M带宽时下载速度:180K/S * 3M 带宽时下载速度:270K/S* 4M 带宽时下载速度:360K/S。
用户申请的宽带业务速率指技术上所能达到的最大理论速率值,用户上网时还受到用户电脑软硬件的配置、所浏览网站的位置、对端网站带宽等情况的影响,故用户上网时的速率通常低于理论速率值。
合理设置缓冲区大小,提高ADSL宽带网速
作者:admin&&&&时间: 21:27:17&&&&浏览:
&&&&&& 一般地,TCP/IP默认数据传输单元缓冲区大小为576字节。当某个TCP/IP分组数据发生错误,整个缓冲区中的内容,都将被丢失并重新传输,若频繁发生错误,那就将反复进行重新传输,进而影响ADSL数据传输效率,影响。因此,合理设置缓冲区大小,对确保ADSL数据传输效率将起到直接的影响。&&&&&& 具体方法如下:&&&&&& 第一步:点击&开始&-&运行&,在弹出的&运行&窗口中输入&regedit&,单击&确定&按钮,打开&注册表编辑器&。&&&&&& 第二步:在&注册表编辑器&中依次展开&HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VxD\MSTCP&项。&&&&&& 第三步:查看右侧窗口,是否存在&DefaultRcvWindows&项。如果不存在,则在右侧窗口中单击鼠标右键,依次点击&新建&-&字符串值&,即能在右侧窗口中新建一个字符串值,将其命名为&DefaultRcvWindows&。&&&&&& 第四步:双击新建的&DefaultRcvWindows&项,随即弹出&编辑字符串&窗口,在&数据数值&栏中输入数字&256960&,单击&确定&按钮以确认操作。&&&&&& 完成上述操作后,重新启动计算机,以便使修改生效。&&&&&& 此后,由于TCP/IP的数据传输单元缓冲区变小,当传输出现问题时,丢失的数据将大幅减少,进而提升整体上网速度。
&&&&& 相关链接&&&&& &&&&& 今天,再介绍一个提高网速的方法,那就是修改网卡IRQ资源。&&&&& IRQ的英文全称是Interrupt Request,翻译成中文就是&中断请求&,缩写为IRQ。&&&&& IRQ主要是就外部设备而言的。声卡、调制解调器等外部设备,在一般情况下并不使用,也就是说,不占用CPU的工作时 间。当我们播放声音文件、上网时,声卡、调制解调器就会向 CPU发出申请,要求CPU分配一些工作时间给它们。
把文章分享到:
QQ交流群()数据缓冲区_百度百科
关闭特色百科用户权威合作手机百科
收藏 查看&数据缓冲区本词条缺少名片图,补充相关内容使词条更完整,还能快速升级,赶紧来吧!
数据缓冲区是用户前端用来存储、操纵数据的对象。在每一个DataWindow对象中有4个二维表作为数据缓冲区,用来存储查询到的数据。外文名data cache area词&&&&性名词
自我管理数据缓冲区内存
开发具有高效性、简单性、可移植性和安全性的代码
C 程序设计语言定义了两个标准的:malloc() 和 free()。C 经常使用那些函数在运行时分配缓冲区,以便在函数之间传递数据。然而在许多场合下,您无法预先确定缓冲区所需的实际大小,这对于构造复杂的 C 程序来说,可能会导致几个根本性的问题。在本文中,Xiaoming Zhang 倡导一种自我管理的抽象数据缓冲区。他概括地给出了抽象缓冲区的伪 C 代码实现,并详细介绍了采用这种机制的优点。
软件的规模和复杂性随时都在增长,从根本上影响了应用程序的体系结构。在许多场合下,将所有功能编码进软件的单个部分中是不切实际的。让独立的软件部分相互交互,比如以插件的形式,这样做的重要性正在变得越来越明显。要相对容易地实现这种交互,甚至是在不同厂商编写的软件部分之间,软件需要有定义良好的接口。使用诸如 C 这样的传统来编写满足这种需要的软件可能是一个挑战。
考虑到这种挑战,本文将研究 C 程序设计语言中的数据缓冲区接口,同时着眼于如何改进当前实践。尽管内存管理看起来可能无足轻重,但是恰当设计的接口能够产生高效、简单和可移植的代码 —— 这其中每个特性都需要进行内存管理才能实现。因而, 下一节将概略介绍程序员在采用传统数据缓冲区管理方案时所面对的各种问题。后面跟着要介绍的是 抽象数据缓冲区方案,并通过实现来进行说明,这种方案解决了许多问题;最后要介绍的是一些 代码片断,用以演示该解决方案的好处。
传统实践和它们带来的问题
C 经常使用动态分配的缓冲区(通过调用 malloc() / free() 函数)在函数之间传递数据。尽管该方法提供了灵活性,但它也带来了一些性能影响。首先,它要求在需要缓冲区块的任何地方进行额外的管理工作(分配和释放内存块)。如果分配和释放不能在相同的代码位置进行,那么确保在某个内存块不再需要时,释放一次(且仅释放一次)该内存块是很重要的;否则就可能导致或代码崩溃。其次,必须预先确定缓冲区的大小才能分配该内存块。然而,您也许会发现,确定数据大小并不总是那么容易。开发人员经常采用最大数据尺寸的保守估计,而这样可能导致严重的内存资源浪费。
为避免由于多次释放而导致的可能的和代码崩溃,好的编程实践要求您明确地预定义负责分配和释放缓冲区内存的程序部分。然而在实践中,定义职责会导致其他困难。在传统方案下,由于在创建缓冲区时必须指定大小,因此 数据提供者(它可能知道它所提供的数据的大小)是用来执行缓冲区分配操作的最佳搭档。另一方面,用于释放的最佳搭档可能是 数据使用者,因为它知道何时不再需要该数据。通常情况下,数据提供者和数据使用者是不相同的。
当数据提供者和数据使用者来自不同的软件提供商时,进行交互的各方可能采用不同的底层内存管理机制。例如,有些软件提供商可能选择自我管理的堆空间,而其他软件提供商则依赖底层操作系统(OS)来获得这样的功能。此外,不同的操作系统可能以不同的方式实现内存管理。例如,PalmOS 提供两种不同的内存资源:基于堆和基于数据库。一般来讲,不同的内存管理机制具有各自的优点和缺点,因此您可能不希望预先假定某种特定的机制。不同的首选项甚至可能导致相互冲突的代码编写习惯。
解决这个问题的三种方法如下:
交互方之一定义用于数据交换的底层机制。另一方总是使用已公布的接口来分配或释放缓冲区,从而避免潜在的不一致。这种模型需要双方都坚持一个可能与软件基本功能无关的编程约定,而且在一般情况下,这个编程约定可能使代码更加不可重用。
驱动数据交换的那一方将负责管理操作 —— 当该方充当数据提供者时,这是一个相对适当的方案。 然而,当该方充当数据使用者时,事情就变得棘手了。为避免去发现数据大小,数据使用者可以分配一个任意大小的缓冲区。如果该数据缓冲区没有足够大,就必须对数据提供者发出多次调用。因此这种方法需要围绕该交互调用编写额外的循环代码,以备多次调用之需。
对于第三种选择,数据使用者将对管理操作负责。然而在这种情况下,如果另一方是数据提供者,数据使用者必须预先发出一次调用以发现缓冲区大小 —— 从而给另一方施加了更多的负担,即编写逻辑代码来提供关于缓冲区大小的信息,而这可能需要执行耗时的算法。而且,这种解决办法还可能引入严重的效率问题:假设函数 a() 从函数 b() 获得数据,后者反过来又在执行期间从函数 c() 获得数据。假设发现缓冲区大小和提供实际的数据都需要执行相同的算法。
为了从 b() 获得数据, a() 必须发出两次调用:一次用于确定缓冲区大小,另一次用于获得实际数据。对于向 a() 发出的每次调用, b() 都必须对 c() 发出两次调用。因此,当这个操作结束时, c() 中的算法代码可能已经执行了四次。原则上,该代码应该仅执行一次。
显而易见地,这三种解决办法全都存在局限性,因此传统缓冲区内存管理方法并不是适合编写大规模交互的机制。
除了上述困难之外,安全性也证明是传统方法存在的问题:传统缓冲区管理方案无法容易地防止恶意用户刻意改写数据缓冲区,从而导致程序异常。考虑到所有这一切,设计一个适当的数据缓冲区接口就势在必行![1]
首先在若干字符作为一个块传输比逐个发送字符耗费的时间少。其次如果你输入有误。就可以使用您的键盘更改功能来修正错误。并且最终按下回车,就可以发送正确的输入。缓冲分为两类,完全缓冲和行缓冲。对于完全缓冲来说,缓冲区满时,缓冲区会被清空。此时缓冲区中的内容也会发往目的地。这种类型的缓冲通常出现在文件输入中。缓冲区的大小取决于系统。但512和4096字节的缓冲区大小比较常见,对于行缓冲来说,遇到一个换行字符时,缓冲区中的内容就会被清空。键盘输入是标准的行缓冲。因此按下回车,缓冲就会被清空。从概念上讲,数据缓冲区在传统方案下是由两个操作创建的:数据缓冲区实体的创建和实际内存的分配。然而事实上,在实际数据变得可用之前,您不需要分配实际的内存 —— 即可以将两个操作分离开来。
最初可以使用内存块的一个空来创建一个抽象缓冲区。抽象数据缓冲区仅在实际数据变得可用时才分配内存。释放内存也变成了抽象数据缓冲的责任。考虑到所有这些,集中内存管理和数据复制操作就会带来以下优点:
各方都能通过调用预定义的 API 函数来构造和/或销毁数据缓冲区。 内存使用将保持接近最优状态,因为缓冲区内存仅在必要时才分配,并且会尽快释放,从而最小化。 任何一方都不需要知道底层的内存管理方案,使得软件高度可移植,同时保证了交互双方之间的兼容性。 由于没有哪一方需要管理内存,确定缓冲区的大小就变得不必要了(因而也不可能存在前面指出的多次执行问题)。 事实证明也不可能会发生,因为仅当存在额外数据空间时才会复制数据。
一种简单的实现
为了表示一个抽象数据缓冲区,需要声明两个结构化的:
清单 1. 声明两个结构化的来表示一个抽象数据缓冲区
typedef struct BufferBlockHeader_st BufferBlockH struct BufferBlockHeader_st { BufferBlockHeader * pNextB}; struct Buffer_st { long int totalL BufferBlockHeader * pFirstB short int startP BufferBlockHeader * pLastB short int endP}; typedef struct Buffer_st B
Buffer 包含关于已创建的抽象缓冲区的信息,它还管理内存块的一个链表:
totalLoength 记录当前存储在缓冲区中的字节数。 pFirstBlock 指向该链表中的第一个内存块。 startPoint 记录第一个内存块中第一个字节的偏移位置。 pLostBlock 指向该链表的最后一个内存块。 endPoint 记录最后一个内存块中第一个空闲字节的偏移位置。
您可以向 Buffer 引入一个附加参数,用以指定每个内存块的大小,并且可以在抽象缓冲区的初始化期间,将该参数设置为一个可取的值。这里假设使用默认块大小。
如果分配了的话, BufferBlockHeader 结构中的 pNextBlock 总是指向该链表中的下一个内存块。每个内存块在分配时都包含一个 BufferBlockHeader 头,后面跟着一个用于存储实际数据的缓冲区块。
图 1 描述了一个存储了一些数据的抽象缓冲区。
图 1. 抽象缓冲区的数据结构
抽象缓冲区的数据结构M 表示 Buffer 的大小(它通常为 20 字节), B 表示所选择的内存块大小。内存开销大约为 (M+B) 个字节(每个内存块开头的指针忽略不计)。 (M+B) 中的 B 平均起来仅有所使用的第一和最后一个内存块的一半。这个开销几乎保持不变。
在能够缓冲数据之前,必须通过调用下面的 newBuffer() 函数来显式地创建抽象缓冲区:
清单 2 使用 newBuffer() 函数创建抽象缓冲区
Buffer * newBuffer() { allocate a B init}
在 清单 2中,该函数分配了包含一个 Buffer 的内存块,并初始化它的条目以指明它是一个空抽象缓冲区。
相应地,必须在使用抽象缓冲区之后通过调用下面的 freeBuffer() 函数来销毁它:
清单 3 使用 freeBuffer() 函数来销毁抽象缓冲区
void freeBuffer(Buffer * pBuffer /* pointer to the buffer to be freed */ ) { while (there is more memory block in the linked list) { free t } free the B}
清单 3中的函数释放链表中的所有内存块,然后释放由 newBuffer() 分配的 Buffer 。
要逐步向抽象缓冲区追加数据段,可使用以下函数:
清单 4. 逐步向抽象缓冲区追加数据段
long int appendData(Buffer * pBuffer, /* pointer to the abstract buffer */ byte * pInput, /* pointer to the data source */ long int offset, /* offset of the input data */ long int dataLength /* number of bytes of the input data */ ) { while (there is more input data) { fill the
if (there is more input data) { allocate a new memory block and add it
清单 4中的函数把存储在 pInput[offset..offset+dataLength] 中的字节复制到 pBuffer 所指向的抽象缓冲区中,并在必要时在链表中插入新的内存块,然后返回成功复制到抽象缓冲区中的字节数目。
采用类似的方式,您可以使用以下函数,逐段地从抽象缓冲区读取数据段:
清单 5. 从抽象缓冲区读取数据段
long int readData(Buffer * pBuffer, /* pointer to the abstract buffer */ byte * pOutput, /* pointer to the output byte array */ long int offset, /* offset of the output byte array */ long int arrayLength /* size of available output byte array */ ) { while (there is something more to read and there is room for output) { read from th if (the first memory block is empty) { delete the first memory block from the linked list } }}
在 清单 5 中,该函数销毁性地从 pBuffer 所指向的抽象缓冲区最多读取 arrayLength 个前导字节,并在内存块变为空时从链表中删除它们,然后返回成功读取的字节数目。
如果需要,您可以实现一个类似 readData() 的函数来允许非销毁性的读取。
实现一个函数来返回当前存储在抽象缓冲区中的字节数目,这样可能会带来好处。
清单 6. 返回抽象缓冲区中的字节数目
long int bytesAvailable(Buffer * pBuffer /* pointer to the abstract buffer */ ) { return totalL}
新手上路我有疑问投诉建议参考资料 查看

我要回帖

更多关于 tcp缓冲区 的文章

 

随机推荐