msghdr结构体是在C++中用于存储和传递消息的结构体。它定义在<sys/socket.h>头文件中,包含以下字段:
struct msghdr {
void *msg_name; // 指向目标地址结构体的指针
socklen_t msg_namelen; // 目标地址结构体的大小
struct iovec *msg_iov; // 指向数据缓冲区的指针
size_t msg_iovlen; // 数据缓冲区的数量
void *msg_control; // 指向辅助数据缓冲区的指针
size_t msg_controllen; // 辅助数据缓冲区的大小
int msg_flags; // 消息标志
};
下面是一个使用msghdr结构体的示例代码:
#include <sys/socket.h>
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
int main() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
cout << "socket创建失败" << endl;
return -1;
}
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("121.43.36.60");
addr.sin_port = htons(8888);
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
strcpy(buffer, "hello, world");
struct iovec iov;
iov.iov_base = buffer;
iov.iov_len = strlen(buffer);
struct msghdr msg;
msg.msg_name = &addr;
msg.msg_namelen = sizeof(addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
ssize_t n = sendmsg(sockfd, &msg, 0);
if (n == -1) {
cout << "发送消息失败" << endl;
close(sockfd);
return -1;
}
close(sockfd);
return 0;
}
上述代码创建了一个UDP套接字,将数据“hello, world”发送到121.43.36.60:8888。使用了msghdr结构体来指定目标地址和数据缓冲区。其中,msg_name指向目标地址结构体的指针,msg_namelen指定目标地址结构体的大小,msg_iov指向数据缓冲区的指针,msg_iovlen指定数据缓冲区的数量,其他字段均为0或默认值。最后,sendmsg函数将消息发送出去。
在C++中,cmsghdr结构体是用于控制消息的头部结构体,它包含了一些控制信息,如文件描述符、IP地址等。其中,msg_control字段是一个指向存储控制信息的缓冲区的指针。
使用msg_control字段的示例代码如下:
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#define CONTROLLEN CMSG_LEN(sizeof(int))
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
std::cerr << "Failed to create socket: " << strerror(errno) << std::endl;
return -1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8080);
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Failed to connect to server: " << strerror(errno) << std::endl;
close(sockfd);
return -1;
}
char buf[1024];
memset(buf, 0, sizeof(buf));
strcpy(buf, "Hello, server!");
struct iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = strlen(buf);
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
char controlbuf[CONTROLLEN];
memset(controlbuf, 0, sizeof(controlbuf));
struct cmsghdr* cmptr;
cmptr = (struct cmsghdr*)controlbuf;
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
cmptr->cmsg_len = CONTROLLEN;
int fd = 12345;
*(int*)CMSG_DATA(cmptr) = fd;
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
if (sendmsg(sockfd, &msg, 0) < 0) {
std::cerr << "Failed to send message: " << strerror(errno) << std::endl;
close(sockfd);
return -1;
}
close(sockfd);
return 0;
}
在上述代码中,我们创建了一个socket连接到服务器,然后构造了一个消息msg,其中包含了一个数据缓冲区iov和一个控制信息cmptr。在构造控制信息cmptr时,我们将一个文件描述符fd放入CMSG_DATA(cmptr)中,然后将cmptr赋值给msg.msg_control。最后,我们通过sendmsg函数将这个消息发送给服务器。
需要注意的是,在接收端需要使用recvmsg函数来接收消息,并从msg.msg_control中提取出控制信息。具体实现可以参考以下代码:
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#define CONTROLLEN CMSG_LEN(sizeof(int))
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
std::cerr << "Failed to create socket: " << strerror(errno) << std::endl;
return -1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8080);
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Failed to connect to server: " << strerror(errno) << std::endl;
close(sockfd);
return -1;
}
char buf[1024];
memset(buf, 0, sizeof(buf));
strcpy(buf, "Hello, server!");
struct iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = strlen(buf);
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
char controlbuf[CONTROLLEN];
memset(controlbuf, 0, sizeof(controlbuf));
msg.msg_control = controlbuf;
msg.msg_controllen = CONTROLLEN;
if (sendmsg(sockfd, &msg, 0) < 0) {
std::cerr << "Failed to send message: " << strerror(errno) << std::endl;
close(sockfd);
return -1;
}
close(sockfd);
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
std::cerr << "Failed to create listen socket: " << strerror(errno) << std::endl;
return -1;
}
struct sockaddr_in listen_addr;
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
listen_addr.sin_port = htons(8081);
if (bind(listenfd, (struct sockaddr*)&listen_addr, sizeof(listen_addr)) < 0) {
std::cerr << "Failed to bind listen socket: " << strerror(errno) << std::endl;
close(listenfd);
return -1;
}
if (listen(listenfd, 10) < 0) {
std::cerr << "Failed to listen on listen socket: " << strerror(errno) << std::endl;
close(listenfd);
return -1;
}
int connfd = accept(listenfd, NULL, NULL);
if (connfd < 0) {
std::cerr << "Failed to accept connection: " << strerror(errno) << std::endl;
close(listenfd);
return -1;
}
struct msghdr msg2;
memset(&msg2, 0, sizeof(msg2));
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
msg2.msg_iov = iov;
msg2.msg_iovlen = 1;
msg2.msg_control = controlbuf;
msg2.msg_controllen = CONTROLLEN;
if (recvmsg(connfd, &msg2, 0) < 0) {
std::cerr << "Failed to receive message: " << strerror(errno) << std::endl;
close(connfd);
close(listenfd);
return -1;
}
int* fdptr = (int*)CMSG_DATA((struct cmsghdr*)controlbuf);
int fd = *fdptr;
std::cout << "Received file descriptor: " << fd << std::endl;
close(fd);
close(connfd);
close(listenfd);
return 0;
}
在上述代码中,我们先创建一个socket连接到服务器,然后发送一个带有控制信息的消息。接着,我们创建一个监听socket,等待客户端连接。当客户端连接成功后,我们使用recvmsg函数从客户端接收消息,并从msg.msg_control中提取出控制信息。最后,我们从控制信息中获取到文件描述符,并关闭它。
需要注意的是,在实际使用中,我们还需要对msg.msg_control中的控制信息进行合法性检查,以避免安全漏洞。