zoukankan      html  css  js  c++  java
  • c++ 网络编程(五) LINUX下 socket编程 多种I/O函数 -以及readv和writev函数用法

    原文作者:aircraft

    原文链接:https://www.cnblogs.com/DOMLX/p/9614056.html

    一.多种I/O函数

    前言:之前我们讲的数据传输一般Linux上用write和read,Windows上用send和recv。其实Linux上也可以用send和recv,它与write和read主要区别是它的最后一个参数可以附带一些扩展功能。

    Linux中的send和recv

    • 基础

    ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
    成功返回发送的字节数,失败返回-1
    参数:
    sockfd:套接字文件描述符
    buf:保存传输数据的缓冲地址值
    nbytes:传输的字节数
    flags:扩展信息

    ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
    成功返回接收的字节数(收到EOF返回0),失败返回-1
    参数:
    sockfd:套接字文件描述符
    buf:保存接收数据的缓冲地址值
    nbytes:可接收的最大字节数
    flags:扩展信息

    这两个函数主要讲的就是最后一个参数flags的扩展信息,以前我们都是没有使用它直接传的0,这些扩展信息可选项可以利用位或运算(|)同时传递多个信息。可选项如下:

    MSG_OOB:传输紧急消息(Out-of-band data)
    MSG_PEEK:验证输入缓冲中是否存在接收的数据
    MSG_DONTROUTE:在本地网络中寻找目的地
    MSG_DONTWAIT:非阻塞I/O
    MSG_WAITALL:防止函数返回,直到接收全部请求的字节数

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <signal.h>
    #include <fcntl.h>
    
    #define BUF_SIZE 30
    void error_handling(char *message);
    void urg_handler(int signo);
    
    int acpt_sock;
    int recv_sock;
    
    int main(int argc, const char * argv[]) {
        struct sockaddr_in recv_adr, serv_adr;
        int str_len, state;
        socklen_t serv_adr_sz;
        struct sigaction act;
        char buf[BUF_SIZE];
        if (argc != 2) {
            printf("Usage: %s <port> 
    ", argv[0]);
            exit(1);
        }
    
        //Linux上的信号处理(事件驱动),Windows可以用select函数模拟
        act.sa_handler = urg_handler; //回调函数
        sigemptyset(&act.sa_mask); //初始化0
        act.sa_flags = 0;
    
        acpt_sock = socket(PF_INET, SOCK_STREAM, 0);
        memset(&recv_adr, 0, sizeof(recv_adr));
        recv_adr.sin_family = AF_INET;
        recv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
        recv_adr.sin_port = htons(atoi(argv[1]));
    
        if(bind(acpt_sock, (struct sockaddr *) &recv_adr, sizeof(recv_adr)) == -1)
            error_handling("bind() error");
        if(listen(acpt_sock, 5) == -1)
            error_handling("listen() error");
    
        serv_adr_sz = sizeof(serv_adr);
        recv_sock = accept(acpt_sock, (struct sockaddr *)&serv_adr, &serv_adr_sz);
    
        //将引发信号事件的句柄recv_sock改为getpid()生成的ID,防止多进程中子进程也响应这个事件
        fcntl(recv_sock, F_SETOWN, getpid());
        state = sigaction(SIGURG, &act, 0); //注册信号事件
    
        //一般消息接收
        while ((str_len = recv(recv_sock, buf, sizeof(buf), 0)) != 0)
        {
            if (str_len == -1)
                continue;
            buf[str_len] = 0;
            puts(buf);
    
        }
    
        close(recv_sock);
        close(acpt_sock);
        return 0;
    }
    
    //紧急消息接收
    void urg_handler(int signo)
    {
        int str_len;
        char buf[BUF_SIZE];
        str_len = recv(recv_sock, buf, sizeof(buf) - 1, MSG_OOB);
        buf[str_len] = 0;
        printf("Urgent message : %s 
    ", buf);
    }
    
    void error_handling(char *message)
    {
        fputs(message, stderr);
        fputc('
    ', stderr);
        exit(1);
    }

    客户端代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    
    #define BUF_SIZE 30
    void error_handling(char *message);
    
    int main(int argc, const char * argv[]) {
        int sock;
        struct sockaddr_in recv_adr;
    
        if(argc != 3)
        {
            printf("Usage: %s <IP> <port> 
    ", argv[0]);
            exit(1);
        }
    
        sock = socket(PF_INET, SOCK_STREAM, 0);
        if(sock == -1)
            error_handling("socket() error");
        memset(&recv_adr, 0, sizeof(recv_adr));
        recv_adr.sin_family = AF_INET;
        recv_adr.sin_addr.s_addr = inet_addr(argv[1]);
        recv_adr.sin_port = htons(atoi(argv[2]));
    
        if (connect(sock, (struct sockaddr *) &recv_adr, sizeof(recv_adr)) == -1)
            error_handling("connect() error");
    
        //发送紧急消息,Lunix上是信号处理(事件驱动),Windows上可以select函数模拟
        write(sock, "123", strlen("123"));
        send(sock, "4", strlen("4"), MSG_OOB);
        sleep(2); //os上紧急消息同时发送,下一条会替换上一条,同一时间只能保存一条(可能是一个变量保存的,不是缓冲数组)
        write(sock, "567", strlen("567"));
        send(sock, "890", strlen("890"), MSG_OOB);
    
        close(sock);
        return 0;
    }
    
    void error_handling(char *message)
    {
        fputs(message, stderr);
        fputc('
    ', stderr);
        exit(1);
    }

    注意了这里虽然调用了紧急发送参数 MSG_OOB但是实际上数据并不会提前,发送顺序也不会改变,MSG_OOB的真正意义在于督促数据对象尽快的处理数据

    大概是这样对他说的“嘿哥们,我快要凉凉了,你能不能快点,不然我们只能下辈子见了====”。QAQ。。hhhhhhh

    二.readv和writev函数用法

      • 基础
        这两个函数有助于提高数据通信效率,它们能对数据进行整合传输及发送,适当使用这2个函数可以减少I/O函数的调用次数。

        ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
        成功返回发送的字节数,失败返回-1
        参数:
        filedes:套接字文件描述符,但该函数并不只限于套接字,它和>一般文件操作函数一样可以向其传递文件或标准输出描述符
        iov:iovec结构体数组的地址值(多个缓冲区数据整合一并发送)
        iovcnt:第二个参数iov数组的长度

        struct iovec
        {
        void *iov_base; //缓冲地址
        size_t iov_len; //缓冲大小
        }
        注释:readv正好相反,这里就不再讲了。

    writev使用(多个缓冲数据一次发送)的代码示例:

    #include <stdio.h>
    #include <sys/uio.h>
    
    int main(int argc, const char * argv[]) {
        struct iovec vec[2];
        char buf1[] = "ABCDEFG";
        char buf2[] = "1234567";
        int str_len;
        vec[0].iov_base = buf1;
        vec[0].iov_len = 3;
        vec[1].iov_base = buf2;
        vec[1].iov_len = 4;
    
        str_len = writev(1, vec, 2); //1是系统标准输出文件描述符
        puts("");
        printf("Write bytes: %d 
    ", str_len);
    
        return 0;
    }

    readv使用(一次数据放到多个缓冲中存储)的代码示例:

    #include <stdio.h>
    #include <sys/uio.h>
    #define BUF_SIZE 100
    
    int main(int argc, const char * argv[]) {
        struct iovec vec[2];
        char buf1[BUF_SIZE] = {};
        char buf2[BUF_SIZE] = {};
        int str_len;
    
        vec[0].iov_base = buf1;
        vec[0].iov_len = 5;
        vec[1].iov_base = buf2;
        vec[1].iov_len = BUF_SIZE;
    
        //把数据放到多个缓冲中储存
        str_len = readv(0, vec, 2);  //2是从标准输入接收数据
        printf("Read bytes: %d 
    ", str_len);
        printf("First message: %s 
    ", buf1);
        printf("Second message: %s 
    ", buf2);
    
        return 0;
    }

     最后说一句啦。本网络编程入门系列博客是连载学习的,有兴趣的可以看我博客其他篇。。。。c++ 网络编程课设入门超详细教程 ---目录

    参考博客:https://blog.csdn.net/u010223072/article/details/48261887

    参考书籍《TCP/IP网络编程-尹圣雨》

    若有兴趣交流分享技术,可关注本人公众号,里面会不定期的分享各种编程教程,和共享源码,诸如研究分享关于c/c++,python,前端,后端,opencv,halcon,opengl,机器学习深度学习之类有关于基础编程,图像处理和机器视觉开发的知识

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 校门外的树
    Java实现 蓝桥杯VIP 算法训练 统计单词个数
    Java实现 蓝桥杯VIP 算法训练 统计单词个数
    Java实现 蓝桥杯VIP 算法训练 开心的金明
    Java实现 蓝桥杯VIP 算法训练 开心的金明
    Java实现 蓝桥杯 算法训练 纪念品分组
    Java实现 蓝桥杯 算法训练 纪念品分组
    Java实现 蓝桥杯VIP 算法训练 校门外的树
    Java实现 蓝桥杯VIP 算法训练 统计单词个数
    Java实现 蓝桥杯VIP 算法训练 开心的金明
  • 原文地址:https://www.cnblogs.com/DOMLX/p/9614056.html
Copyright © 2011-2022 走看看