免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2628 | 回复: 2
打印 上一主题 下一主题

简单事务协议(Trivial Transaction Protocol) [复制链接]

论坛徽章:
0
1 [报告]
发表于 2004-01-13 21:31 |只看该作者

简单事务协议(Trivial Transaction Protocol)

版主请看下面这篇文章,如果软件工程技术不接受的话我就想不出它应该放在chinaunix的哪个地方了。另外还有很多关于算法的程序,都发到c/c++版去了。

Network Working Group
Request for Comments: 9551
                                                       2003年5月


        简单事务协议(Trivial Transaction Protocol)



本备忘录状态

        这个RFC是为联机事务处理的通讯问题提出的正式标
        准,此备忘录的发布是无限制的。


摘要

        这个RFC是描述一种新的协议,它以UDP为传输层协议,从UDP的角度来看,它属于
        应用层;但是从使用者的角度来看,它又属于传输层。该协议以代价最小化为基
        础,为更高层的应用提供了一种可靠的,顺序的数据报服务。

目录

        概述
        TTP首部
        TTP连接的建立与终止
        TTP数据的发送与接收
        TTP数据的超时与重传
        TTP的拥塞控制策略
        TTP的性能



1.概述

        标准的TCP/IP提供两种传输层服务:TCP和UDP,分别提供虚电路服务和数据报服
        务。这两个协议差别很大,在我们要讨论的一类被称为“事务“的应用中,我们就
        会发现这些应用所需要的通讯陷入了TCP和UDP的鸿沟--没有哪一种协议是适当的。
       
        我们开始列举典型的“事务处理”应用中通讯模型的特征。
       
        *  非对称模式
       
        通讯的两个端点扮演不同的角色,通常称为“客户”和“服务器”。这导致了一个
        不对称的通讯模式。
       
        最简单的形式是,客户向服务器发一个请求,服务器返回一个对应的应答。
       
        有些情况可能需要连续的信息交换,称为一个“对话”或是“会话”。例如,一个
        从文件服务器读文件的请求会引起一系列的消息,每一个是一个文件块。
       
        *  单一的数据传输
       
        不管是什么模式,它总是由一系列简单的数据传输组成;从不需要双方同时发送数
        据。
       
        *  持续时间短
       
        事务序列通常具有短的持续时间,典型为100微秒到10秒之间,不会是几个小时。
       
        * 很小的数据包
       
        数据报通常在几百字节到几千字节左右。
       
        *  信息确定性
       
        在事务中传输的数据的自然单位是一个完整的信息(“记录”),不是字节流。
       
        TCP提供可靠的,顺序的,有流量控制的信息交换,但是,采用TCP代价太高了,对
        一个最简单的事务应用(客户端与服务器进行一次信息的交互),网络上交互的分
        组数是9个,而且,TCP内部的事务处理时间测量也不准确,客户端测量的时间是
        2*RTT(往返时间)+SPT(服务器处理时间),而实际上应该是RTT+SPT。而且采用
        TCP会消耗服务器大量的资源。
       
        我引用RFC1185里的一段话来表明TCP不适合作为事务应用的传输层协议:“当时人
        们希望TCP既是应用层事务协议的基础,同时也是面向连接协议的基础。当时讨论中
        甚至把既包含有SYN和FIN比特,同时又包含数据的报文段叫做‘圣诞树’报文段和
        ‘Kamikaze(敢死队)’报文段。但这种热情很快被泼了冷水,因为人们发现,三次
        SYN握手和FIN握手意味着一次数据交换至少需要5个分组。而且,TIME_WAIT状态的
        延续说明同一个连接不可能马上再次打开。于是,再没有人在这个领域做进一步的
        研究,尽管现在的某些应用程序(比如,简单邮件传送协议,SMTP)经常会产生很短
        的会话。人们一般都可以采用为每个连接选用不同的端口对的办法来避开重用问
        题”。
       
        UDP避免了TCP建立连接的系统开销。 然而,UDP有两个潜在的问题--
       
        (1)不可靠的通讯,导致包可能丢失,重复,或失序;
       
        (2)以前的主机需求RFC指出,单个IP数据包的长度不能超过576,这使得最大的
        发送数据为548字节,当然现在的主机一般都不存在这个限制(只要支持NFS,就可
        以发送8192字节的数据报),但是较大的UDP包必然会被IP层分片,任何一片的丢
        失都会导致整个UDP数据报的丢失,而且更为严重的是,当某个路由器丢弃属于某
        个UDP数据报的一片时,该数据报的其余各片仍然向目的地传送,浪费了网络的有
        限资源。
       
        由于UDP是不保证可靠的协议,数据报可能会丢失、失序或重复,因此实用的应用
        程序必须处理这些问题。这通常是在客户程序调用recvfrom时设置一个超时定时
        器,用以检测数据报的丢失,并重传请求。如果要使用超时定时器,客户程序就要
        测量RTT并动态更新,这是因为互连网上的RTT会在很大范围内变化,并且变化很
        快。但如果是服务器的应答丢失,而不是请求,那么服务器就要再次处理同一个请
        求,这可能会给某些服务带来问题。解决这个问题的办法之一是让服务器将每个客
        户最近一次请求的响应暂存起来,必要时重传这个应答即可,而不需要再次处理这
        个请求。最后,典型的情况是,客户向服务器发送的每个请求中都有一个不同的标
        识,服务器把这个标识在响应中传回来,使客户能把请求和响应匹配起来。但这将
        在程序中增加大量源代码。
       
        一方面,许多UDP应用程序都通过执行所有这些额外步骤(超时机制、RTT值测量、
        请求标识,等等)来增加可靠性;另一方面,随着新的UDP应用程序不断出现,这些
        步骤也在不断地推陈出新。为了开发“可靠的UDP应用程序”,你要有状态信息(序
        列号、重传计数器和往返时间估计器),原则上你要用到当前TCP连接块中的全部信
        息,因此,构筑一个“可靠的UDP”,要差不多跟设计一个TCP一样难。
       
        因此,我设计了这个协议,实现这个协议之后,应用程序就可以以最简单的代码实
        现最高效的通讯。这个协议是建立在UDP之上的,它提供了可靠的,顺序的报文流,
        同时又力求使得达成这些目标所花费的代价最小。从UDP的角度来看,这是一个
        应用层协议,从应用层来看,这又是一个传输层协议。
       
        这个协议并没有满足所有的要求,它对事务的限制为:
       
        *  只能支持最简单的事务:客户向服务器发一个请求,服务器返回一个对应的应
           答。
       
        *  没有分块发送功能,因此不能支持太大的事务包。


2.TTP首部

        TTP首部共9个字节,分别是:1字节协议标志,4字节客户号,4字节事务号。从高
        位算起,协议标志的第1位是建立连接标志,第二位是连接复位
        标志,第三位是连接结束标志,第四位是应答重发标志,中间两位保留,最后两
        位是重发计数器,从00到11,因此最多支持3次重发,而实际应用中只进行两次
        重发。注意从下面可以看出,重发计数器不是服务器端判断请求是否重发的依据,
        而是为了让客户端精确计算应答时间。
       
        4位客户号可以支持4G个客户同时连接。
       
        当一个客户与服务器建立连接时,初始事务号由客户指定,并不一定为零,以后
        每进行一次事务通讯,事务号都会加1,加到最大值后返回零。服务器方在收到
        客户的请求包后,将先判断本次事务号与上次事务号的关系,如果本次事务号
        小于上次事务号,认为请求是非法的请求(当然有可能是因为大上一次的请求在
        网络中传输时间过长,引起了延迟交付);如果事务号与上次事务号一致,认为
        是上次请求的重传,将把保存的上一次应答传给客户端;如果大于上次事务号,
        认为是新的请求。


3.TTP连接的建立与终止

        客户端在与服务器通讯之前,必须与服务器建立连接。连接建立的步骤如下:
       
        1)客户端发送一个仅含首部的TTP报文给服务端,这个首部的建立连接标志置为
        1,客户号为该客户的客户号,事务号由客户端决定,称为初始事务号。
       
        2)服务器收到建立连接请求后,将检验该客户号是否合法,以及该客户是否已
        经建立了连接,如果这两个条件有一个成立,将客户发来的请求报文的复位标志
        置为1,发给客户端,拒绝接受连接;否则,向客户发送相同的报文作为应答,
        接受连接,并且把这个报文存到该客户的连接信息中去。
       
        3)客户收到这个应答包后,连接就被建立起来了。
       
        一个报文从一台主机传到另一台主机所需的最大时间称为报文最大生存时间MSL
        (Maximum Segment Lifetime)。它是任何报文被丢弃前在网络内的最大时间。
        这个时间是有限的,因为IP报文有限制其生存时间的TTL字段。RFC793指出MSL应
        该为2分钟。
       
        当一个服务端重启之后,它有可能收到重启前客户端发出的迟到的报文,解决这
        个问题的办法是在重启后MSL时间内不能接受任何请求,因此这段时间称为平静
        时间。
       
        很多情况导致无法建立连接,原因可能是网络不同或者是服务器没有准备好。
        这些情况出现时,客户端在发出连接请求后将收不到应答,这是客户端将重发连
        接请求,最多重发两次。第一次请求连接的等待时间为12秒,以后每进行一次重
        发等待时间加倍,为24秒和48秒。
       
        连接的终止过程是这样的:
       
        1)客户端发送一个仅含首部的TTP报文给服务端,这个首部的建立连接标志置为
        1,客户号为该客户的客户号,事务号为上次的事务号加1。
       
        2)服务器收到了结束连接请求时,将检验该客户号是否合法,以及该客户是否已
        经建立了连接,如果这两个条件都满足,还要看请求的事务号是否在上次事务号+
        1 -- 上次事务号+M+1 之间(M为允许的最大丢失事务数),如果满足所有的条件,
        则向客户端发送一个与请求相同的报文作为应答,同时将该客户信息设为未连接。
       
        3)客户收到这个应答包后,连接就结束了。
       
        客户端在发出结束连接请求后也有可能收不到应答,这是应该重发请求,最多重发
        2次,但是超时定时器不应该为协议层设定的值,而应该是根据经历的事务的处理
        时间改进的某个值。同样,本次等待时间为上次的两倍。
       
        即使客户端收不到服务器的应答,这个连接也已经没有意义,因此客户端也应该认
        为它跟服务器的连接已经断了。这会造成客户端与服务器端的连接状态不同步,为
        了安全起见,这个同步应该由服务器端的管理者来做,把客户端的连接“杀”掉。


4.TTP数据的发送与接收

        客户端在发送请求时,将上一次的事务号加1作为本次的事务号,首部标志位的各标
        志都清零,然后进行发送,发送完毕后,进入应答的阻塞接收阶段。
       
        服务端收到数据后,首先要看该请求报文里的客户号是否合法,如果合法,要看该
        客户是否已经建立了连接,并且连接地址和端口号是否与请求包中的地址和端口号
        一致,如果这些都没有问题,那么将请求所含的事务号与存储的前一事务号相比
        较,如果本次事务号小于上次事务号,将该包丢弃;如果等与上次事务号,则重传
        上一次的应答;如果大于上一次的事务号,但又不超过上次事务号+M+1,就认为请
        求为合法的新请求,将交给高层来处理。
       
        如果通讯没有发生任何异常的话,本次的事务号会等于上次的事务号加1,但是,
        有可能因为网络或者服务器的原因引起了某次事务以失败告终:客户端已经多次重
        发请求,而服务端却从未收到。鉴于这种情况,因该允许本次的事务号位于上次事
        务号+1 -- 上次事务号+M+1 之间(M为允许的最大丢失事务数),以允许在偶尔的
        通讯异常后,客户端和服务端还能正常通信。
       
        要注意的是,因为事务号有长度限制,事务号当达到最大值时,会重新返回零,因
        此在进行合法性判断时,不能单纯的认为上次事务号+M+1就一定比上次事务号大,
        这种情况将在本文档的最后一章作更详细的讨论。



5.TTP数据的超时与重传
       
        客户端发送数据后,将启动一个定时器并接收应答。当收到首部与它发送的请求包
        的首部不一致的应答包时,将该应答包丢弃并继续等待,如果定时器到期,将重传
        请求。
       
        在一个事务中,从一个请求被提出到对应的应答被收到,总的间隔时间分为两部
        分,一部分是网络往返时间,简记为RTT,另一部分是服务器处理时间,简记为
        SPT。对于不同种类的交易,RTT可能相差不大,而SPT可能相差很大,但是,我们
        给客户端设超时时间时,不可能给每个交易设一个超时时间,因为这违反了事务层
        和应用层的层次关系。这个超时时间的设置很重要,如果设得太短了,就会引起大
        量不必要的重传,加重网络和主机的负担;如果设得太大了,当发生丢包时要重传
        时行动又太慢,使得交易的响应时间加长。
       
        重传超时时间采用动态设置,假设本次通讯往返时间为M,上一次平滑往返
        时间估计器的值为A n-1,上一次平滑均值偏差为D n-1,则此次平滑往返时间估
        计器的值为A n = A n-1 + 0.125*(M-A n-1),本次平滑均值偏差为 D n =
        D n-1 + 0.25 *(|M-An-1|-Dn-1),此次重传时间为An+4Dn,一次不成功下一次
        时间加倍。A的初始值为建立连接的时间+10秒。D的初始值定为0秒。
       
        TTP首部中有重传计数器,当客户端收到服务器端的应答时,将根据应答包里的重
        传计数器的值检查它是对应哪一次请求,从而更加精确的进行时间估计。注意的
        一点是服务端收到重传的请求后,如果它以前并没有收到该请求,将交给高层处
        理这个请求,处理完毕组织返回包的首部的重传计数器的值跟收到的请求包里的
        值是相同的,否则重传上一次保存的的结果,但是要把应答重发标志置位,客户
        收到应答重发标志为1的应答包时不进行响应时间的更新。



6.TTP的拥塞控制策略

        TTP的拥塞控制是对同一主机访问同一资源的所有客户端而言的。TTP客户端必须
        从以下四种选择中选出一种作为它的拥塞控制策略:
       
        1)无拥塞控制
       
        当确定同一主机的客户端同时提交的请求数较小时,或者是确定
        该主机上的客户端不会引起拥塞时可以采用这种方式。
       
        2)拥塞预防
       
        为同一主机上访问同一资源的客户端指定一个同时提交请求的最大
        值,这个值是固定的。当一台主机的客户端可能会造成拥塞,而又要保证该主机
        上的客户端具有一定的流量,或者该主机上的客户端的流量在总流量中占据了较
        大而且比较固定的份额,或者服务器为本主机上的客户所独占而服务器的处理能
        力比较稳定。
       
        3)拥塞避免
       
        服务器(包括网络)各种因素不确定,有出现拥塞的可能性。
       
        对一个主机上访问同一服务器(端口号也相同)的所有客户维持三个变量,一个
        称为发送窗口的尺寸sndwindow,一个称为发送窗口当前可用值
        sndwindowuseable,一个称为慢启动门限ssthresh。sndwindow和ssthresh的值可
        以手工配置,sndwindow一定要比ssthresh小,ssthresh至少为2。
       
       
        客户端发送一个新请求前,要对sndwindowuseable进行wait操作,收到对应的应
        答时,客户端发送一个新请求前,要对sndwindowuseable进行wait操作,收到对
        应的应答时,进行signal操作。当sndwindow小于ssthresh时,每收到一个应答,
        sndwindow的值加1,这个状态成为慢启动状态,否则,sndwindow的值加1/
        sndwindow,这个状态称为拥塞避免状态同时,如果sndwindow
        增加的值大于1,要对sndwindowuseable进行signal操作。
       
        当超时发生时,ssthresh设为当前sndwindow值的一半,sndwindow设为当前
        sndwindow的四分之一,同时对sndwindowuseable也要进行wait(3*sndwindow/
        4)操作,这个过程可能会等待一段时间,在完成设置之前,如果还有超时发生,
        不会引起上述值的变化。
       
        4)有底限的拥塞避免
       
        与上一种控制方法的区别是sndwindow有一个底限,这个底限是指定的。


7.TTP的性能

        * 超时时间的估计
       
        前面讲到的超时时间估计器是当前为止最好的估计器,它是网络专家经多年研究
        所得,既能适应状态稳定的客户-服务器网络,也能适应变动较大的情况,但是
        有一点要指出,这种时间估计还没做到家,因为不同的交易类型的处理时间是
        不同的。但是,继续细化已经很困难了,TCP的时间估计细化(按路由进行度量)
        我认为也是失败的。
       
        * 分组大小问题
       
        每一条路经都有其最大传输单元,如果传输的IP报文大于这个值,该报文就会被
        分片,丢失的可能性将加大,但是如果采用更复杂的传输方法,比如选择重传
        ARQ,算法将会很复杂,而且服务段也不能仅仅绑定一个端口,将使协议变得非
        常复杂,因此这不在我们的讨论范围之内。如果能将数据进行压缩,将大大缩短
        报文长度,能缓解这一问题,但也仅仅是缓解而已。
       
        * 大客户
       
        如果同一台主机上有很多客户,那么一个分组的丢失会使吞吐量急剧减少。当然
        慢启动会使得吞吐量慢慢恢复,但是需要一定的时间。第四种拥塞控制策略就是
        专门为这种情况设计的。
       
        * 事务号回绕
       
        如果一个服务器处理速度非常快,当一个客户端发送一个请求后没有收到应答,
        进行了两次重发后放弃了这次事务,然后又进行了4G笔事务,而这时在网络中传
        输的先前的请求又到达了服务端,这就会使服务器做出错误的解释。这种情况现
        在来看是不可能的,但是理论上是可能的。

论坛徽章:
0
2 [报告]
发表于 2004-01-15 15:51 |只看该作者

简单事务协议(Trivial Transaction Protocol)

我觉得unix程序员中有一个矛盾,写unix程序有很多情况都是要用到网络通讯的,但是很多unix程序员对socket实现不甚了解,而那些做交换机和路由器的人,又对unix不甚了解,我认识的搞路由器设计的人甚至都大会用数据库,真是隔行如隔山。
上面写的那篇文章是我在看了rfc 955之后写的,要说有什么意义,意义也真不怎么大,据我的测试,在联机事务处理中,采用这个协议,要比使用tcp套接字快10倍,而且没有什么诸如fin_wait_2之类的现象,但这又能如何?只能算是自娱自乐罢了。
下面是这个协议的客户端实现,也没什么高深的知识,我个人水平也很一般,也写不出什么高深的东西来,感兴趣的可以看一看。服务器端比较简单,我就不写了。
[code]/********************************************************************************************************
联机事务处理客户端程序
说明:uint16_t表示无符号16位整数,一般对应于unsigned short类型
这个实现要求套接字功能中的setsockopt()设置超时时间,在某些系统中,比如sco unix,早期版本的linux可能不支持,
需要用设置SIGALM信号处理函数的方式来解决
本程序在IBM AIX4.3下调试通过
*********************************************************************************************************/
#include <stdlib.h>;
#include <time.h>;
#include <sys/time.h>;
#include <sys/socket.h>;
#include <arpa/inet.h>;
#include <netinet/in.h>;


#define REPEATTIME 3
#define TRUE 1
#define FALSE 0
#define LOGINRTO 10
#define HEADLEN 6
#define MAXDATALEN 4096


static int clisock;
static int svrsock;
static struct sockaddr_in cliaddr;
static struct sockaddr_in svraddr;
static struct sockaddr_in svraddr2;
static unsigned char sphead[HEADLEN];


/********************************************************************************************************
登陆函数
输入参数:客户代号(16位无符号整数),初始事务号(16位无符号整数)
          客户端地址("xxx.xxx.xxx.xxx"),客户端端口号(16位无符号整数)
          服务端地址("xxx.xxx.xxx.xxx"),服务端端口号(16位无符号整数)
          要求输入参数为主机字节顺序
输出参数:1为成功,0为失败,错误信息保存在errno里
本函数修改了全局静态变量,是不可重入函数
********************************************************************************************************/
int login(uint16_t curid, uint16_t isn, const char * cliaddrs, uint16_t cliport, const char * svraddrs, uint16_t svrport)
{
        int i;
        struct timeval loginrto;
        int connected;
        int answered;
        int recvlen;
        int time1,time2;
        unsigned char answer[HEADLEN];
       
        connected=FALSE;
        clisock=socket(AF_INET,SOCK_DGRAM,0);
        if(clisock>;0)
        {
                cliaddr.sin_family=AF_INET;
                cliaddr.sin_port=htons(cliport);
                cliaddr.sin_addr.s_addr=inet_addr(cliaddrs);
               
                if(bind(clisock,(struct sockaddr *)&cliaddr,sizeof(cliaddr))==0)
                {
                        sphead[0]=0x80;
                        *((uint16_t *)(sphead+1))=htons(curid);
                        *((uint16_t *)(sphead+3))=htons(isn);
                        sphead[5]=0;
                       
                        svraddr.sin_family=AF_INET;
                        svraddr.sin_port=htons(svrport);
                        svraddr.sin_addr.s_addr=inet_addr(svraddrs);
                       
                        i=0;
                        loginrto.tv_sec=LOGINRTO;
                        loginrto.tv_usec=0;
                        answered=FALSE;
                       
                        /**** 接受应答循环,如果发来的是杂包则丢弃*/
                        while((i<REPEATTIME) && !answered)
                        {
                                sendto(clisock,sphead,sizeof(sphead),0,(struct sockaddr *)&svraddr,sizeof(svraddr));
                                setsockopt(clisock,SOL_SOCKET,SO_RCVTIMEO,&loginrto,sizeof(loginrto));
                                time(&time1);
                                recvlen=recvfrom(clisock,answer,sizeof(answer),0,NULL,NULL);
                                if((recvlen==sizeof(answer)) && (memcmp(answer+1,sphead+1,5)==0))/*是针对本次的应答包*/
                                {
                                        answered=TRUE;
                                        if(answer[0]==sphead[0])
                                                connected=TRUE;/*还有一种可能是answer[0]==0xC0,服务端拒绝了客户的连接*/
                                }
                                else
                                        if(recvlen<0)/*接收失败*/
                                        {
                                                i++;
                                                loginrto.tv_sec=loginrto.tv_sec<<2;
                                        }
                                        else/*接收到了上次的包*/
                                        {
                                                time(&time2);
                                                loginrto.tv_sec=loginrto.tv_sec-(time2-time1);
                                        }
                        }/*end of while*/
                }/*end of bind()==0 */
        }
       
        return connected;
}


/********************************************************************************************************
开始一个事务
********************************************************************************************************/
void beginsection(void)
{
        sphead[0]=0;
        sphead[3]++;
        sphead[4]=sphead[4]+(sphead[3]==0);
        sphead[5]=0;
        svraddr2=svraddr;
}

/********************************************************************************************************
退出函数
并不返回结果
********************************************************************************************************/
int logout(void)
{
        int i;
        int answered,disconnected;
        struct timeval logoutrto;
        int recvlen,time1,time2;
        unsigned char answer[HEADLEN];
       
        /*事务号加一*/
        beginsection();
        /*结束连接标志置位*/
        sphead[0]=0x20;
        logoutrto.tv_sec=LOGINRTO;
        logoutrto.tv_usec=0;
       
        i=0;
        answered=FALSE;
        disconnected=FALSE;
        /**** 接受应答循环,如果发来的是杂包则丢弃*/
        while((i<REPEATTIME) && !answered)
        {
                sendto(clisock,sphead,sizeof(sphead),0,(struct sockaddr *)&svraddr,sizeof(svraddr));
                setsockopt(clisock,SOL_SOCKET,SO_RCVTIMEO,&logoutrto,sizeof(logoutrto));
                time(&time1);
                recvlen=recvfrom(clisock,answer,sizeof(answer),0,NULL,NULL);
                if((recvlen==sizeof(answer)) && (memcmp(answer+1,sphead+1,5)==0))/*是针对本次的应答包*/
                {
                        answered=TRUE;
                        if(answer[0]==sphead[0])
                                disconnected=TRUE;
                }
                else
                        if(recvlen<0)/*接受失败*/
                        {
                                i++;
                                logoutrto.tv_sec=logoutrto.tv_sec<<1;
                        }
                        else/*接收到了上次的包*/
                        {
                                time(&time2);
                                logoutrto.tv_sec=logoutrto.tv_sec-(time2-time1);
                        }
        }/*end of while*/
       
        close(clisock);
        return disconnected;
}


int sendandrecv(const char * sendbuff,int sendsize, char * recvbuff, int recvsize)
{
        char sendbuff2[HEADLEN+MAXDATALEN];
        char recvbuff2[HEADLEN+MAXDATALEN];
        static unsigned int rtoa=6;/*平滑时间估计器*/
        static unsigned int rtod=1;/*标准偏差时间估计器*/
        static struct timeval rto;/*时间估计器*/

        int time1,time2,timem;
        int i;
        int answered;
        int returnlen;
        size_t fromlen;
       
        memcpy(sendbuff2,sphead,sizeof(sphead));
        memcpy(sendbuff2+sizeof(sphead),sendbuff,sendsize);
        rto.tv_sec=rtoa+rtod<<2;
        rto.tv_usec=0;
       
        answered=FALSE;
        i=0;
        fromlen=sizeof(svraddr2);
        while((i<REPEATTIME)&& !answered)
        {
                sendto(clisock,sendbuff2,sizeof(sphead)+sendsize,0,(struct sockaddr *)&svraddr,sizeof(svraddr));
                setsockopt(clisock,SOL_SOCKET,SO_RCVTIMEO,&rto,sizeof(rto));
                time(&time1);
                returnlen=recvfrom(clisock,recvbuff2,sizeof(sphead)+recvsize,0,(struct sockaddr *)&svraddr2,&fromlen)-sizeof(sphead);
                time(&time2);
                timem=time2-time1;
                if((returnlen>;0)&& (memcmp(recvbuff2+1,sphead+1,5)==0))/*接收到本次的返回包*/
                {
                        answered=TRUE;
                        rtod=rtod+(abs(timem-rtoa)-rtod)>;>;2;
                        rtoa=rtoa+(timem-rtoa)>;>;3;
                        sphead[5]++;
                }
                else
                        if((returnlen==0)&&(recvbuff2[0]==0x20))/*收到复位标志*/
                                answered=TRUE;
                        else
                                if(returnlen<0)/*接收失败*/
                                {
                                        i++;
                                        rto.tv_sec=rto.tv_sec<<1;
                                }
                                else/*接受的不是本次的包*/
                                        rto.tv_sec=rto.tv_sec-timem;
        }
       
        return returnlen;
}

int main(void)
{
        return 0;
}[/code]

论坛徽章:
1
NBA常规赛纪念章
日期:2015-05-04 22:32:03
3 [报告]
发表于 2006-05-10 11:09 |只看该作者
还不是我要的东东了。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP