zoukankan      html  css  js  c++  java
  • 对引擎收发包的一次思考

     这是对引擎strace 的结果,可以看到引擎在回复报文的时, 频繁的使用write 系统调用,报文内容可以看到就是一个http响应报文,

    正常情况应该是只会调用一次write回复报文,但是实际情况是调用了多次write回复报文,根据http报文的响应头、响应行等依次回复报文。

    所以优化方式: 将多次回复合并成一次

    情况1:将多次的buf 组装成一个buf,然后write出去,所以需要提前使用memcpy 拷贝报文

    那是不是就没有别的办法吗??

    还记得 APUE中有一章节高级I/O讲到readv writev吗!! scatter read /gather write

    现在来看看socket 收发包的接口和他们的区别:

    #include <unistd.h>
     ssize_t read(int fd, void *buf, size_t count);
     ssize_t write(int fd, const void *buf, size_t count);

    可以看到 read write实现的功能就是向fd 中读取数据/写入数据,但是可以看到内核和用户态进程没有任何的交互;同时要求fd 是已经建立连接

    于是就有了recv send,但是flag在设计上存在一个基本问题:它是按值传递的,而不是值-结果参数,因此它只能从进程向内核传递标志,内核不能向进程传递标志。

    send/recv与write/read的作用基本相同,只是多了一个flag参数,当flag参数设置为0时,它们的功能一致了!

    为了解决要求fd 是connected状态问题,于是就有了recvfrom sendto等问题,可以给无连接状态fd 收发包,比如udp socket

    sendto/recvfrom函数地址指针为NULL且地址长度为0时,其作用于send/recv一致;

    sendto用于向socket中写入(读取)数据,如果用在已经建立连接的socket上,需要忽略其地址和地址长度参数,即地址指针设置为NULL,地址长度设置为0;如udp,如果不调用connec建立连接,则需要指定地址参数,如果调用connect建立了连接,则省略地址参数

    为了解决读写缓冲区单一问题;于是就有了readv和writev(分散读,集中写),但是有个缺点就是 内核和进程无法交互相关信息

    recvmsg sendmsg解决了内核和进程之间传递辅助数据的问题

    sendmsg用于向socket文件描述符中写入多个缓冲区的数据,recvmsg用于向多个缓冲区读取socket文件描述符中的数据,发送(接收)前需要构造msghdr消息头

    #include <sys/types.h>
    #include <sys/socket.h>
    
    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    
    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                          const struct sockaddr *dest_addr, socklen_t addrlen);
    
    ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
    /*--------------------------------*/
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                            struct sockaddr *src_addr, socklen_t *addrlen);
    
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

    msghdr参数就比较复杂

    struct msghdr {
        void          *msg_name;            /* protocol address */
        socklen_t     msg_namelen;          /* sieze of protocol address */
        struct iovec  *msg_iov;             /* scatter/gather array */
        int           msg_iovlen;           /* # elements in msg_iov */
        void          *msg_control;         /* ancillary data ( cmsghdr struct) */
        socklen_t     msg_conntrollen;      /* length of ancillary data */
        int           msg_flags;            /* flags returned by recvmsg() */
    }

    msg_name和msg_namelen用于套接字未连接的时候(主要是未连接的UDP套接字),用来指定接收来源或者发送目的的地址。两个成员分别是套接字地址及其大小,类似recvfrom和sendto的第二和第三个参数。对于已连接套接字,则可直接将两个参数设置为NULL和0。而对于recvmsg,msg_name是一个值-结果参数,会返回发送端的套接字地址。
    msg_iov和msg_iovlen两个成员用于指定数据缓冲区数组,即iovec结构数组。iovec结构如下

    #include <sys/uio.h>
    struct iovec {
        void    *iov_base;      /* starting address of buffer */
        size_t  iov_len;        /* size of buffer */
    }

    其中iov_base就是一个缓冲区元素,事实上也是一个数组,而iov_len则是指定该数据的大小。也就是说,缓冲区是一个二维数组,并且每一维长度不是固定的

    所以:为了解决引擎多次系统调用的问题,目前最简单的办法是使用writev 接口发送数据,但是需要考虑writev 发送1000bytes的数据,实际上只发送了500, 剩下500bytes在下次发送问题,

    也就是writev的异常逻辑处理,也就每次writev后需要找出那部分数据还没有发送,然后重新监听fd到writeable 然后继续发送,已经发送完毕的数据其缓存可以释放掉

  • 相关阅读:
    python基础之网络及网络编程
    三:C语言基础复习--指针(指针常量,常量指针,指针数组,数组指针)
    一:C语言基础复习--控制结构
    将python源文件打包成exe文件
    Python ImportError: DLL load failed: %1 不是有效的 Win32 应用程序。
    解决 java “错误:编码GBK 的不可映射字符”
    Fiddler小技巧之添加请求耗时Timetaken/qIP/请求方法Method列
    Python中如何打印空行
    Thinkpad F2不能进行重命名的问题
    Notepad++安装xml插件
  • 原文地址:https://www.cnblogs.com/codestack/p/13892332.html
Copyright © 2011-2022 走看看