高级套接口-(sendmsg和recvmsg)举例

这几天学习网络编程,踩了好多坑,网上这部分资料不多,例子也是抄来抄去。

sendmsg和recvmsg这两个接口是高级套接口,这两个接口支持一般数据的发送和接收,还支持多缓冲区的报文发送和接收(readv和sendv支持多缓冲区发送和接收),还可以在报文中带辅助数据。
还是先上代码

server 端

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

#define DEFAULT_PORT 8080
#define MAXLINE 4096
#define MAX_NAME_LEN 4095
#define MAX_MMSG_NUM 10

int receive_message(int socket_fd, int* socket_num, int* sockets) {
    struct iovec iov[1];
    int32_t utm[MAX_MMSG_NUM];

    char buf[CMSG_SPACE(sizeof(int) * MAX_MMSG_NUM)];
    struct msghdr msg;
    {
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_flags = 0;
        msg.msg_iov = iov;
        //msg.msg_iov = 0;
        msg.msg_iovlen = sizeof(iov) /sizeof(iov[0]);
        //msg.msg_iovlen = 0;
        msg.msg_control = buf;
        //msg.msg_control = NULL;
        msg.msg_controllen = sizeof(buf);
        //msg.msg_controllen = 0;
        msg.msg_iov[0].iov_base = utm;
        msg.msg_iov[0].iov_len = sizeof(utm);
    }

    errno = 0;
    const int n = recvmsg(socket_fd, &msg, MSG_WAITALL);
    std::cout << "receive finish n=" << n << "sizeof(utm)=" << sizeof(utm) << std::endl;
    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
    {
        if (n <= 0) {
            std::cout << "receive message faild, return " << strerror(errno) << std::endl;
            return errno;
        }
        if(0 != (msg.msg_flags & (MSG_TRUNC | MSG_CONFIRM))) {
            std::cout << "receive message truncated, flags=" << msg.msg_flags << std::endl;
            return errno;
        }
    }
    *socket_num = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int32_t);
    int32_t* fd_arr = reinterpret_cast(CMSG_DATA(cmsg));
    for (int32_t i = 0; i < *socket_num; i++) {
        sockets[i] = fd_arr[i];
        std::cout << "the data recv is " << utm[i] <<" " << sockets[i] << std::endl;
    }
    return 0;
}

int main(int argc, char** argv) {
    int socket_fd;
    int connect_fd;
    int num = 0;
    //struct sockaddr_in servaddr;//net conn
    struct sockaddr_un servaddr;//local conn
    char buff[4096];

#if 0
    //init the socket
        std::cout << "create socket error" << std::endl;
    if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        exit(1);
    }

    // init the in struct
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//set the server ip
    servaddr.sin_port = htons(DEFAULT_PORT);//set the listion port
#else
    unlink("server_socket");
    if ((socket_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        std::cout << "create socket error" << std::endl;
        exit(1);
    }

    servaddr.sun_family = AF_UNIX;
    snprintf(servaddr.sun_path, MAX_NAME_LEN, "%s", "server_socket");
#endif
    //bind the struct to the socket
    if ( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1 ) {
        std::cout << "bind socket error: " << strerror(errno) <<std::endl;
        exit(1);
    }

    //listion the socket 10 is the max conn num
    if ( listen(socket_fd, 10) == -1 ) {
        std::cout << "listen error" << std::endl;
    }

    std::cout << "start to listen 8080 port" << std::endl;
    connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL);
    if ( connect_fd == -1 ) {
        std::cout << "accept socket error" << strerror(errno) << std::endl;
    }
#if 0
    while(true) {
        num = recv(connect_fd, buff, MAXLINE, 0);
        if (num < 0) {
            std::cout << "receive error" << strerror(errno) << std::endl;
        }
        buff[num] = '\0';
        std::cout << "receive from client " << buff << std::endl;

        if ( send(connect_fd, "we have received your message", 29, 0) == -1 ) {
            std::cout << "send error" << strerror(errno) << std::endl;
        }
        sleep(3);
    }
#else
    int socket_num = 0;
    int socket_array [MAX_MMSG_NUM];
    receive_message(connect_fd, &socket_num, socket_array);
#endif
}

/* vim: set ts=4 sw=4 sts=4 tw=100 */

client 端

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

#include 

#define DEFAULT_PORT 8080
#define MAXLINE 4096
#define MAX_NAME_LEN 4095
#define MAX_MMSG_NUM 10

//send fd to server with sendmsg
int write_message(int socket_fd, int socket_num, int32_t* socket_array, int32_t* data) {
    struct iovec iov[1];
    char buf[CMSG_SPACE(sizeof(int) * MAX_MMSG_NUM)];
    struct msghdr msg;
    {
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_flags = 0;
        msg.msg_iov = iov;
        //msg.msg_iov = 0;
        msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
        //msg.msg_iovlen = 0;
        msg.msg_control = buf;
        //msg.msg_control = NULL;
        msg.msg_controllen = sizeof(buf);
        //msg.msg_controllen = 0;

        iov[0].iov_base = const_cast(data);
        iov[0].iov_len = sizeof(data[0]) * MAX_MMSG_NUM;

        struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
        cmsg->cmsg_level = SOL_SOCKET;
        cmsg->cmsg_type = SCM_RIGHTS;
        cmsg->cmsg_len = CMSG_LEN(sizeof(int32_t) * socket_num);
        int32_t* fdptr = reinterpret_cast(CMSG_DATA(cmsg));
        for (uint32_t i = 0; i < socket_num; i++) {
            fdptr[i] = static_cast(socket_array[i]);
        }
        msg.msg_controllen = CMSG_SPACE(sizeof(int32_t) * socket_num);
    }

    //send info to client
    while (true) {
        errno = 0;
        const ssize_t n = sendmsg(socket_fd, &msg, 0);
        if (-1 == n) {
            std::cout << "send message error " << strerror(errno) << std::endl;
            if (EINTR == errno) {
                continue;
            }
            return errno;
        }
        std::cout << "sendmsg success n = " << n << std::endl;
        break;
    }
    return 0;
}

int main(int argc, char** argv) {
    int socket_fd;
    int num = 0;
    int rec_len = 0;
    //struct sockaddr_in servaddr;// net conn
    struct sockaddr_un servaddr;//local conn
    char recvline[4096];
    char sendline[4096];
    char buff[MAXLINE];

    // apply socket_fd
#if 0
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd == -1) {
        std::cout << "create socket error" << std::endl;
        exit(1);
    }

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(DEFAULT_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    //transfer ip
#else
    socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (socket_fd == -1) {
        std::cout << "create socket error" << std::endl;
        exit(1);
    }
    servaddr.sun_family = AF_UNIX;
    snprintf(servaddr.sun_path, MAX_NAME_LEN, "%s", "server_socket");
#endif
    int ret = 0;
    //conn
    ret = connect(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    if (ret < 0) {
        std::cout << "connect error " << strerror(errno) << std::endl;
        exit(1);
    }

    std::cout << "send message to server" << std::endl;
#if 0
    std::string msg = "hello server!";
    strcpy(sendline, msg.c_str());
    int time = 3;
    while(time > 0) {
        ret = send(socket_fd, sendline, strlen(msg.c_str()), 0);
        if (ret < 0) {
            std::cout << "send msg error" << strerror(errno) << std::endl;
            continue;
        }
        std::cout << "send again" << std::endl;
        time--;
    }
    rec_len = recv (socket_fd, buff, MAXLINE, 0);
    if (rec_len < 0) {
        std::cout << "recv msg error" << strerror(errno) << std::endl;
    }
    buff[rec_len] = '\0';
    std::cout << "received " << buff << std::endl;
#else
    int32_t array[MAX_MMSG_NUM];
    for (int i = 0; i < MAX_MMSG_NUM; i++) {
        array[i] = socket(AF_UNIX, SOCK_STREAM, 0);
    }

    int32_t data[MAX_MMSG_NUM];
    for (int i = 0; i < MAX_MMSG_NUM; i++) {
        data[i] = i;
        std::cout << "the data adn fd is " << data[i] << " " <<array[i] << std::endl;
    }

    write_message(socket_fd, MAX_MMSG_NUM, array, data);

#endif
    close(socket_fd);
}

/* vim: set ts=4 sw=4 sts=4 tw=100 */

踩的坑

  1. 如果要在报文中添加辅助数据,则socket必须为本地模式(AF_UNIX)
  2. 辅助数据只能是文件描述符,比如例子中的fd。其它数据会导致Bad file descriptor 错误

你可能感兴趣的:(编程)