分享
 
 
 

低速串行链路上的TCPIP头部压缩(3)

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

附录A:一个实现示例

下面是本文档中描述的协议的一个实现例子。

因为很多可能处理这些代码的人很熟悉BerkeleyUnix内核及其编码风格(被亲切地称为

“内核规范格式”),这些代码正是遵循该风格。使用了Berkeley“子程序”(subroutines,

实际上就是宏和/或内联汇编的扩展)来转换网络字节序(networkbyteorder)以及拷贝

/比较字节串。这些例行程序在A.5中为那些不熟悉它们的读者作简要介绍。

这些代码在25页列出的所有机器上运行通过。作者希望没有字节序或对齐方面的问

题(但是其中嵌入了假设的对BerkeleyUnix有效的对齐方式,这种对齐方式对于其他IP

实现可能不正确。见sl_compress_tcp和sl_decompress_tcp中提到的关于对齐的注释)。

曾试图尝试使这些代码更高效,不幸的是可能会使其中的一部分不可理解。作者为

此感到抱歉(老实说,我的C语言风格被认为是晦涩的,并且借口就是“效率”)。

该实例代码以及完整的BerkeleyUnix实现(机器可读的)通过匿名FTP从

ftp.ee.lbl.gov(128.3.254.68)上得到,即文件cslip.tar.Z。这是一个压缩的Unix

tar文件,必须以二进制模式进行ftp。附录中所有代码均包含下面的版权通告:/*

*Copyright(c)1989RegentsoftheUniversityofCalifornia.

*Allrightsreserved.

*

*Redistributionanduseinsourceandbinaryformsare

*permittedprovidedthattheabovecopyrightnoticeandthis

*paragraphareduplicatedinallsUChformsandthatany

*documentation,advertisingmaterials,andothermaterials

*relatedtosuchdistributionanduseacknowledgethatthe

*softwarewasdevelopedbytheUniversityofCalifornia,

*Berkeley.ThenameoftheUniversitymaynotbeusedto

*endorseorpromoteproductsderivedfromthissoftware

*withoutspecificpriorwrittenpermission.

*THISSOFTWAREISPROVIDED``ASIS''ANDWITHOUTANYEXPRESS

*ORIMPLIEDWARRANTIES,INCLUDING,WITHOUTLIMITATION,THE

*IMPLIEDWARRANTIESOFMERCHANTIBILITYANDFITNESSFORA

*PARTICULARPURPOSE.

*/

Jacobson[Page27]

RFC1144CompressingTCP/IPHeadersFebruary1990

A.1DefinitionsandStateData

#defineMAX_STATES16/*mustbe>2and<255*/

#defineMAX_HDR128/*maxTCP+IPhdrlength(byprotocoldef)*/

/*packettypes*/

#defineTYPE_IP0x40

#defineTYPE_UNCOMPRESSED_TCP0x70

#defineTYPE_COMPRESSED_TCP0x80

#defineTYPE_ERROR0x00/*thisisnotatypethateverappearson

*thewire.Thereceiveframerusesitto

*tellthedecompressortherewasapacket

*transmissionerror.*/

/*Bitsinfirstoctetofcompressedpacket*/

#defineNEW_C0x40/*flagbitsforwhatchangedinapacket*/

#defineNEW_I0x20

#defineTCP_PUSH_BIT0x10

#defineNEW_S0x08

#defineNEW_A0x04

#defineNEW_W0x02

#defineNEW_U0x01

/*reserved,special-casevaluesofabove*/

#defineSPECIAL_I(NEW_SNEW_WNEW_U)/*echoedinteractivetraffic*/

#defineSPECIAL_D(NEW_SNEW_ANEW_WNEW_U)/*unidirectionaldata*/

#defineSPECIALS_MASK(NEW_SNEW_ANEW_WNEW_U)

/*"state"dataforeachactivetcpconversationonthewire.Thisis

*basicallyacopyoftheentireIP/TCPheaderfromthelastpackettogether

*withasmallidentifierthetransmit&receiveendsofthelineuseto

*locatesavedheader.*/

structcstate{

structcstate*cs_next;/*nextmostrecentlyusedcstate(xmitonly)*/

u_shortcs_hlen;/*sizeofhdr(receiveonly)*/

u_charcs_id;/*connection#associatedwiththisstate*/

u_charcs_filler;

union{

charhdr[MAX_HDR];

structipcsu_ip;/*ip/tcphdrfrommostrecentpacket*/

}slcs_u;

};

#definecs_ipslcs_u.csu_ip

Jacobson[Page28]

RFC1144CompressingTCP/IPHeadersFebruary1990

#definecs_hdrslcs_u.csu_hdr

/*

*allthestatedataforoneserialline(weneedoneoftheseperline).

*/

structslcompress{

structcstate*last_cs;/*mostrecentlyusedtstate*/

u_charlast_recv;/*lastrcvdconn.id*/

u_charlast_xmit;/*lastsentconn.id*/

u_shortflags;

structcstatetstate[MAX_STATES];/*xmitconnectionstates*/

structcstaterstate[MAX_STATES];/*receiveconnectionstates*/

};

/*flagvalues*/

#defineSLF_TOSS1/*tossingrcvdframesbecauseofinputerr*/

/*

*Thefollowingmacrosareusedtoencodeanddecodenumbers.Theyall

*assumethat`cp'pointstoabufferwherethenextbyteencoded(decoded)

*istobestored(retrieved).Sincethedecoderoutinesdoarithmetic,

*theyhavetoconvertfromandtonetworkbyteorder.

*/

/*

*ENCODEencodesanumberthatisknowntobenon-zero.ENCODEZchecksfor

*zero(zerohastobeencodedinthelong,3byteform).

*/

#defineENCODE(n){if((u_short)(n)>=256){*cp++=0;cp[1]=(n);cp[0]=(n)>>8;cp+=2;}else{*cp++=(n);}}

#defineENCODEZ(n){if((u_short)(n)>=256(u_short)(n)==0){*cp++=0;cp[1]=(n);cp[0]=(n)>>8;cp+=2;}else{*cp++=(n);}}

/*

*DECODELtakesthe(compressed)changeatbytecpandaddsittothe

Jacobson[Page29]

RFC1144CompressingTCP/IPHeadersFebruary1990

*currentvalueofpacketfield'f'(whichmustbea4-byte(long)integer

*innetworkbyteorder).DECODESdoesthesamefora2-byte(short)field.

*DECODEUtakesthechangeatcpandstuffsitintothe(short)fieldf.

*'cp'isupdatedtopointtothenextfieldinthecompressedheader.

*/

#defineDECODEL(f){if(*cp==0){(f)=htonl(ntohl(f)+((cp[1]<<8)cp[2]));cp+=3;}else{(f)=htonl(ntohl(f)+(u_long)*cp++);}}

#defineDECODES(f){if(*cp==0){(f)=htons(ntohs(f)+((cp[1]<<8)cp[2]));cp+=3;}else{(f)=htons(ntohs(f)+(u_long)*cp++);}}

#defineDECODEU(f){if(*cp==0){(f)=htons((cp[1]<<8)cp[2]);cp+=3;}else{(f)=htons((u_long)*cp++);}}

Jacobson[Page30]

RFC1144CompressingTCP/IPHeadersFebruary1990

A.2压缩

该子程序看起来令人生畏,其实并非如此。代码分为大小大致相等的四块:第一块治理

一个最近最少使用的活动TCP连接循环链表(注46),第二块计算sequence/ack/window/urg

变化量并确定压缩数据包的大致结构(bulk),第三部分处理非凡情形的编码,第四块进行

数据包ID和connectionnumber的编码并且用压缩后的头部代替初始数据包的头部。

该子程序的参数为一个指向待压缩数据包的指针,一个指向串行链路压缩状态结构的指

针,一个答应/禁止对connection进行压缩的标志(即C位)。

压缩是“取代式”(inplace)的,所以每产生一个压缩数据包,输入数据包的开始地址

和长度(m中的off和len域)以反映出初始数据包头部已被移除并被经过压缩的头部所取

代。不管产生压缩数据包还是未压缩数据包,压缩状态结构都要更新。该子程序返回帧传输

器(transitframer)传送的数据包类型(TYPE_IP,TYPE_UNCOMPRESSED_TCP或者

TYPE_COMPRESSED_TCP)。

由于头部各种域中有16位和32位,所以输入IP数据包必须做好对齐(例如,在SPARC

上,IP头部在32位边界对齐)。假如不是这样的话,必须对下面的代码进行彻底修改(先把

输入头部按字节拷贝到某个地方然后再转变可能代价会小一些)。

注重输出数据包可以可按任意方式对齐(也就是说,它可以很轻易的从奇字节边界开始)。

注46:注重对连接表的两个最常见的操作是终止于第一个入口的查找(最近使用连接的一

个新的数据包)以及把表的最后一个入口移到表的头部(新连接来的第一个数据包)。一个

循环表可以有效的处理这两个操作。

Jacobson[Page31]

RFC1144CompressingTCP/IPHeadersFebruary1990

u_char

sl_compress_tcp(m,comp,compress_cid)

structmbuf*m;

structslcompress*comp;

intcompress_cid;

{

registerstructcstate*cs=comp->last_cs->cs_next;

registerstructip*ip=mtod(m,structip*);

registeru_inthlen=ip->ip_hl;

registerstructtcphdr*oth;/*lastTCPheader*/

registerstructtcphdr*th;/*currentTCPheader*/

registeru_intdeltaS,deltaA;/*generalpurposetemporaries*/

registeru_intchanges=0;/*changemask*/

u_charnew_seq[16];/*changesfromlasttocurrent*/

registeru_char*cp=new_seq;

/*BailifthisisanIPfragmentoriftheTCPpacketisn't

*`compressible'(i.e.,ACKisn'tsetorsomeothercontrolbitis

*set).(Weassumethatthecallerhasalreadymadesurethepacket

*isIPprotoTCP).*/

if((ip->ip_off&htons(0x3fff))m->m_len<40)

return(TYPE_IP);

th=(structtcphdr*)&((int*)ip)[hlen];

if((th->th_flags&(TH_SYNTH_FINTH_RSTTH_ACK))!=TH_ACK)

return(TYPE_IP);

/*Packetiscompressible--we'regoingtosendeithera

*COMPRESSED_TCPorUNCOMPRESSED_TCPpacket.Eitherwayweneedto

*locate(orcreate)theconnectionstate.Specialcasethemost

*recentlyusedconnectionsinceit'smostlikelytobeusedagain&

*wedon'thavetodoanyreorderingifit'sused.*/

if(ip->ip_src.s_addr!=cs->cs_ip.ip_src.s_addr

ip->ip_dst.s_addr!=cs->cs_ip.ip_dst.s_addr

*(int*)th!=((int*)&cs->cs_ip)[cs->cs_ip.ip_hl]){

/*Wasn'tthefirst--searchforit.

*Statesarekeptinacircularlylinkedlistwithlast_cs

*pointingtotheendofthelist.Thelistiskeptinlru

*orderbymovingastatetotheheadofthelistwhenever

*itisreferenced.Sincethelistisshortand,

*empirically,theconnectionwewantisalmostalwaysnear

*thefront,welocatestatesvialinearsearch.Ifwe

*don'tfindastateforthedatagram,theoldeststateis

*(re-)used.*/

registerstructcstate*lcs;

registerstructcstate*lastcs=comp->last_cs;

do{

lcs=cs;

cs=cs->cs_next;

if(ip->ip_src.s_addr==cs->cs_ip.ip_src.s_addr

&&ip->ip_dst.s_addr==cs->cs_ip.ip_dst.s_addr

&&*(int*)th==((int*)&cs->cs_ip)[cs->cs_ip.ip_hl])

gotofound;

Jacobson[Page32]

RFC1144CompressingTCP/IPHeadersFebruary1990

}while(cs!=lastcs);

/*Didn'tfindit--re-useoldestcstate.Sendan

*uncompressedpacketthattellstheothersidewhat

*connectionnumberwe'reusingforthisconversation.Note

*thatsincethestatelistiscircular,theoldeststate

*pointstothenewestandweonlyneedtosetlast_csto

*updatethelrulinkage.*/

comp->last_cs=lcs;

hlen+=th->th_off;

hlen<<=2;

gotouncompressed;

found:

/*Foundit--movetothefrontontheconnectionlist.*/

if(lastcs==cs)

comp->last_cs=lcs;

else{

lcs->cs_next=cs->cs_next;

cs->cs_next=lastcs->cs_next;

lastcs->cs_next=cs;

}

}

/*

*Makesurethatonlywhatweexpecttochangechanged.Thefirst

*lineofthe`if'checkstheIPprotocolversion,headerlength&

*typeofservice.The2ndlinechecksthe"Don'tfragment"bit.

*The3rdlinechecksthetime-to-liveandprotocol(theprotocol

*checkisunnecessarybutcostless).The4thlinecheckstheTCP

*headerlength.The5thlinechecksIPoptions,ifany.The6th

*linechecksTCPoptions,ifany.Ifanyofthesethingsare

*differentbetweentheprevious&currentdatagram,wesendthe

*currentdatagram`uncompressed'.

*/

oth=(structtcphdr*)&((int*)&cs->cs_ip)[hlen];

deltaS=hlen;

hlen+=th->th_off;

hlen<<=2;

if(((u_short*)ip)[0]!=((u_short*)&cs->cs_ip)[0]

((u_short*)ip)[3]!=((u_short*)&cs->cs_ip)[3]

((u_short*)ip)[4]!=((u_short*)&cs->cs_ip)[4]

th->th_off!=oth->th_off

(deltaS>5&&BCMP(ip+1,&cs->cs_ip+1,(deltaS-5)<<2))

(th->th_off>5&&BCMP(th+1,oth+1,(th->th_off-5)<<2)))

gotouncompressed;

/*

*Figureoutwhichofthechangingfieldschanged.Thereceiver

Jacobson[Page33]

RFC1144CompressingTCP/IPHeadersFebruary1990

*expectschangesintheorder:urgent,window,ack,seq.

*/

if(th->th_flags&TH_URG){

deltaS=ntohs(th->th_urp);

ENCODEZ(deltaS);

changes=NEW_U;

}elseif(th->th_urp!=oth->th_urp)

/*

*argh!URGnotsetbuturpchanged--asensible

*implementationshouldneverdothisbutRFC793doesn't

*prohibitthechangesowehavetodealwithit.

*/

gotouncompressed;

if(deltaS=(u_short)(ntohs(th->th_win)-ntohs(oth->th_win))){

ENCODE(deltaS);

changes=NEW_W;

}

if(deltaA=ntohl(th->th_ack)-ntohl(oth->th_ack)){

if(deltaA>0xffff)

gotouncompressed;

ENCODE(deltaA);

changes=NEW_A;

}

if(deltaS=ntohl(th->th_seq)-ntohl(oth->th_seq)){

if(deltaS>0xffff)

gotouncompressed;

ENCODE(deltaS);

changes=NEW_S;

}

/*

*Lookforthespecial-caseencodings.

*/

switch(changes){

case0:

/*

*Nothingchanged.Ifthispacketcontainsdataandthelast

*onedidn't,thisisprobablyadatapacketfollowingan

*ack(normalonaninteractiveconnection)andwesendit

*compressed.Otherwiseit'sprobablyaretransmit,

*retransmittedackorwindowprobe.Sendituncompressed

*incasetheothersidemissedthecompressedversion.

*/

if(ip->ip_len!=cs->cs_ip.ip_len&&

ntohs(cs->cs_ip.ip_len)==hlen)

break;

/*(fallthrough)*/

caseSPECIAL_I:

Jacobson[Page34]

RFC1144CompressingTCP/IPHeadersFebruary1990

caseSPECIAL_D:

/*

*Actualchangesmatchoneofourspecialcaseencodings--

*sendpacketuncompressed.

*/

gotouncompressed;

caseNEW_SNEW_A:

if(deltaS==deltaA&&

deltaS==ntohs(cs->cs_ip.ip_len)-hlen){

/*specialcaseforechoedterminaltraffic*/

changes=SPECIAL_I;

cp=new_seq;

}

break;

caseNEW_S:

if(deltaS==ntohs(cs->cs_ip.ip_len)-hlen){

/*specialcasefordataxfer*/

changes=SPECIAL_D;

cp=new_seq;

}

break;

}

deltaS=ntohs(ip->ip_id)-ntohs(cs->cs_ip.ip_id);

if(deltaS!=1){

ENCODEZ(deltaS);

changes=NEW_I;

}

if(th->th_flags&TH_PUSH)

changes=TCP_PUSH_BIT;

/*

*GraBThecksumbeforeweoverwriteitbelow.Thenupdateour

*statewiththispacket'sheader.

*/

deltaA=ntohs(th->th_sum);

BCOPY(ip,&cs->cs_ip,hlen);

/*

*Wewanttousetheoriginalpacketasourcompressedpacket.(cp-

*new_seq)isthenumberofbytesweneedforcompressedsequence

*numbers.Inadditionweneedonebyteforthechangemask,one

*fortheconnectionidandtwoforthetcpchecksum.So,(cp-

*new_seq)+4bytesofheaderareneeded.hlenishowmanybytes

*oftheoriginalpackettotosssosubtractthetwotogetthenew

*packetsize.

*/

deltaS=cp-new_seq;

cp=(u_char*)ip;

if(compress_cid==0comp->last_xmit!=cs->cs_id){

comp->last_xmit=cs->cs_id;

Jacobson[Page35]

RFC1144CompressingTCP/IPHeadersFebruary1990

hlen-=deltaS+4;

cp+=hlen;

*cp++=changesNEW_C;

*cp++=cs->cs_id;

}else{

hlen-=deltaS+3;

cp+=hlen;

*cp++=changes;

}

m->m_len-=hlen;

m->m_off+=hlen;

*cp++=deltaA>>8;

*cp++=deltaA;

BCOPY(new_seq,cp,deltaS);

return(TYPE_COMPRESSED_TCP);

uncompressed:

/*

*Updateconnectionstatecs&senduncompressedpacket

*('uncompressed'meansaregularip/tcppacketbutwiththe

*'conversationid'wehopetouseonfuturecompressedpacketsin

*theprotocolfield).

*/

BCOPY(ip,&cs->cs_ip,hlen);

ip->ip_p=cs->cs_id;

comp->last_xmit=cs->cs_id;

return(TYPE_UNCOMPRESSED_TCP);

}

Jacobson[Page36]

RFC1144CompressingTCP/IPHeadersFebruary1990

A.3解压缩

该子程序对一个收到的数据包进行解压。调用参数是一个指向待解压数据包的指针,数

据包的长度和类型,以及一个指向输入串行线路的压缩状态结构的指针。假如正确则返回指

向结果数据包的指针,假如输入数据包中有错误,返回0,假如数据包类型为COMPRESSED_TCP

或UNCOMPRESSED_TCP,压缩状态将被更新。

新的数据包“取代式”地构建得到。这意味着在bufp前必须由128字节的空间以重新构

建IP和TCP头部。重新构建后的数据包将在32位边界处对齐。

u_char*

sl_uncompress_tcp(bufp,len,type,comp)

u_char*bufp;

intlen;

u_inttype;

structslcompress*comp;

{

registeru_char*cp;

registeru_inthlen,changes;

registerstructtcphdr*th;

registerstructcstate*cs;

registerstructip*ip;

switch(type){

caseTYPE_ERROR:

default:

gotobad;

caseTYPE_IP:

return(bufp);

caseTYPE_UNCOMPRESSED_TCP:

/*

*Locatethesavedstateforthisconnection.Ifthestate

*indexislegal,clearthe'discard'flag.

*/

ip=(structip*)bufp;

if(ip->ip_p>=MAX_STATES)

gotobad;

cs=&comp->rstate[comp->last_recv=ip->ip_p];

comp->flags&=~SLF_TOSS;

/*

*RestoretheIPprotocolfieldthensaveacopyofthis

*packetheader.(Thechecksumiszeroedinthecopysowe

*don'thavetozeroiteachtimeweprocessacompressed

Jacobson[Page37]

RFC1144CompressingTCP/IPHeadersFebruary1990

*packet.

*/

ip->ip_p=IPPROTO_TCP;

hlen=ip->ip_hl;

hlen+=((structtcphdr*)&((int*)ip)[hlen])->th_off;

hlen<<=2;

BCOPY(ip,&cs->cs_ip,hlen);

cs->cs_ip.ip_sum=0;

cs->cs_hlen=hlen;

return(bufp);

caseTYPE_COMPRESSED_TCP:

break;

}

/*We'vegotacompressedpacket.*/

cp=bufp;

changes=*cp++;

if(changes&NEW_C){

/*

*Makesurethestateindexisinrange,thengrabthe

*state.Ifwehaveagoodstateindex,clearthe'discard'

*flag.

*/

if(*cp>=MAX_STATES)

gotobad;

comp->flags&=~SLF_TOSS;

comp->last_recv=*cp++;

}else{

/*

*Thispackethasanimplicitstateindex.Ifwe'vehada

*lineerrorsincethelasttimewegotanexplicitstate

*index,wehavetotossthepacket.

*/

if(comp->flags&SLF_TOSS)

return((u_char*)0);

}

/*

*FindthestatethenfillintheTCPchecksumandPUSHbit.

*/

cs=&comp->rstate[comp->last_recv];

hlen=cs->cs_ip.ip_hl<<2;

th=(structtcphdr*)&((u_char*)&cs->cs_ip)[hlen];

th->th_sum=htons((*cp<<8)cp[1]);

cp+=2;

if(changes&TCP_PUSH_BIT)

th->th_flags=TH_PUSH;

else

th->th_flags&=~TH_PUSH;

/*

Jacobson[Page38]

RFC1144CompressingTCP/IPHeadersFebruary1990

*Fixupthestate'sack,seq,urgandwinfieldsbasedonthe

*changemask.

*/

switch(changes&SPECIALS_MASK){

caseSPECIAL_I:

{

registeru_inti=ntohs(cs->cs_ip.ip_len)-cs->cs_hlen;

th->th_ack=htonl(ntohl(th->th_ack)+i);

th->th_seq=htonl(ntohl(th->th_seq)+i);

}

break;

caseSPECIAL_D:

th->th_seq=htonl(ntohl(th->th_seq)+ntohs(cs->cs_ip.ip_len)

-cs->cs_hlen);

break;

default:

if(changes&NEW_U){

th->th_flags=TH_URG;

DECODEU(th->th_urp)

}else

th->th_flags&=~TH_URG;

if(changes&NEW_W)

DECODES(th->th_win)

if(changes&NEW_A)

DECODEL(th->th_ack)

if(changes&NEW_S)

DECODEL(th->th_seq)

break;

}

/*UpdatetheIPID*/

if(changes&NEW_I)

DECODES(cs->cs_ip.ip_id)

else

cs->cs_ip.ip_id=htons(ntohs(cs->cs_ip.ip_id)+1);

/*

*Atthispoint,cppointstothefirstbyteofdatainthepacket.

*Ifwe'renotalignedona4-byteboundary,copythedatadownso

*theIP&TCPheaderswillbealigned.Thenbackupcpbythe

*TCP/IPheaderlengthtomakeroomforthereconstructedheader(we

*assumethepacketwewerehandedhasenoughspacetoprepend128

*bytesofheader).Adjustthelenthtoaccountforthenewheader

*&fillintheIPtotallength.

*/

len-=(cp-bufp);

if(len<0)

/*

*wemusthavedroppedsomecharacters(crcshoulddetect

*thisbuttheoldslipframingwon't)

Jacobson[Page39]

RFC1144CompressingTCP/IPHeadersFebruary1990

*/

gotobad;

if((int)cp&3){

if(len>0)

OVBCOPY(cp,(int)cp&~3,len);

cp=(u_char*)((int)cp&~3);

}

cp-=cs->cs_hlen;

len+=cs->cs_hlen;

cs->cs_ip.ip_len=htons(len);

BCOPY(&cs->cs_ip,cp,cs->cs_hlen);

/*recomputetheipheaderchecksum*/

{

registeru_short*bp=(u_short*)cp;

for(changes=0;hlen>0;hlen-=2)

changes+=*bp++;

changes=(changes&0xffff)+(changes>>16);

changes=(changes&0xffff)+(changes>>16);

((structip*)cp)->ip_sum=~changes;

}

return(cp);

bad:

comp->flags=SLF_TOSS;

return((u_char*)0);

}

Jacobson[Page40]

RFC1144CompressingTCP/IPHeadersFebruary1990

A.4初始化

本子程序对某条串行链路的传输方和接收方的状态结构都进行初始化。它在每一条

链路建立时被调用。

void

sl_compress_init(comp)

structslcompress*comp;

{

registeru_inti;

registerstructcstate*tstate=comp->tstate;

/*

*Cleanoutanyjunkleftfromthelasttimelinewasused.

*/

bzero((char*)comp,sizeof(*comp));

/*

*Linkthetransmitstatesintoacircularlist.

*/

for(i=MAX_STATES-1;i>0;--i){

tstate[i].cs_id=i;

tstate[i].cs_next=&tstate[i-1];

}

tstate[0].cs_next=&tstate[MAX_STATES-1];

tstate[0].cs_id=0;

comp->last_cs=&tstate[0];

/*

*Makesurewedon'taccidentallydoCIDcompression

*(assumesMAX_STATES<255).

*/

comp->last_recv=255;

comp->last_xmit=255;

}

A.5BerkeleyUnix的依靠关系

注重:假如在你想把该例子代码运行于不是4BSD(BerkleyUnix)的系统上时才有用。

这里的代码使用了规范的BerkeleyUnix头文件(位于/usr/include/netinet)来定义IP

和TCP头部。结构标志(structuretags)遵循RFC的协议,即使你没有访问4BSD系统,

也应该是能看懂(注47)。

注48.假如不能看懂,这些头文件(和所有的Berkeley网络代码)可以通过匿名ftp从主

机ucbarpa.berkeley.edu获取,文件分别为pub/4.3/tcp.tar以及pub/4.3/inet.tar.

Jacobson[Page41]

RFC1144CompressingTCP/IPHeadersFebruary1990

宏BCOPY(src,dst,amt)调用来从src拷贝amt个字节到dst。在BSD中,它被转化为

bcopy调用。假如你不幸运行于System-VUnix环境下,它将被转化为memcpy调用。宏

OVBCOPY(src,dst,amt)用来在当src和dst覆盖(overlap)时的拷贝(也就是说,在做

4-字节对齐的拷贝时)。在BSD内核中,它转化为一个ovbcopy调用。因为AT&T修补了

memcpy的定义,可能应该转化为一个System-V下的copy循环。

宏BCMP(src,dst,amt)调用来比较src和dst的amt个字节是否相等。在BSD中,它被

转化为bcmp调用。在System-V中,它被转化为memcmp调用,你也可以自己写一个子程序

来完成这些比较。子程序在src和dst所有的字节都相等时返回0,否则返回非0值。

子程序ntohl(dat)把(4个字节)的long数据从网络字节序(networkbyteorder)

转化为主机字节序(hostbyteorder)。在一个合理的cpu中,这可能是“什么都不做的”

宏定义:

#definentohl(dat)(dat)

在一个Vax或者IBMPC(或者任何Intel字节序的系统)中,你将必须定义一个宏或者子

程序来重新安排这些字节。

子程序ntohs(dat)与ntohl相似,但是转化的是(2个字节的)short而不是long。子程

序htonl(dat)和htons(dat)完成long和short的相反的转换(主机字节序到网络字节序)。

结构mbuf在调用sl_compress_tcp时使用,因为该子程序在假如输入数据包是压缩数

据包时需要修改startaddress和length。在BSD中,mbuf是内核的缓冲治理结构(buffer

managementstructure)。假如是其它的系统,下面的定义应该足够了:

structmbuf{

u_char*m_off;/*pointertostartofdata*/

intm_len;/*lengthofdata*/

};

#definemtod(m,t)((t)(m->m_off))

Jacobson[Page42]

RFC1144CompressingTCP/IPHeadersFebruary1990

附录B与过去错误的兼容性

在与现代的PPP串行链路协议(参考文献[9])联合使用时,头部压缩自动进行对用户

来说是不可见的。不幸的是,很多站点还有使用参考文献[12]中定义的SLIP的用户,该协议

没有顾及到不同的协议类型来区分头部经过压缩的数据包和IP数据包,也没有顾及到版本

号,也没有顾及到用来自动协商头部压缩的选项。

作者已经使用下面的技巧来答应头部经过压缩的SLIP与现存的服务器和客户端实现互

操作。注重这是用来与过去错误兼容的手段,思维正确(rightthinking)的人应该把它视

为具有攻击性的。在这里提供出来只是为了减小在运行SLIP时用户等待vendors释放PPP

的痛苦。

B.1没有“type”字节照样存活

选用A.1中希奇的数据包类型号是为了答应在不想或者不可能增加一个显式type

字节时链路上发送数据包的类型。注重IP数据包的第一个字节的头4位总是包含“4”(即

IP协议版本号)。同时压缩数据包头部第一个字节的最高有效位(themostsignificant

bit)总是被忽略。使用A.1中的数据包类型,则type可以使用下面的代码编码到输出数据

包的那几个最高有效位中:

p->dat[0]=sl_compress_tcp(p,comp);

在接收方的解码为

if(p->dat[0]&0x80)

type=TYPE_COMPRESSED_TCP;

elseif(p->dat[0]>=0x70){

type=TYPE_UNCOMPRESSED_TCP;

p->dat[0]&=~0x30;

}else

type=TYPE_IP;

status=sl_uncompress_tcp(p,type,comp);

B.2向后兼容SLIP服务器

参考文献[12]中描述的SLIP不包括能用来自动协商头部压缩的机制。答应这种SLIP

的用户使用头部压缩是件好事,但是,假如这两种类型的SLIP用户使用同一个服务器,手

动配置每一个连接的两端使之能够使用本压缩将是一件很烦人的事情。下面的过程用来避免

手动配置。

因为有两种类型的拨号客户端(dial-inclients)即使用压缩的和不使用压缩的,而同

一个服务器要为这两种类型的客户端服务。很明显服务器将要重新配置每一个新的客户端会

话,但客户端就是更改也很少改变配置。假如必须手动配置,它应该在不经常改动的一方进

行——即客户端。这就暗示着服务器应该以某种方式从客户端得知是否使用头部压缩。假设

由于对称性(也就是说,假如使用则应该在两个方向上都使用压缩)服务器可以通过来自客

户端的压缩数据包来暗示着它可以向客户端发送压缩数据包。这就导致了下面的算法:

每一条链路用两位来控制头部压缩:allowed和on。假如设置了on位,发送的是压缩数

据包,否则不发送压缩数据包。假如设置了allowed位,可以接收压缩数据包,假如收到的

UNCOMPRESSED_TCP数据包没有设置on位,则设置on位(注49)。假如收到压缩数据包并且

没有设置allowed位,数据包将被忽略。

客户端配置为两位都设置(假如设置了on则总是设置allowed),服务器开始每一个会话

时设置allowed位,清除on位。客户端来的第一个压缩数据包(一定是一个

UNCOMPRESSED_TCP数据包)将为服务器打开压缩功能。

注49:因为参考文献[12]中的帧不包括错误检测,必须注重千万别把服务器的压缩开关设

置为false。在压缩被使能之前,UNCOMPRESSED_TCP数据包应该检查连续性(例如,IP检验

和的正确性)。COMPRESSED_TCP数据包的到达不应该用来使能压缩。

Jacobson[Page44]

RFC1144CompressingTCP/IPHeadersFebruary1990

C更主动的压缩

如3.2.2中指出的那样,压缩头部中存在很轻易检测到的特征,表明可以进行进一步

的压缩。这有价值么?

压缩后的数据包的头部仅有7个比特(注50)。帧必须至少为一个比特(已表明“type”),

更可能的情况是2到3个字节。在大多数有趣的场合,将至少有一个字节的数据。最后,端

到端的检查——TCPchecksum——必须未加修改地加以传递(注51)。

帧,数据和checksum将保留即使头部完全地压缩,所以数据包的平均大小最多从4个字

节降到三个字节加一个比特——大约把延迟性能提高25%(注52)。这个提高可能看起来很

大,在一条2400bps的链路上,它意味着击键回显的响应时间为25毫秒而不是29毫秒。

在当前人类进化阶段,这个差别根本无法感觉出来(detectable)。

但是,作者坦率地承认把该压缩方案曲解为一种非常非凡情形数据获取问题:我们有漂

浮与200KV之上的工具和控制包,通过遥测系统与地面通信。由于很多原因(多路复用通信,

流水线操作,错误恢复,精心检测过的实现的可用性等等),跟这个包使用TCP/IP通信是很方

便的。但是,因为遥感链路的基本用处就是获取数据,设计成为上传信道<下传信道容量的

0.5%。为了满足应用延迟性要求,数据包为100个字节,并且,因为TCP每隔一个数据包确

认一次,相关的上传信道的确认(ack)带宽为a/200,其中a为确认数据包的总长度。假如

使用本文档的方案,最小的ack为4个字节,意味着上传链路的带宽未下传链路带宽的2%。

注50:已经对几百万个固定流量负载的数据包进行测试(也就是说,统计了以年之内从我家

到工作地之间的流量)表明80%的数据包使用两种非凡情形编码之一,这样,唯一的头部就是

changemask。

注51.假如某人试图向你出售压缩TCP检验和的方案,一定不要买。(Justsay“no”)。某

些可怜的傻瓜仍然要继续伤心的经历来展示端到端的讨论是真理。更坏的是,因为这个傻瓜

正在捣乱你的端到端的错误检查,你可能为这个教训付出学费,他们一点也不聪明。得到两

个字节时间的延迟但丢失内心的平静有什么好处?

注52.再次注重在讨论时我们必须关心交互延迟时间:批量数据传输的性能主要由发送数

据的时间决定,包含成百上千个字节数据的数据报文中三个字节和四个字节头部的差异实际

上没有差别。

Jacobson[Page45]

RFC1144CompressingTCP/IPHeadersFebruary1990

这是不可能的,所以我们使用注15中描述的方案:假如帧中第一个比特位为1,意味着压缩

数据包头部与上一次的相同。否则接下去的两个比特位将给出3.2中描述的类型之一。因为

链路有很多转发错误(forwarderror),流量仅做一次跳跃(hop),TCPchecksum已经被

相同头部的数据包类型压缩出去(惭愧!)(注53),所以这些数据包的头部总长度就是一个

比特。在几个月的操作中,超个99%的40个字节的TCP/IP头部都压缩降为一个比特(注54)。

D安全方面的考虑

本文档不讨论安全方面的问题。

E作者地址

Address:VanJacobson

RealTimeSystemsGroup

MailStop46A

LawrenceBerkeleyLaboratory

Berkeley,CA94720

Phone:Useemail(authorignoreshisphone)

EMail:van@helios.ee.lbl.gov

注53:检验和在解压器方已经重新产生,当然,“丢弃”(“toss”)逻辑被设计成更加主动以

防止错误的传播。

注54:我们已经听到建议,认为出于实时的需要要求放弃TCP/IP而赞成使用具有更小头部

的轻权协议(light-weight'protocol)。很难想象头部平均只有一个比特的协议。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有