网络软件设计--数据的传输1、整数:字节序和网络字节序2、字符串3、文件4、数据块:PDU设计5、传输方式:字节流或数据报制作主讲段景山TCP/IP网络编程——数据的传输1、整数:网络字节序制作主讲段景山段景山数据传输程序中常用的数据传输方法字符串文件——二进制流整数、数据结构与报文格式3核心问题:发送与接收缓冲区的组织段景山4流程中的片断填写端点地址结构structsockaddr_inaddr;……addr.sin_addr.S_un.S_addr=htonl(0x0a010203);addr.sin_port=htons(0x1234);……为什么要使用htonl,htons?怎样在两个进程之间传递整数?段景山5字节序问题的由来关于整数的表达、存储和传输表达十进制:4660十六进制:0x1234或1234H二进制:(0001001000110100)2整数表达中高字节在左边,低字节在右边高字节低字节段景山6字节序问题的由来整数的存储0x1234。。。。。。0100101低地址高地址。。。。。。0100101。。。0x340x12。。。01001010x120x34littleendianbigendianMotoIntel实验!段景山7字节序问题的由来传输!都是从缓冲区的低地址开始,逐个字节发送,或逐个字节接收无论软件设计,还是硬件设计,都遵循这个原则发送算法char*cp;cp=buffer;while(!empty){phy_send(*cp);cp++;}接收算法char*cp,ch;cp=buf;while(!end){phy_recv(ch);*(cp++)=ch;}。。。。。。0x120x340x120x340buffer段景山8字节序问题的由来问题出现了!发送的数据是:接收的数据是:。。。。。。。。。。。。0x120x340x120x3400bufferMotoIntelbuf段景山9字节序问题的解决方案1方案一、在发送整数时,如果可以根据对方系统类型调整整数的发送顺序。。。。。。。。。。。。0x120x340x120x3400littleendianbigendian方案二、在接收的时候,根据系统类型存放整数。。。。。。。。。。。。0x120x340x120x3400即改变发送程序的读取顺序即改变接收程序的存放顺序传输的顺序会变化段景山10字节序问题的解决方案2方案三、规定传输的顺序互联网协议规定在传输整数时应该先传高字节(高位)--网络字节序规定传输顺序等于规定了缓冲区数据的放置顺序传输程序操作缓冲区时,都是从低地址处理到高地址(发送接收同理)发送char*cp;cp=buffer;while(!empty){link_send(*cp);cp++;}接收char*cp,ch;cp=buffer;while(!empty){link_recv(ch);*(cp++)=ch;}段景山11字节序问题的解决方案3方案三:规定传输的顺序即规定用户程序向缓冲区中放置和取出数据的顺序--高字节在前用户程序、传输程序、缓冲区。。。。。。0用户程序缓冲区网络传输程序(如TCP实体)系统段景山12字节序问题的解决方案4方案三、规定传输的顺序--网络字节序对Bigendian系统(MOTO)放置和取出的顺序不用改变对Littleendian系统(Intel)放置和取出的顺序必须和普通情况下不同--颠倒。。。0x120x34。。。。。。0x120x34。。。BigEndianLittleEndian0x120x34网络序主机序网络序主机序段景山13字节序问题解决原则小结尽量少地对系统进行改变传输程序属于系统程序(TCP/IP协议栈)改变都增加在用户一侧改变传输时取放缓冲区的顺序--系统的改变改变传输前/后用户取放缓冲区的顺序--用户的改变。。。。。。0用户程序网络传输程序段景山14字节序问题的解决用户将数据放置到缓冲区准备传输时:char*cp;x=0x1234;cp=buffer;*cp=(char)(x8);*(cp+1)=(char)x;用户从接收缓冲区读取数据以使用时:char*cp;intx;cp=buffer;x=*cp;x=x8;x+=*(cp+1)(int*)cp=x;htons(x);x=*(int*)cp;ntohs(*(int*)cp);htonl();处理长整数ntohl();处理长整数段景山15字节序的使用规定1、由系统提供htonx(),ntohx()的实现2、网络软件在任何时候需要向数据缓冲内存放(读取)整数时必须使用以上函数3、不同的系统有不同的htonx(),ntohx()的实现方法4、在以上基础上,用户网络软件不需再考虑字节序的不一致和程序移植问题段景山16例1、填入端点IP地址和端口号structsockaddr_inaddr;addr.sin_addr.S_un.S_addr=htonl(0xca730c02);addr.sin_port=htons(0x1234);connect(s,&addr,len);202.115.12.22、从端点结构中读出IP地址和端口号structsockaddr_inremote;unsignedlongip_addr;intport;recvfrom(s,buf,len,0,&remote,&remote_len);ip_addr=ntohl(remote.sin_addr.S_un.S_addr);port=ntohs(remote.sin_port);printf(“IP=%lx,port=%u“,ip_addr,port);段景山17点分十进制地址的使用1ip地址本质为四个字节的长整数从程序的角度理解为方便程序员的使用,ip地址采用点分十进制的表达方式计算机程序无法正确理解点分十进制表达方式因为点分十进制其实是个字符串202.115.12.20xca730c02段景山18点分十进制地址的使用2将点分十进制ip地址填入端点地址结构时,需要转换为长整数inet_addr(…);实现点分十进制转换为整数的功能从端点地址结构取出ip地址显示给用户看的时候要转换为字符串inet_ntoa(…);实现长整数转换为点分十进制字符串的功能以上两个函数由系统调用用户不用自己动手去变换长整数为网络字节序段景山19点分十进制地址的使用(例)unsignedlonginet_addr(constantchar*cp);char*inet_ntoa(IN_ADDRin);1、填写端点地址结构structsockaddr_inaddr;addr.sin_addr.S_un.S_addr=inet_addr(“202.115.12.2”);2、显示ip地址structsockaddr_inaddr;char*ipa_addr;ipa_addr=inet_ntoa(addr.sin_addr.S_un.S_addr);printf(“%s“,ipa_addr);实验!段景山20传输整数、数据传输整数和数据:网络字节序!htons(),htonl(),ntohs(),ntohl()问题:如果接收方事先不知道整数的长度怎么办?可以先传一个长度信息,然后再传整数新问题:长度信息也是一个整数!x=htonl(y);send(s,&x,sizeof(x),0);recv(s,&x,sizeof(x),0);y=ntohl(x);段景山21传输整数、数据可借鉴ASN--抽象语法标记ASN:描述了一种对数据进行表示、编码、传输和解码的数据格式。--作为表示层协议BER编码基本思想:每一个数据由三部分组成:类型,长度,值各种数据均可按照这种方式表示一字节整数0x10x020x010x1两字节整数0x12340x020x020x120x34字符串:“Iamaclient”0x040x0d0x490x200x610x6d…类型Tag类型TagSEQUENCE0x30IPADDR0x40INT0x02COUNTER0x41OCTSTR0x04GAUGE0x42NULL0x05TIMETICKS0x43OBUID0x06TCP/IP网络编程——数据的传输2、字符串制作主讲段景山段景山23引入观察例程中对收发数据的处理方法char*sendbuf=“Iamaclient”;charrecvbuf[128];问题:上述方法有什么区别和共同的地方?区别:定义不同共同点:字符串形式,占用一定空间段景山24传输字符串申明及空间char*buf;没有空间,若直接用于传输将导致错误获得空间的方法:静态buf=“Iamaclient”;charbuf[128];动态buf=(char*)malloc(size);//要注意及时的释放free段景山25传输字符串传输长度字符串的结束符为‘\0’,实际值为0strlen(buf);取得字符串长度不包含结束符,所以传输时应将长度+1否则接收方如果直接将接收信息当作字符串处理,会因为没有结束符而导致错误还需注意一些其它的特殊字符:‘\r’,’\b’等特殊字符会对字符串操作,如strlen()、strcpy()、strcmp()等带来意想不到的效果send(s,sendbuf,strlen(buf)+1,0)段景山26传输字符串字符串缓冲区内容的组织静态char*sendbuf=“Iamaclient”;动态从键盘获得scanf(“%s”,sendbuf);gets()等sendbuf一定要准备足够的缓冲区。从其它地方复制strcpy(sendbuf,src);memcpy(sendbuf,src,len);TCP/IP网络编程——数据的传输3、文件制作主讲段景山段景山28传输文件文件类型文本文件流式文件文本文件处理的基本函数fopen();fclose()--打开、关闭文件fgets();fprintf()--读、写文件fseek()--移动文件读写指针段景山29传输文件文本文件操作例:FILE*Fidr,*Fidw;Fidr=fopen(“c:\test\mytestr.c”,“r+t”);Fidw=fopen(“c:\test\mytestw.c”,“a+t”);……fgets(Fidr,recvbuf);fprintf(Fidw,“Iamaclient”);……fclose(Fidr);fclose(Fidw)文本文件是多个字符串的集合,文本文件的结束符是EOF每次读写文件都是接着上一次读写结束的位置;是从文件读写指针处开始,而每次读写动作都会移动这个读写指针。段景山30传输文件流式文件处理的基本函数fread();fwrite();流式文件操作例Fid=fopen(“c:\test\mytest.c”,“a+b”);……fread(recvbuf,size,Fid);fwrite(sendbuf,size,Fid);……fclose(Fid);流式文件是没有特殊操作符的,读写操作也是以大小为限段景山31传输文件记录式、字符型文件的传输与处理字符串传输类似例:fgets(Fid,sendbuf);send(s,sendbuf,strlen(sendbuf)+1,0);……retval=recv(s,recvbuf,len,0);if(retval=len){fprintf(Fid,recvbuf);}else{……}段景山32传输文件流式文件传输不受特殊字符的干扰,长度参数是很重要的信息例:len=fread(sendbuf,size,Fid);send(s,sendbuf,len,0);……len=recv(s,recvbuf,size,0);fwrite(