11.数据结构的传输和XDR标准

更新时间:2023-12-01 06:12:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

十一 数据结构的传输和XDR标准

第11章 数据结构的传输和XDR标准

在复杂的网络应用中,在客户进程和服务器进程间可能需要传递一些复杂的数据结构,这些数据结构可能用于控制进程的动作或者返回进程处理的结果。

本章首先说明在网络中传送数据结构中,存在的问题。比如各种数据类型的不同处理、指针的传递等等。接着我们示例一个使用我们自己定义的简单规则进行数据结构传送的程序片断。

然后我们将说明在数据结构传送中实际上被使用的标准XDR(extern data representation),外部数据表示。我们介绍了XDR转换库函数的使用。

11.1 数据结构的传送

我们在UDP数据报一章曾经演示了一个视频点播的模拟,在模拟中,我们使用了非常简单的控制命令,而实际的系统中,可能需要传递更多的信息。比如在客户进程和服务器进程连接TCP连接后,用户必须输入自身的用户标识和用户密码等等身份确认信息,然后通过TCP连接将这些信息发送给服务器端,在服务器进程根据这些信息对用户的身份进行确认后,才能允许用户进入系统,使用系统中的各种资源。

因而,对于较复杂的网络应用,我们常常需要复杂的数据结构,而不是简单的几个字节。

11.1.1 数据结构传送的问题

下面,我们将说明在数据结构传递中可能存在的问题。 1. 网络字序的问题

不同类型的计算机对于数据的存储格式可能不同,这将导致它们对相同整数(或其他类型数)的2进制序列理解不同,这个问题在以前我们已经详细说明了。 2. 浮点数的传递

整数部分比特小数部分比特整数部分比特小数部分比特整数a整数b整数a整数b图11.1 将浮点数的小数部分和整数部分分别传递

1

Linux网络编程

浮点数的传递比整数更加困难,通常浮点数使用若干比特表示整数部分,其他比特表示小数部分。不同类型的浮点数float和double,它们使用的比特数不相同,这使得在网络中传递它们有一定的困难。

对于浮点数的处理,可以将浮点数用字符串的形式进行传递,在接收端再将浮点数字符串转化成浮点数。

或者我们可以将浮点数的小数点前后的两个部分分别看成两个整数,分别进行传递。

3.14159263.1415926图11.2 将浮点数作为字符串传递

3. 指针的处理

在数据结构传递中,最为困难的是指针,因为指针的含义是本机上存放某个数据的地址,这个地址在远端的主机没有任何的意义。所以我们必须传递的是指针的内容而不是指针本身。比如一个字符串指针,我们需要将字符串的内容包含在数据内容中,同时还需要包含字符串的长度信息。 11.1.2 简单的示例

1. 应用说明

我们将演示在笔者设计的视频点播系统中一些数据结构的传递。

我们将定义自己的数据包格式。在每个数据包的最前面有一个数据包头,对于简单的消息(比如一些简单的播控命令stop),只需要这个数据包头就可以了,对于需要复杂的消息(比如连接请求中需要包含用户名和用户的密码,用于身份的认证。),还需要在包头后面加上实际的数据。包头的定义如下: typedef struct{

uint16_t uint8_t uint16_t uint16_t uint32_t

packetlen; cmdtype; sessionid; messageid; ackinfo;

}ControlHeader;

packetlen指明这个数据包的总长度,cmdtype指明本数据包的命令类型,sessionid用

于说明这个数据包属于哪个会话过程(我们将客户进程同服务器进程进行交互的整个过程称为会话过程,在这个会话过程将包含客户进程同服务进程进行交互需要的所有资源,包括一个TCP连接用于发送控制数据,UDP连接用于发送媒体数据,还有应用缓冲和其他资源。)

2

十一 数据结构的传输和XDR标准

messageid用于指明这个数据包在本会话过程中的消息序号。ackinfo用于回带确认信息。包头的结构参看图 。会话过程的概念参见图 。

数据包头0数据包的长度2命令类型3会话过程ID5消息ID7回带信息10图11.3 数据包头的结构

一个会话过程TCP连接发送控制命令应用接收缓冲serv_udpTCP熟知端口应用发送缓冲UDP端口TCP端口cli_udpUDP熟知端口UDP连接传送数据图11.4 简单视频传送的示意图

我们将传递一个连接请求的数据包,其结构如下:

typedef struct ConnInfo{

uint32_t userid; uint16_t usernamlen; char char

usernam[MAX_USER_NAMLEN]; usercode[MAX_USER_CODLEN];

uint16_t usercodelen; uint16_t udpport;

}ConnInfo;

userid是用户唯一的用户ID,usernamlen是用户名长度,usernam用于存放用户名,(实际上我们可以将usernam定义成指针,这里为了程序简短,避免过多的内存分配和释放语句,我们将它定义成字符数组。)usercodelen是用户密码的长度,usercode是用户的密码,udpport是客户进程使用的udp端口号。 最后我们定义数据包的结构如下:

typedef struct{ ControlHeader

head;

3

Linux网络编程

void * }Packet;

detail;

数据包中,包头结构是必须的,而后面的detail指针将指向数据包的具体内容,对于

一些简单的消息,detail指针将为空。我们使用这样的定义接口,是为了给不同的消息结构提供一致的接口,并且可以非常方便的进行消息的扩展。

我们使用的发送方式是,首先将数据结构发送到一个应用缓冲区中,而后一次性的调用write写进套接字缓冲区。对于字符串,我们首先填写字符串的长度,而后再填入字符串的内容。接收方可以首先字符串的长度,而后重构字符串。具体过程参见下图。

\0x000x05linyu发送方1.首先读取长度接收方0x000x053.读取字符串内容2.根据数据长度分配空间“linyu\图11.5 字符串的发送2. 应用源码 #define PLAY_REQ 0x40 #define CONN_REQ 0x60 #define FORWARD_REQ 0x47 #define BACK_REQ

0x49

#define MAX_USER_NAMLEN 20 #define MAX_USER_CODLEN 20

/*helpers to send data to the buffer*/

/*

*我们示例uint16_t和uint32_t、字符串的传送,下面的函数将实现把这几种 *数据类型的数据写进应用发送缓冲区

*/

void SendUint32_tToBuffer(char *buf,uint32_t n) {

n=htonl(n);

4

十一 数据结构的传输和XDR标准

bcopy((char *)&n,buf,sizeof(uint32_t));

}

void SendUint16_tToBuffer(char *buf,uint16_t n) { n=htons(n);

bcopy((char *)&n,buf,sizeof(uint16_t));

}

void SendStringToBuffer(char *buf,char *str) { bcopy(str,buf,strlen(str)); }

uint32_t GetUint32_tFromBuf(char *buf) { uint32_t i;

bcopy(buf,(void *)&i,sizeof(uint32_t)); return ntohl(i); /*convert 32 bits int to host presentation*/

}

uint16_t GetUint16_tFromBuf(char *buf) { uint16_t i;

bcopy(buf,(void *)&i,sizeof(uint16_t));

return ntohs(i); /*convert 16 bits int to host presentation*/

} /*

*需要发送的报文的报文头包含:报文的长度packetlen、报文的命令类型cmdtype、*会话的标识sessionid、消息的标识messageid、ackinfo可以用于回带简单的信息。*/

typedef struct{ uint16_t packetlen; uint8_t cmdtype; uint16_t sessionid; uint16_t messageid; uint32_t

ackinfo;

}ControlHeader;

5

Linux网络编程

/*InitControlHeader:初始化报文控制头结构*/

void InitControlHeader(uint8_t type,uint16_t sid,\\ uint16_t mid,uint32_t ack,ControlHeader *phead)

{ phead->cmdtype=type; phead->sessionid=sid; phead->messageid=mid; phead->ackinfo=ack;

}

/*

*SendHeaderToBuffer:将控制头信息发送到应用的发送缓冲区中, *并且转化成合适的网络格式。

*/

void SendHeaderToBuffer(char *buf,ControlHeader *phead) { char *pos=buf;

if(buf==NULL) fprintf(stderr,\ else{ SendUint16_tToBuffer(pos,phead->packetlen); pos+=sizeof(uint16_t); *pos=phead->cmdtype;

pos++;

SendUint16_tToBuffer(pos,phead->sessionid); pos+=sizeof(uint16_t);

SendUint16_tToBuffer(pos,phead->messageid); pos+=sizeof(uint16_t);

SendUint32_tToBuffer(pos,phead->ackinfo);

}

} /*

*GetHeaderFromBuffer:从应用的接收缓冲区中获取控制头信息, *并且转化成合适的本地格式。 */

void GetHeaderFromBuffer(void *pbuf,ControlHeader *p) {

6

十一 数据结构的传输和XDR标准

char *buf=(void *)pbuf;

bcopy(buf,(void *)&p->packetlen,sizeof(uint16_t)); p->packetlen=ntohs(p->packetlen); buf=buf+sizeof(uint16_t);

p->cmdtype=*buf; buf=buf+1;

bcopy(buf,(void *)&p->sessionid,sizeof(uint16_t)); p->sessionid=ntohs(p->sessionid); buf=buf+sizeof(uint16_t);

bcopy(buf,(void *)&p->messageid,sizeof(uint16_t)); p->messageid=ntohs(p->messageid); buf=buf+sizeof(uint16_t);

bcopy(buf,(void *)&p->ackinfo,sizeof(uint32_t)); p->ackinfo=ntohl(p->ackinfo);

}

/*GetHeadLen:获取头部的长度*/

int GetHeadLen() { return (sizeof(uint32_t)+sizeof(uint16_t)*3+sizeof(uint8_t)); }

/*

*定义CONN_REQ消息的消息体:包含用户标识userid、用户的姓名长度usernamlen、*用户姓名usernam、用户密码长度usercodelen、用户密码usercode、 *用户使用的UDP端口号udpport。 */

typedef struct ConnInfo{ uint32_t userid; uint16_t usernamlen;

char usernam[MAX_USER_NAMLEN]; uint16_t usercodelen;

char usercode[MAX_USER_CODLEN]; uint16_t udpport;

}ConnInfo;

7

Linux网络编程

/*Initialization*/

extern void InitConnInfo(uint32_t id,char * name,char * code,\\ uint16_t port,ConnInfo* p);

/*operations*/

extern int GetConnTotalLen(ConnInfo *pInfo); extern void FreeConnInfo(ConnInfo * pInfo);

extern void SendConnInfoToBuffer(char *buf,ConnInfo *pInfo); extern void GetConnInfoFromBuffer(char *buf,ConnInfo *p);

/*Initialization*/

void InitConnInfo(uint32_t id,char * name,char * code, uint16_t port,ConnInfo* p) { p->userid=id;

p->usernamlen=strlen(name);

if(p->usernamlen>MAX_USER_NAMLEN) err_msg(\ p->usercodelen=strlen(code);

if(p->usernamlen>MAX_USER_CODLEN) err_msg(\

strcpy(p->usernam,name); strcpy(p->usercode,code);

p->udpport=port;

}

/*FreeConnInfo:释放接收struct ConnInfo*/ void FreeConnInfo(ConnInfo * pInfo) { if(pInfo!=NULL) free(pInfo); pInfo=NULL;

}

/*GetConnTotalLen:计算在缓冲区中该结构的字节数*/ int GetConnTotalLen(ConnInfo *pInfo) { int len=3*sizeof(uint16_t)+sizeof(uint32_t);

len+=pInfo->usernamlen;

8

十一 数据结构的传输和XDR标准

len+=pInfo->usercodelen; return len;

}

/*SendConnInfoToBuffer:将结构struct ConnInfo发送到缓冲区中*/ void SendConnInfoToBuffer(char *buf,ConnInfo *pInfo) { SendUint32_tToBuffer(buf,pInfo->userid); buf=buf+sizeof(uint32_t);

SendUint16_tToBuffer(buf,pInfo->usernamlen); buf=buf+sizeof(uint16_t);

SendStringToBuffer(buf,pInfo->usernam); buf=buf+strlen(pInfo->usernam);

SendUint16_tToBuffer(buf,pInfo->usercodelen); buf=buf+sizeof(uint16_t);

SendStringToBuffer(buf,pInfo->usercode); buf=buf+strlen(pInfo->usercode);

SendUint16_tToBuffer(buf,pInfo->udpport);

}

/*GetConnInfoFromBuffer:从缓冲区中获取结构ConnInfo*/ void GetConnInfoFromBuffer(char *buf,ConnInfo *p) { p->userid=GetUint32_tFromBuf(buf); buf=buf+sizeof(uint32_t);

p->usernamlen=GetUint16_tFromBuf(buf); buf=buf+sizeof(uint16_t);

bcopy(buf,p->usernam,p->usernamlen); p->usernam[p->usernamlen]='\\0';

buf=buf+p->usernamlen;

p->usercodelen=GetUint16_tFromBuf(buf); buf=buf+sizeof(uint16_t);

bcopy(buf,p->usercode,p->usercodelen); p->usercode[p->usercodelen]='\\0'; buf=buf+p->usercodelen;

p->udpport=GetUint16_tFromBuf(buf);

}

9

Linux网络编程

/*

*定义报文的结构,它包含报文控制头和具体的报文内容,报文的内容可以根据 *控制头中的命令不同而不同。报文的内容由detail指针来指示。 */

typedef struct{ ControlHeader head; void * detail;

}Packet;

/*Initialization*/

extern void ConstructPacket(uint8_t type,uint16_t sid, uint16_t mid,uint32_t ack,void * pdetail,Packet *p); extern void FreePacket(Packet * p);

/*operations*/

extern void GetPacketFromBuffer(char * buf,int len,Packet *p); extern void SendPacketToBuffer(Packet * p,char* buf); /*

*SendPacketToBuffer:将报文信息发送到应用的发送缓冲区中, *并且转化成合适的网络格式。

*/

void SendPacketToBuffer(Packet * p,char* buf) { int i;

char *pos;

int len=(p->head).packetlen;

SendHeaderToBuffer(buf,&(p->head));

pos=buf+GetHeadLen();

if(p->detail!=NULL){ switch((p->head).cmdtype){ case CONN_REQ:

SendConnInfoToBuffer(pos,p->detail); break;

/*many other messages here*/ /*

case PLAY_REQ:

10

Linux网络编程

下面,我们列出XDR库函数中进行类型转化的函数。 #include

extern bool_t xdr_void ((void));

xdr_void用于处理没有数据的空选项。

extern bool_t xdr_short ((XDR *xdrs, short *sp));

xdr_short用于处理short类型数据。xdrs是XDR流指针,sp是指向存放short类型数据空间的指针。

extern bool_t xdr_u_short ((XDR *xdrs, u_short *usp));

xdr_u_short用于处理u_short类型数据。xdrs是XDR流指针,usp是指向存放u_short类型数据空间的指针。

extern bool_t xdr_int ((XDR *xdrs, int *ip));

xdr_int用于处理int类型数据。xdrs是XDR流指针,ip是指向存放int类型数据空间的指针。

extern bool_t xdr_u_int ((XDR *xdrs, u_int *up));

xdr_u_int用于处理u_int类型数据。xdrs是XDR流指针,up是指向存放u_int类型数据空间的指针。

extern bool_t xdr_long ((XDR *xdrs, long *lp)); xdr_long用于处理long类型数据。xdrs是XDR流指针,lp是指向存放long类型数据空间的指针。

extern bool_t xdr_u_long ((XDR *xdrs, u_long *ulp)); xdr_u_long用于处理u_long类型数据。

extern bool_t xdr_hyper ((XDR *xdrs, quad_t *llp)); xdr_hyper用于处理hyper64比特数类型数据。llp是指向hyper类型的指针。 extern bool_t xdr_u_hyper ((XDR *xdrs, u_quad_t *ullp));

xdr_u_hyper用于处理u_hyper64比特无符号整数类型数据。 extern bool_t xdr_longlong_t ((XDR *xdrs, quad_t *llp)); extern bool_t xdr_u_longlong_t ((XDR *xdrs, u_quad_t *ullp)); 这两个函数分别同xdr_hyper、xdr_u_hyper函数类似。 extern bool_t xdr_int8_t ((XDR *xdrs, int8_t *ip));

xdr_int8_t用于处理8比特符号整数类型数据。

extern bool_t xdr_uint8_t ((XDR *xdrs, uint8_t *up)); xdr_uint8_t用于处理8比特无符号整数类型数据。

extern bool_t xdr_int16_t ((XDR *xdrs, int16_t *ip)); xdr_int16_t用于处理16比特符号整数类型数据。

extern bool_t xdr_uint16_t ((XDR *xdrs, uint16_t *up));

xdr_uint16_t用于处理16比特无符号整数类型数据。 extern bool_t xdr_int32_t ((XDR *xdrs, int32_t *ip)); xdr_int32_t用于处理32比特符号整数类型数据。 extern bool_t xdr_uint32_t ((XDR *xdrs, uint32_t *up)); xdr_uint32_t用于处理32比特无符号整数类型数据。

16

十一 数据结构的传输和XDR标准

extern bool_t xdr_int64_t ((XDR *xdrs, int64_t *ip)); xdr_int64_t用于处理64比特符号整数类型数据。

extern bool_t xdr_uint64_t ((XDR *xdrs, uint64_t *up)); xdr_uint64_t用于处理64比特无符号整数类型数据。 extern bool_t xdr_bool ((XDR *xdrs, bool_t *bp)); xdr_bool用于处理bool_t类型数据。

extern bool_t xdr_enum ((XDR *xdrs, enum_t *ep)); xdr_enum用于处理枚举类型数据。

extern bool_t xdr_array ((XDR * _xdrs, caddr_t *addrp, u_int *sizep, u_int maxsize, u_int elsize, xdrproc_t elproc)); extern bool_t xdr_bytes ((XDR *xdrs, char **cpp, u_int *sizep, u_int maxsize));

extern bool_t xdr_opaque ((XDR *xdrs, caddr_t cp, u_int cnt)); extern bool_t xdr_string ((XDR *xdrs, char **cpp, u_int maxsize)); extern bool_t xdr_union ((XDR *xdrs, enum_t *dscmp, char *unp, const struct xdr_discrim *choices, xdrproc_t dfault));

extern bool_t xdr_char ((XDR *xdrs, char *cp)); extern bool_t xdr_u_char ((XDR *xdrs, u_char *cp));

extern bool_t xdr_vector ((XDR *xdrs, char *basep, u_int nelem, u_int elemsize, xdrproc_t xdr_elem)); extern bool_t xdr_float ((XDR *xdrs, float *fp)); extern bool_t xdr_double ((XDR *xdrs, double *dp));

extern bool_t xdr_reference ((XDR *xdrs, caddr_t *xpp, u_int size,

xdrproc_t roc));

extern bool_t xdr_pointer ((XDR *xdrs, char **objpp, u_int obj_size, xdrproc_t xdr_obj));

extern bool_t xdr_wrapstring ((XDR *xdrs, char **cpp)); extern u_long xdr_sizeof ((xdrproc_t, void *));

这些函数都需要一个已经创建好的XDR流作为操作对象,都返回一个bool_t类型说明操作成功与否。关于这些函数的具体使用方法读者可以参见函数的帮助,我们不一一说明。 2. 2种XDR工作方式

实际上XDR库函数中提供了2种XDR流的支持:工作在内存的XDR流、工作于I/O XDR流。

我们已经介绍的函数xdrmem_create,它用于在内存中创建一个XDR流,我们可以使用将需要传递的数据结构在这个内存XDR编码流中进行编码,而后使用系统调用发送到套接字缓冲区中,而后当接收方收到这些数据后也在内存中创建XDR解码流,并从套接字中将数据复制到这个XDR流中,接着使用对应的函数进行解码,恢复出原来的数据结构。

17

Linux网络编程

数据编码数据解码xdrmem_create创建的内存XDR编码流流头部流数据内容内存XDR解码流流头部流数据内容套接字缓冲区套接字缓冲区图11.6 使用内存XDR流的工作过程

函数xdrstdio_create用于创建工作在I/O上的XDR流,函数的形式如下:

#include

extern void xdrstdio_create ((XDR *xdrs, FILE *file, enum xdr_op xop));

xdrs是指向创建的XDR流的指针,file是用于输入/输出的文件流,xop是操作选项,它的取值同xdrmem_create函数中xop相似。

I/O XDR流可以将编码/解码的结果使用系统提供的标准输出函数输出到文件流中,或者通过标准输入函数库,从文件流中读取编码/解码的结果。

流头部流数据编码/解码结果输出文件流编码/解码结果输入流头部流数据I/O XDR流的工作过程图11.7 I/O XDR流的工作过程

图显示了I/O XDR流的工作过程。实际上,对于套接字描述符,我们可以使用fdopen把它转化成对应的I/O流形式,函数fdopen的形式如下:

#include

FILE *fdopen(int fd);

之后,应用程序每次调用一个XDR的转化函数,则转化函数将使用文件流指针所包含的描述符,自动完成一个带缓冲的write/read操作,将数据发送到套接字缓冲区,或者从套接字缓冲中读取数据,这样,可以不需要进行显示的write/read系统调用。

18

十一 数据结构的传输和XDR标准

文件指针描述符缓冲区所有的XDR转换函数将隐含的调用write/read(程序还可以调用fflush刷新输出)文件指针描述符缓冲区套接字缓冲区套接字缓冲区图11.8 将套接字描述符附加在I/O流后的结果

3. 面向记录的 XDR

上面的这两种XDR都是以流方式工作的,由于XDR和TCP都是流的抽象,所以XDR同TCP可以很好的结合,但是UDP提供的是面向记录的数据抽象,因此为了使XDR同UDP能够很好的结合,XDR库中还提供了面向记录的XDR抽象。 另外,函数xdrrec_create函数可以直接在TCP套接字上创建XDR流。函数的形式如下: #include

extern void xdrrec_create ((XDR *xdrs, u_int sendsize,

u_int recvsize, caddr_t tcp_handle, int (*readit) (char *, char *, int), int (*writeit) (char *, char *, int)));

send_size是发送缓冲的大小,recvsize是接收缓冲的大小,readit和writeit是读写

函数指针。库函数中还提供了记录边界的控制函数。

/* 在XDR中标记是记录的结束 */

extern bool_t xdrrec_endofrecord ((XDR *xdrs, bool_t sendnow)); sendnow用于指定是否立即发送这个记录。 /* 移动到下一个记录的开始*/

extern bool_t xdrrec_skiprecord ((XDR *xdrs));

/* 判断一个记录是否结束 */

extern bool_t xdrrec_eof ((XDR *xdrs));

/* 释放XDR的内存缓冲 */

19

Linux网络编程

extern void xdr_free ((xdrproc_t roc, char *objp)); 4. 重写代码

#define PLAY_REQ 0x40 #define CONN_REQ 0x60 #define FORWARD_REQ 0x47

#define BACK_REQ 0x49

/*新的结构去除了原先的packetlen长度域*/ typedef struct{ uint8_t cmdtype; uint16_t sessionid; uint16_t messageid; uint32_t

ackinfo;

}ControlHeader;

void InitControlHeader(uint8_t type,uint16_t sid,\\ uint16_t mid,uint32_t ack,ControlHeader *phead) { phead->cmdtype=type; phead->sessionid=sid; phead->messageid=mid; phead->ackinfo=ack; }

void SendHeaderToBuffer(char *xdrs,ControlHeader *phead) { if(xdrs==NULL||phead==NULL)

fprintf(stderr,\ else{ xdr_uint16_t(xdrs,&phead->packetlen); xdr_uint8_t(xdrs,&phead->cmdtype); xdr_uint16_t(xdrs,&phead->sessionid); xdr_uint16_t(xdrs,&phead->messageid); xdr_uint32_t(xdrs,&phead->ackinfo); }

}

void GetHeaderFromBuffer(XDR * xdrs,ControlHeader *p)

20

十一 数据结构的传输和XDR标准

{ if(xdrs==NULL|| p==NULL) fprintf(stderr,\ else{ xdr_uint16_t(xdrs,&phead->packetlen); xdr_uint8_t(xdrs,&phead->cmdtype); xdr_uint16_t(xdrs,&phead->sessionid); xdr_uint16_t(xdrs,&phead->messageid); xdr_uint32_t(xdrs,&phead->ackinfo); }

}

/*新的结构去除了原先的字符串长度域*/ typedef struct ConnInfo{ uint32_t userid;

char usernam[MAX_USER_NAMLEN]; char usercode[MAX_USER_CODLEN]; uint16_t udpport;

}ConnInfo;

/*Initialization*/

extern void InitConnInfo(uint32_t id,char * name,char * code,\\ uint16_t port,ConnInfo* p);

/*operations*/

extern void FreeConnInfo(ConnInfo * pInfo);

extern void SendConnInfoToBuffer(char *xdrs,ConnInfo *pInfo); extern void GetConnInfoFromBuffer(char *xdrs,ConnInfo *p);

/*Initialization*/

void InitConnInfo(uint32_t id,char * name,char * code, uint16_t port,ConnInfo* p) { p->userid=id;

strcpy(p->usernam,name); strcpy(p->usercode,code); p->udpport=port;

}

void FreeConnInfo(ConnInfo * pInfo)

21

Linux网络编程

{ }

if(pInfo!=NULL) free(pInfo); pInfo=NULL;

void SendConnInfoToBuffer(char *xdrs,ConnInfo *p) { if(xdrs==NULL|| p==NULL) fprintf(stderr,\ else{ xdr_uint32_t(xdrs,&p->userid); xdr_string(xdrs,p->usernam); xdr_string(xdrs,&p->usercode); xdr_uint16_t(xdrs,&p->udpport); }

}

void GetConnInfoFromBuffer(char *xdrs,ConnInfo *p) { if(xdrs==NULL|| p==NULL) fprintf(stderr,\ else{ xdr_uint32_t(xdrs,&p->userid); xdr_string(xdrs,p->usernam); xdr_string(xdrs,&p->usercode); xdr_uint16_t(xdrs,&p->udpport); }

}

typedef struct{ ControlHeader head;

void * detail;

}Packet;

/*Initialization*/

extern void ConstructPacket(uint8_t type,uint16_t sid, uint16_t mid,uint32_t ack,void * pdetail,Packet *p); extern void FreePacket(Packet * p);

22

十一 数据结构的传输和XDR标准

/*operations*/

extern void GetPacketFromBuffer(XDR* xdrs,int len,Packet *p); extern void SendPacketToBuffer(Packet * p,XDR* xdrs);

void SendPacketToBuffer(Packet * p,XDR* xdrs) { SendHeaderToBuffer(xdrs,&(p->head));

if(p->detail!=NULL){ switch((p->head).cmdtype){ case CONN_REQ: SendConnInfoToBuffer(xdrs,p->detail);

break;

/*many other messages here*/ /*

case PLAY_REQ: SendPlayInfoToBuffer(xdrs,p->detail);

break;

case FORWARD_REQ:

case BACK_REQ: SendForBackInfoToBuffer(xdrs,p->detail); break; */

default: fprintf(stderr,\ break;

}

}

}

void GetPacketFromBuffer(Packet * p,XDR * xdrs) { GetHeaderFromBuffer(xdrs,&(p->head));

switch((p->head).cmdtype){ case CONN_REQ: p->detail=Malloc(sizeof(ConnInfo));

GetConnInfoFromBuffer(xdrs,(ConnInfo *)p->detail);

23

Linux网络编程

break;

/*many other messages here*/ /*

case PLAY_REQ: p->detail=Malloc(sizeof(PlayInfo));

GetPlayInfoFromBuffer(xdrs,(PlayInfo *)p->detail); break; case FORWARD_REQ:

case BACK_REQ: p->detail=Malloc(sizeof(ForBackInfo));

GetForBackInfoFromBuffer(xdrs,(ForBackInfo *)p->detail); break; */ default: fprintf(stderr,\ break;

}

}

void FreePacket(Packet * p) { if(p->detail!=NULL){ switch((p->head).cmdtype){ case CONN_REQ:

FreeConnInfo((ConnInfo *)p->detail); break; /*

case PLAY_REQ: FreePlayInfo((PlayInfo *)p->detail);

break;

case FORWARD_REQ: case BACK_REQ: free((ForBackInfo *)p->detail); p->detail=NULL;

break;

*/

default:

break;

}

free(p);

24

十一 数据结构的传输和XDR标准

}

}

p=NULL;

void SendPacket(int connfd,uint8_t type,uint32_t info,void *moreinfo, { }

void GetPacket(int connfd,Pakcet *ppacket) { Packet* ppacket; char buffer[3000]; }

GetPacketFromBuffer(ppacket,xdrs); XDR *xdrs;

/*将I/O流附加在连接套接字上*/ FILE *file_p=fdopen(conn_fd);

uint16_t SessionId) Packet* ppacket; char buffer[3000]; XDR *xdrs;

/*将I/O流附加在连接套接字上*/ FILE *file_p=fdopen(conn_fd); /*创建用于编码的XDR I/O流*/

xdrstdio_create(xdrs,file_p,XDR_ENCODE); ppacket=(Packet *)Malloc(sizeof(Packet)); ppacket->detail=NULL;

ConstructPacket(type,0,0,info,moreinfo,ppacket);

SendPacketToBuffer(ppacket,xdrs); FreePacket(ppacket);

/*创建用于编码的XDR I/O流*/

xdrstdio_create(xdrs,file_p,XDR_DECODE);

我们在新的代码中调整了结构的定义和响应的处理函数。由于XDR流将能够定义字符串,所以我们将原先程序中用于定位字符串的字段去除,整个包长的字段也可以省略。

25

Linux网络编程

我们将套接字描述符转化成I/O流的形式,使得程序可以不必显式的调用输入和输出函数。程序中,发送和接收的代码几乎是相同的,所不同的是操作的XDR流的性质不同,分别是编码和解码流。 本章小结

复杂数据结构在网络中的传输,是非常重要的内容,只有这样,网络应用间才可以非常方便的进行复杂信息的传递。XDR标准中定义了同C语言中类似的数据类型,并提供了响应的类型转化函数,XDR流包含内存XDR流、I/O XDR流和具有记录定义能力的XDR。 XDR的转化函数所做的操作决定于XDR流本身的性质,当XDR流是编码流时,转化函数进行编码的工作,如果XDR流是解码流时,转化函数进行解码操作。

XDR是RPC远程过程调用的基础,我们将在下一章说明RPC调用的原理。

26

Linux网络编程

我们将套接字描述符转化成I/O流的形式,使得程序可以不必显式的调用输入和输出函数。程序中,发送和接收的代码几乎是相同的,所不同的是操作的XDR流的性质不同,分别是编码和解码流。 本章小结

复杂数据结构在网络中的传输,是非常重要的内容,只有这样,网络应用间才可以非常方便的进行复杂信息的传递。XDR标准中定义了同C语言中类似的数据类型,并提供了响应的类型转化函数,XDR流包含内存XDR流、I/O XDR流和具有记录定义能力的XDR。 XDR的转化函数所做的操作决定于XDR流本身的性质,当XDR流是编码流时,转化函数进行编码的工作,如果XDR流是解码流时,转化函数进行解码操作。

XDR是RPC远程过程调用的基础,我们将在下一章说明RPC调用的原理。

26

本文来源:https://www.bwwdw.com/article/pfft.html

Top