高级IO函数之recvmsg和sendmsg

  

目录

recvmsg 函数

函数原型

参数说明

返回值

sendmsg 函数

函数原型

参数说明

返回值

示例代码


recvmsg 和 sendmsg 是在 linux网络编程中用于通用数据读写的函数,它们提供了比传统的 recv 和 send 函数更强大、灵活的功能,特别是在处理复杂的套接字地址结构、控制消息和辅助数据时。

recvmsg 函数

函数原型

#include 

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

参数说明

  • sockfd:套接字描述符,标识接收数据的套接字。
  • msg:指向 struct msghdr 结构体的指针,该结构体包含了接收数据的详细信息,包括数据缓冲区、地址信息、控制消息等。struct msghdr 的定义通常如下:其中关于struct iovec结构体以及相关知识,请大家先看:高级IO函数之readv和writev-CSDN博客
struct msghdr {
    void         *msg_name;       /* 指向套接字地址结构的指针 */
    socklen_t     msg_namelen;    /* 套接字地址结构的长度 */
    struct iovec *msg_iov;        /* 指向iovec结构体数组的指针 */
    int           msg_iovlen;     /* iovec结构体数组的元素个数 */
    void         *msg_control;    /* 指向控制消息缓冲区的指针 */
    socklen_t     msg_controllen; /* 控制消息缓冲区的长度 */
    int           msg_flags;      /* 接收消息的标志 */
};
  • flags:接收数据时的标志位,可以是 0 或以下一个或多个标志的按位或:
    • MSG_DONTWAIT:设置为非阻塞接收,若没有数据可接收,函数立即返回,而不是阻塞等待。
    • MSG_PEEK:查看数据,数据被复制到用户缓冲区,但保留在套接字接收队列中,下次接收操作仍可获取相同的数据。
    • MSG_WAITALL:等待直到请求的字节数全部被接收,除非发生错误、接收到信号或连接被关闭。

返回值

        成功时,返回接收到的字节数。如果连接被关闭,返回 0。出错时,返回 -1,并设置 errno 以指示错误原因,如 EAGAIN(非阻塞模式下无数据可读)、EBADF(无效的文件描述符)等。

sendmsg 函数

函数原型

#include 

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

参数说明

  • sockfd:套接字描述符,标识发送数据的套接字。
  • msg:指向 struct msghdr 结构体的指针,与 recvmsg 中的 msg 结构体类似,但用于指定发送数据的相关信息,如数据缓冲区、目标地址、控制消息等。
  • flags:发送数据时的标志位,常见的标志包括:
    • MSG_DONTWAIT:设置为非阻塞发送,若套接字缓冲区没有足够空间,函数立即返回,而不是阻塞等待。
    • MSG_NOSIGNAL:在面向连接的套接字(如 TCP)上发送数据时,如果连接已断开,不产生 SIGPIPE 信号,而是返回 -1 并设置 errno 为 EPIPE

返回值

        成功时,返回发送的字节数。出错时,返回 -1,并设置 errno 以指示错误原因,如 EAGAIN(非阻塞模式下套接字缓冲区满)、EBADF(无效的文件描述符)等。

示例代码

        大家可以类比udp服务器的逻辑去看。

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

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_DUDP, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    memset(&cliaddr, 0, sizeof(cliaddr));

    // 填充服务器地址结构
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT);

    // 绑定套接字到指定地址和端口
    if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    char buffer[BUFFER_SIZE];
    struct msghdr send_msg, recv_msg;
    struct iovec iov_send, iov_recv;
    socklen_t len = sizeof(cliaddr);

    // 初始化接收消息
    iov_recv.iov_base = buffer;
    iov_recv.iov_len = BUFFER_SIZE;
    recv_msg.msg_name = (void *)&cliaddr;
    recv_msg.msg_namelen = sizeof(cliaddr);
    recv_msg.msg_iov = &iov_recv;
    recv_msg.msg_iovlen = 1;
    recv_msg.msg_control = NULL;
    recv_msg.msg_controllen = 0;
    recv_msg.msg_flags = 0;

    // 接收消息
    ssize_t recv_bytes = recvmsg(sockfd, &recv_msg, 0);
    if (recv_bytes < 0) {
        perror("recvmsg failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    buffer[recv_bytes] = '\0';
    printf("Received: %s\n", buffer);

    // 初始化发送消息
    iov_send.iov_base = "Message received successfully";
    iov_send.iov_len = strlen("Message received successfully");
    send_msg.msg_name = (void *)&cliaddr;
    send_msg.msg_namelen = len;
    send_msg.msg_iov = &iov_send;
    send_msg.msg_iovlen = 1;
    send_msg.msg_control = NULL;
    send_msg.msg_controllen = 0;
    send_msg.msg_flags = 0;

    // 发送消息
    ssize_t send_bytes = sendmsg(sockfd, &send_msg, 0);
    if (send_bytes < 0) {
        perror("sendmsg failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("Sent %zd bytes\n", send_bytes);

    close(sockfd);
    return 0;
}

你可能感兴趣的:(linux,服务器,运维)