socket编程:recvmsg 和 sendmsg 函数

背景

复习 socket 编程的时候发现了以前没有留意到的 2个函数:recvmsgsendmsg

ref : Linux编程之recvmsg和sendmsg函数

知识

先来看看函数原型:

#include <sys/types.h>
#include <sys/socket.h>

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

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

struct msghdr {
    void          *msg_name;        // protocol address
    socklen_t      msg_namelen;     // size of protocol address
    struct iovec  *msg_iov;         // scatter/gather array
    int            msg_iovlen;      // elements in msg_iov
    void          *msg_control;     // ancillary data (cmsghdr struct)
    socklen_t      msg_controllen;  // length of ancillary data
    int            msg_flags;       // flags returned by recvmsg()
};

msg_name 和 msg_namelen

这两个成员用于套接字未连接的场合(如未连接 UDP 套接字)。它们类似 recvfrom 和 sendto 的第五个和第六个参数:

  • msg_name 指向一个套接字地址结构,调用者在其中存放接收者(对于 sendmsg 调用)或发送者(对于recvmsg调用)的协议地址。如果无需指明协议地址(如对于 TCP 套接字或已连接 UDP 套接字),msg_name 应置为空指针。
  • msg_namelen 对于 sendmsg 是一个值参数,对于 recvmsg 却是一个值-结果参数。

msg_iov 和 msg_iovlen

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

msg_control 和 msg_controllen

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

flags

对于 recvmsg 和 sendmsg,必须区别它们的两个标志变量:

  • 一个是传递值的 flags 参数;
  • 另一个是所传递 msghdr 结构的 msg_flags 成员,它传递的是引用,因为传递给函数的是该结构的地址。

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

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

recvmsg 返回的 7 个标志如下:

  • MSG_BCAST:本标志随 BSD/OS 引入,相对较新。它的返回条件是本数据包作为链路层广播收取或者其目的 IP 地址是一个广播地址。与 IP_RECVD-STADDR 套接字选项相比,本标志是用于判定一个 UPD 数据包是否发往某个广播地址的更好方法。
  • MSG_MCAST:本标志随 BSD/OS 引入,相对较新。它的返回条件是本数据报作为链路层多播收取。
  • MSG_TRUNC:本标志的返回条件是本数据报被截断,也就是说,内核预备返回的数据超过进程事先分配的空间(所有 iov_len 成员之和)。
  • MSG_CTRUNC:本标志的返回条件是本数据报的辅助数据被截断,也就是说,内核预备返回的辅助数据超过进程事先分配的空间(msg_controllen)。
  • MSG_EOR:本标志的返回条件是返回数据结束一个逻辑记录。TCP 不使用本标志,因为它是一个字节流协议。
  • MSG_OOB:本标志绝不为 TCP 带外数据返回。它用于其他协议族(如 OSI 协议族)。
  • MSG_NOTIFICATION:本标志由 SCTP 接收者返回,指示读入的消息是一个事先通知,而不是数据消息。

图解

下图展示了一个 msghdr 结构以及它指向的各种信息。图中假设进程即将对一个 UDP 套接字调用 recvmsg:

图中给协议地址分配了 16 个字节,给辅助数据分配了 20 个字节。为缓冲数据初始化了一个由 3 个 iovec 结构构成的数组:第一个指定一个 100 字节的缓冲区,第二个指定一个 60 字节的缓冲区,第三个指定一个 80 字节的缓冲区。假设已为这个套接字设置了 IP_RECVDSTADDR 套接字选项,以接收所读取 UDP 数据包的目的 IP 地址。

假设从 198.38.100:2000 到达一个 170 字节的 UDP 数据报,它的目的地是我们的 UDP 套接字,目的 IP 地址为 206.168.112.96.下图展示了 recvmsg 返回时 msghdr 结构中的所有信息。

图中被 recvmsg 修改过的字段标上了阴影。从第一幅图到第二幅图的变动包括以下几点:

  • 由 msg_name 成员指向的缓冲区被填以一个网际网套接字地址结构,其中有所收到数据报的源 IP 地址和源 UPD 端口号。
  • msg_namelen 成员(一个值-结果参数)被更新为存放在 msg_name 所指缓冲区中的数据量。本成员并无变化,因为 recvmsg 调用前和返回后其值均为 16.
  • 所收取数据报的前 100 个字节数据存放在第一个缓冲区,中 60 字节数据存放在第二个缓冲区,后 10 字节数据存放在第三个缓冲区。最后那个缓冲区的后 70 字节没有改动。recvmsg 函数的返回值(即 170)就是该数据报的大小。
  • 由 msg_control 成员指向的缓冲区被填以一个 cmsghdr 结构。该 cmsghdr 结构中,cmsg_len 成员值为 16,cmsg_level 成员值为 IPPROTO_IP,cmsg_type 成员值为 IP_RECVDSTADDR,随后 4 个字节存放所收到 UDP 数据报的目的 IP 地址。这个 20 字节缓冲区的后 4 个字节没有改动。
  • msg_controllen 成员被更新为所存放辅助数据的实际数据量。本成员也是一个值-结果参数,recvmsg 返回时其结果为 16。
  • msg_flags 成员同样被 recvmsg 更新,不过没有标志返回给进程。

原文地址:https://www.cnblogs.com/schips/p/12539824.html

时间: 2024-11-10 13:24:11

socket编程:recvmsg 和 sendmsg 函数的相关文章

Linux编程之recvmsg和sendmsg函数

recvmsg 和 sendmsg 函数 #include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_

socket编程中服务器端常用函数 以及简单实现

1 常用函数 1.1   socket() int socket(int family, int type, int protocol); socket()打开一个网络通讯端口,如果成功的话,返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1.对于IPv4,family参数指定为AF_INET.对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议.如果是UDP协议,则type参数指定为SOCK_D

PHP Socket编程 之使用fsockopen()函数

Socket可以理解为两台计算机相互通信的通道. 用法:使用fsockopen()函数 具体用法详见上篇文章.函数的参数为URL.端口号.一个存放错误编号的变量.一个存放错误信息字符串的变量和超时等待时间.(只有第一个参数是必须的) 常见的端口表: 端口号 主要用途 21 FTP 22 SSH 23 Telnet 25 SMTP 80 Web 110 POP 其中,组成URl的几个部分为:协议名(scheme),主机(host),端口号(port),文件路径(path),查询参数(query).

socket编程中客户端常用函数 以及简单实现

1 常用函数 1.1   connect() int connect(int sockfd, const struct sockaddr *servaddr, socklen_taddrlen); 客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址.connect()成功返回0,出错返回-1. 1.2   bind():很少用 由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端

基于UDP的socket编程

一.相关函数说明 UDP是无连接的,即发送数据之前不需要建立连接. 除了基于TCP中的socket编程所需的函数之外,基于UDP的socket编程中还需要用到两个函数. 1.sendto函数:用于客户端中指定一目的地发送数据. (1)函数原型 (2)参数说明 sockfd:套接字 buf:待发送数据的缓冲区 len:缓冲区长度 flags:调用方式标志位,一般为0:若改变flags,则sendto发送数据的形式会变成阻塞 dest_addr:指向目的套接字的地址 addrlen:指向目的套接字的

最近关于socket编程的理解

最近看了看socket网络编程,对于我这种一点经验都没有的选手来说只能理解一点点吧.所以在此记录一下最近的收获. socket编程无非就那几个函数,对于服务端来说,主要的为socket(),bind(),listen(),accept(),close().对于客户端来说主要为connect(),close()等.当然,我所说的只是针对tcp协议而言的.对于udp而言,就可以简单很多,服务端和客户端都建立socket并进行绑定,从而用sendto()和recvfrom()通信即可. 以下直接上一个

socket编程之三:socket网络编程中的常用函数

这节本来打算先给出常用函数介绍,再给两个代码实例,写着写着发现越来越长,决定把代码放在下一节. 本节内容持续更新...... 1 socket()函数 原型: int socket(int domain, int type, int protocol); 描述: 类似打开一个文件,返回一个socket描述符,唯一标识一个socket,后面相应的操作都是这用这个socket描述符. 参数: domain:协议族,常用的协议族有AF_INET.AF_INET6.AF_LOCAL.AF_ROUTE等:

1.socket编程:socket编程,网络字节序,函数介绍,IP地址转换函数,sockaddr数据结构,网络套接字函数,socket相关函数,TCP server和client

 1  Socket编程 socket这个词可以表示很多概念: 在TCP/IP协议中,"IP地址+TCP或UDP端口号"唯一标识网络通讯中的一个进程,"IP 地址+端口号"就称为socket. 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接.socket本身有"插座"的意思,因此用来描述网络连 接的一对一关系. TCP/IP协议最早在BSD UNIX上实现,

socket编程中recv与send函数

recv函数会将套接字缓冲区中的内容读出,但不清空,与read函数的区别在此.此函数有一个flag标志位,设为MSG_PEEK. send函数会将缓冲区中的内容写入到套接字,也不清空,与write函数的区别在此. 用这两个函数可以先接收或发送缓冲区中的内容,然后再用readn(此时缓冲区中的内容依然存在)与write函数去继续判断换行符/n,对缓冲区内容实现换行输出. echocli.c #include <unistd.h> #include <sys/types.h> #inc