首先介绍两个数据结构及相关的操作函数:struct iovec、struct msghdr
struct iovec {
void * iov_base; /*向量的缓冲地址*/
size_t iov_len; /*向量缓冲区的大小,以字节为单位*/
};
iovec定义在linux/include/uio.h中,此数据结构与readv()和writev()联合使用。
ssize_t readv(int s, const struct iovec *vector, int count);
返回值为成功接收的字节数。 s: 文件描述符 vector:iovec数组的起始地址 count:iovec数组的元素个数。
ssize_t writev(int fd, const iovec *vector, int count);
返回值为成功发送的字节数。 fd:文件描述符 vector:发送数据的vector数组地址 count:iovec数组的元素个数
应用的例子:
/* 服务器端代码 */ #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <sys/uio.h> #include <netinet/in.h> #define Serv_Port 8888 #define BackLog 5 void process_client_vector(int sock_cli); int main(char *argv[], int argc) { int sock_serv, sock_cli; struct sockaddr_in server_addr; struct sockaddr_in client_addr; int err; pid_t handle_client_pid; sock_serv = socket(AF_INET, SOCK_STREAM, 0); if(sock_serv < 0){ printf("Error When build Socket "); return 1; } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(Serv_Port); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); err = bind(sock_serv, (struct sockaddr*)&server_addr, sizeof(server_addr)); if(err < 0){ printf("Error When Bind "); return 1; } err = listen(sock_serv, BackLog); if(err < 0){ printf("Error When Listen "); return 1; } // while(1){ int addrlen = sizeof(struct sockaddr); printf("Waiteing... ... "); sock_cli = accept(sock_serv, (struct sockaddr*)&client_addr, &addrlen); if(sock_cli < 0) ;//continue; handle_client_pid = fork(); if(handle_client_pid == 0){ close(sock_serv); process_client_vector(sock_cli); } else close(sock_cli); // } } void process_client_vector(int sock_cli) { int number; char str; char ch; int i = 0; printf("One client is Accpted "); struct iovec *recv_vector = (struct iovec*)malloc(3*sizeof(struct iovec)); if(!recv_vector){ printf("NO Enough Space Here "); return; } recv_vector[0].iov_base = &number; recv_vector[0].iov_len = sizeof(number); recv_vector[1].iov_base = &str; recv_vector[1].iov_len = sizeof(str); recv_vector[2].iov_base = &ch; recv_vector[2].iov_len = sizeof(ch); ssize_t size = readv(sock_cli, recv_vector, 3); printf("RECVED: Number%d STR: %c CHAR:%c ",number,str,ch); }
/* 客户器端代码 */ #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <sys/uio.h> #include <netinet/in.h> #define Serv_Port 8888 void process_server_vector(int sock_cli); int main(char *argv[], int argc) { struct sockaddr_in server_addr; int err; int sock_cli; sock_cli = socket(AF_INET, SOCK_STREAM, 0); if(sock_cli < 0){ printf("Error When Socket() "); return 1; } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(Serv_Port); inet_pton(AF_INET, "127.0.0.1",&server_addr.sin_addr); connect(sock_cli, (struct sockaddr *)&server_addr, sizeof(server_addr)); process_server_vector(sock_cli); close(sock_cli); return 0; } void process_server_vector(int sock_cli) { int number = 10; char str = 'K'; char ch = 'M'; ssize_t size = 0; struct iovec *vector = (struct iovec *)malloc(3*sizeof(struct iovec)); vector[0].iov_base = &number; vector[0].iov_len = sizeof(number); vector[1].iov_base = &str; vector[1].iov_len = sizeof(str); vector[2].iov_base = &ch; vector[2].iov_len = sizeof(ch); size = writev(sock_cli, vector, 3); if(size < 0 ) printf("Writev Error "); }
msghdr 结构体定义在linux/include/socket.h中
struct msghdr {
void *msg_name; /* ptr to socket address structure */
int msg_namelen; /* size of socket address structure */
struct iovec *msg_iov; /* scatter/gather array */
__kernel_size_t msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data */
__kernel_size_t msg_controllen; /* ancillary data buffer length */
unsigned int msg_flags; /* flags on received message */
};
这个数据结构如果不考虑msg_falsgs,与struct iovec的用法并没有很大的差别,操作这个数据结构的函数:
#include<sys/uio.h>
ssize_t recvmsg(int s, const struct msghdr *msg, int flags);
s: 套接字 msg:承接数据的消息数据结构 flags:没有很大的意义,
ssize_t sendmsg(int s, const struct msghdr *msg, int flags );
s: 套接字 msg:承接数据的消息数据结构 flags:决定以什么方式发送数据
readmsg的接收方式取决于msg结构中msg_flags的值,这也是readmsg()与函数sendmsg()的不同的地方,sendmsg()函数的发送方式有参数决定。
在没有介绍套接字的选项前,先不对msghdr的相关代码编写进行实践,因为msghdr中的msg_name,msg_control,msg_flags 等成员的设定与当前使用的协议是相关的,待以后把套接字的选项介绍完后,在深入探究msghdr的使用。
除了上述介绍的几个函数,还有很多的IO函数,现总结如下:
int read(int fd, void * buffer, int nbyte);
int write(int handle, void *buf, int nbyte);
这两个函数可以用于任何的描述符,可以用于文件,标准输入输出,和套接字。
readv(), writev(),recvmsg(),sendmsg()的具体函数参考上面的具体内容。
int recvfrom(int s,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen);
s: 套接字 buf:数据接收缓冲区 len:接收数据长度 flags:是以下一个或者多个标志的组合体,可通过or操作连在一起
from:数据的来源的地址 fromlen: 地址长度。