recvmsg()和sendmsg()函数(1)

这个两个函数是通用的I/O函数。实际上可以把所有read,readv,recv,recvfrom调用替换成recvmsg调用;类似的,各种输出函数也可以替换成sedmsg调用。


函数原型:

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);


这两个函数把大部分参数封装到一个msghdr结构体中:

struct msghdr{

void *msg_name;

socklen_t      msg_namelen;

        struct iovec   *msg_iov;

int  msg_iovlen;

void *msg_control;

socklen_t msg_controllen;

int msg_flags;

};


msg_name和msg_namelen用于套接字未连接的场合(譬如未连接的udp套接字)。它们类似于recvfrom和sendto的第五个和第六个参数。

msg_name:指向一个套接字地址结构,用户存放接收者(对于sendmsg)或发送者(对于sendmsg)的协议地址。如果无需指明协议地址(例如tcp套接字或已连接udp套接字),msg_name应置为空。

msg_namelen:对于sendmsg是一个参数,对recvmsg却是一个结果参数。


msg_iov和msg_iovlen这两个成员制定输入或输出缓冲区数组(即iovec结构数组),类似readv和writev的第二个和第三参数。


msg_control和msg_controlln这两个成员指定可选的辅助数据的位置和大小。msg_controllen对于recvmsg是一个结果参数。


msg_flags: 对于recvmsg和sendmsg,必须区别msg_flags和函数中传递的参数flags的区别(msghdr结构中的msg_flags成员,传递的是引用,因为传递给函数的是该结构的地址):

(1)只有recvmsg使用msg_flags成员。recvmsg被调用时,flags参数被复制到msg_flags成员,并有内核使用其值驱动接收处理过程。内核还根据recvmsg的结果更新msg_flags成员的值。

(2)sendmsg忽略msg_flags成员,因为它直接使用flags参数驱动发送处理过程。这一点意味着如果乡镇某个sendmsg调用中设置MSG_DONTWAIT标志,那就是把flags参数设置为该值,把msg_flags成员设置为该值不起作用。


辅助数据

辅助数据可以通过调用sendmsg和recvmsg这两个函数,使用msghdr结构中的msg_control和msg_controller这两个成员发送和接收。

辅助数据由一个或多个辅助数据对象构成,每个对象以一个定义在头文件中的cmsghdr结构体开头。

struct cmsghdr{

socklen_t cmsg_len;   /*附属数据的字节数,包含结构头的尺寸,这个值有CMSG_LEN()宏计算*/

int msg_level;               /*表明了原始的协议级别(如,SOL_SOCKET)*/

int cmsg_type;              /* 表明了控制信息类型(如SCM_RIGHTS,附属数据对象是文件描述符; SCM_CREDENTIALS,附属数据对象是一个包含证书信息的结构)*/

};

下图展示了在一个控制缓冲区中出现2个辅助数据对象的例子。

recvmsg()和sendmsg()函数(1)_第1张图片


msg_control指向第一个辅助数据对象,附属数据的长度则由msg_controllen指定。每个对象开头都是一个描述该对象的cmshhdr结构。在cmsg_type成员和实际数据之间可以有填充字节,从数据结尾出到下一个辅助数据之前也可以有填充字节。

既然由recvmsg返回的辅助数据可含有任意数目的辅助数据对象,为了对应用程序屏蔽可能出现的填充字节,头文件中定义了以下5个宏,以简化对辅助数据的处理。

#include


struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msghg);

输入参数:指向struct msghdr结构的指针

返回:指向附属数据缓冲区的第一个附属对象的struct cmsghdr指针。如果不存在附属数据对象则返回null


struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh,  struct cmsghdr *cmsg);

输入参数:指向struct msghdr结构的指针,指向当前struct cmsghdr的指针。

返回: 下一个附属数据对象的strcut cmsghdr指针,如果没有下一个附属数据对象,在返回null


size_t CMSG_ALIGN(size_t length);

输入参数:附属数据缓冲区中对象大小

返回:包火了维护对齐所需要的额外的填充字节。


size_t CMSG_SPACE(size_t length);

输入参数:附属数据缓冲区中对象的大小

返回:计算cmghdr头结构加上附属数据大小,并包括对齐字段和可能的结尾填充字符,注意CMSG_LEN()值并不包括可能的结尾填充字符。CMSG_SPACE()对于确定所需的缓冲区尺寸是非常由用的。

注意如果在缓冲区中有多个附属数据结构,一定要同时添加多个CMSG_SPACE()宏调用来得到所需的总空间。


size_t CMSG_LEN(size_t length);

输入参数:附属数据缓冲区中的对象大小

返回:计算cmsghdr头结构加上附属数据大小,包括必要的对齐字段,这个值用设置cmsghdr对象的cmsg_len成员


void * CMSG_DATA(struct cmsghdr *cmsg);

输入参数:指向cmsghdr结构的指针。

返回:跟在头部以及填充字节后的附属数据的第一个字节(如果存在)的地址。


client端代码:

#include
#include
#include
#include
#include
#include
#include

#define MAXLEN 4096

int main(int argc,char ** argv){
        int sockfd,n;
        char rcvline[4096],sendline[4096];
        struct sockaddr_in servaddr;

        if(argc!=2){
                printf("usage:./client ");
                exit(0);
        }

        if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){
                printf("Create socket error:%s(errno:%d)\n",strerror(errno),errno);
                exit(0);
        }

        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family=AF_INET;
        servaddr.sin_port=htons(6666);
        if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0){
                printf("inet_pton error for %s\n",argv[1]);
                exit(0);
        }

        if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){
                printf("connect error: %s(errno:%d)\n",strerror(errno),errno);
                exit(0);
        }

        printf("send msg to server:\n");
        fgets(sendline,4096,stdin);
        
        struct msghdr msg;
               msg.msg_name=NULL;/*TCP,set NULL*/


        struct iovec io; /*return data*/
        io.iov_base=sendline;
        io.iov_len=sizeof(sendline);


        msg.msg_iov=&io;
        msg.msg_iovlen=1; /*one buffer*/


        ssize_t size=sendmsg(sockfd,&msg,0);


        close(sockfd);
        exit(0);
}

server端代码:

#include
#include
#include
#include
#include
#include
#include


#define MAXLINE 4096


int main(int argc,char **argv){
        int listenfd,connfd;
        struct sockaddr_in servaddr;
        char buff[4096];
        int n;


        if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1){
                printf("create socket error:%s(errno:%d)\n",strerror(errno),errno);
                exit(0);
        }


        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family=AF_INET;
        servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
        servaddr.sin_port=htons(6666);


        if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1){
                printf("baind socket error:%s(errno:%d)\n",strerror(errno),errno);
                exit(0);
        }


        if(listen(listenfd,10)==-1){
                printf("listen socket error:%s(errno:%d)\n",strerror(errno),errno);
                exit(0);
        }


        printf("=====wait for client's reqest===\n");
        
        struct msghdr msg;
        msg.msg_name=NULL;
        struct iovec io;
        io.iov_base=buff;
        io.iov_len=MAXLINE;
        msg.msg_iov=&io;
        msg.msg_iovlen=1;
        while(1){
                if((connfd=accept(listenfd,(struct sockaddr *)NULL,NULL))==-1){
                        printf("accept socket error:%s(errno:%d)",strerror(errno),errno);
                        continue;
                }
                ssize_t size=recvmsg(connfd,&msg,0);
                char *tmp=msg.msg_iov[0].iov_base;
                tmp[size]='\0';
                printf("recv msg from client:%s\n",tmp);
                close(connfd);
        }
        close(listenfd);
        return 0;
}

运行:

gcc -o server server.c

gcc -o client client.c

server:  ./server

client:  ./client 127.0.0.1


转自:http://blog.sina.com.cn/s/blog_976bcdc201018su5.html


你可能感兴趣的:(Socket)