《UNIX网络编程 卷2》 笔记: 字节流与消息

来源:互联网 发布:股票数据api接口 编辑:程序博客网 时间:2024/05/21 06:40

目前我们给出的管道和FIFO的例子通信双方使用的都是字节流I/O模型,程序不对它作任何解释。举个例子,如果一个进程从FIFO收到100字节数据,它不知道这100字节数据是一个进程发送的还是多个进程发送的。

如何区分字节流是哪个进程发送的?

有种常用的技巧就是将数据封装在一条消息(可以理解为带边界信息的字节流)里。消息包括消息头和数据,通信双方事先约定好消息的格式,就可以通过消息通信了。比如为解决上面的问题,双方可以约定在消息头里包含进程ID号,这样就可以区分字节流是哪个进程发送的了。

下面我们修改管道这一节中的代码,使得客户和服务器使用消息通信。

首先,我们需要定义一个消息的格式。

#define PIPE_BUF 5120/*消息包含的最大数据量*/#define MAXMESGDATA (PIPE_BUF - 2 * sizeof(long))/*消息头的长度*/#define MESGHDRSIZE (sizeof(struct mymesg) - MAXMESGDATA)struct mymesg {long mesg_len; /*数据长度*/long mesg_type; /*消息类型*/char mesg_data[MAXMESGDATA]; /*数据*/};
本例中,我们定义一个消息的消息头包含消息类型和数据长度,整个消息的最大长度为5120字节。消息里的数据长度为0表明本次通信结束,为1表示正常的数据。本例实际用不到消息类型,所以代码中消息类型一直为1,这样使用主要是告诉读者这种包含数据长度和消息类型的消息头是非常通用的。

读消息和发送消息的函数代码如下:

ssize_tmesg_send(int fd, struct mymesg *mptr){return (write(fd, mptr, MESGHDRSIZE + mptr->mesg_len));}ssize_tmesg_recv(int fd, struct mymesg *mptr){size_t len;ssize_t n;/*读消息头*/if ((n = Read(fd, mptr, MESGHDRSIZE)) == 0)return 0;else if (n != MESGHDRSIZE)err_quit("message header: expected %d, got %d", MESGHDRSIZE, n);/*读消息里的数据*/if ((len = mptr->mesg_len) > 0)if ((n = Read(fd, mptr->mesg_data, len)) != len)err_quit("message data: expected %d, got %d", len, n);return len;}/*包裹函数*/ssize_tMesg_send(int fd, struct mymesg *mptr){ssize_t n;if ((n = mesg_send(fd, mptr)) == -1)return -1;return n;}ssize_tMesg_recv(int fd, struct mymesg *mptr){return mesg_recv(fd, mptr);}
发送消息时,我们填充消息类型和数据长度,添加数据,然后调用Mesg_send发送。

修改后的client函数代码如下:

void client(int readfd, int writefd){size_t len;ssize_t n;struct mymesg mesg;/*从标准输入读入文件路径*/Fgets(mesg.mesg_data, MAXMESGDATA, stdin);len = strlen(mesg.mesg_data);/*删除结尾的'\n'*/if (mesg.mesg_data[len - 1] == '\n')len--;/*封装成消息发送到服务器*/mesg.mesg_len = len;mesg.mesg_type = 1;Mesg_send(writefd, &mesg);/*从管道2读入文件内容,一次读取一行*/while ((n = Mesg_recv(readfd, &mesg)) > 0)Write(STDOUT_FILENO, mesg.mesg_data, n);}
修改后的server函数代码如下:

void server(int readfd, int writefd){FILE *fp;ssize_t n;struct mymesg mesg;mesg.mesg_type = 1;/*获取文件路径名*/if ((n = Mesg_recv(readfd, &mesg)) == 0)err_quit("pathname missing");mesg.mesg_data[n] = '\0';/*打开文件*/if ((fp = fopen(mesg.mesg_data, "r")) == NULL) {snprintf(mesg.mesg_data + n, sizeof(mesg.mesg_data) - n,": can't open, %s\n", strerror(errno));mesg.mesg_len = strlen(mesg.mesg_data);Mesg_send(writefd, &mesg);} else {/*从文件一行一行读取内容,封装成消息发送*/while (Fgets(mesg.mesg_data, MAXMESGDATA, fp) != NULL) {mesg.mesg_len = strlen(mesg.mesg_data);Mesg_send(writefd, &mesg);}Fclose(fp);}/*发送一个数据长度为0的消息,表示发送结束*/mesg.mesg_len = 0;Mesg_send(writefd, &mesg);}

阅读全文
0 0