做网站的技术门槛高吗南海网官网
目录
recvmsg 函数
函数原型
参数说明
返回值
sendmsg 函数
函数原型
参数说明
返回值
示例代码
recvmsg
和 sendmsg
是在 linux网络编程中用于通用数据读写的函数,它们提供了比传统的 recv
和 send
函数更强大、灵活的功能,特别是在处理复杂的套接字地址结构、控制消息和辅助数据时。
recvmsg
函数
函数原型
#include <sys/socket.h>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 <sys/socket.h>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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>#define PORT 8080
#define BUFFER_SIZE 1024int 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;
}