tcp网络编程性能优化点

1、修改select recv缓存区大小

    char tmp[1024];
    AMS_WARN("  --clear socket buff...");
    while(1) {        
        ret = select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
        if(ret == 0) {
            break;
        }
        if (recv(client_fd, tmp, 1024, 0 ) != 1024) {
            break;
        }
    }

建议将tmp大小设置为65535,业务代码逻辑可能需要修改。

2、应用程序级修改缓冲区大小

  • 可以在程序中动态地修改(通过setsockopt系统调用)持有的有效Socket的读写缓冲区大小。

  • 参考代码:

    int ret, sock, sock_buf_size;
    sock = socket( AF_INET, SOCK_STREAM, 0 );
    sock_buf_size = BDP;
    ret = setsockopt( sock, SOL_SOCKET, SO_SNDBUF,
                       (char *)&sock_buf_size, sizeof(sock_buf_size) );
    ret = setsockopt( sock, SOL_SOCKET, SO_RCVBUF,
                       (char *)&sock_buf_size, sizeof(sock_buf_size) );
    

    参考文章:Socket缓冲区大小修改与系统设置

3、端口复用

  • 端口复用reuseport原因:单线程listener,在处理高速率海量连接时,会成为瓶颈;

  • 参考文章:Linux3.9中 SO_REUSEPORT 对高并发服务器的改进

  • 配置修改参考: c/c++:端口复用(setsockopt)、io多路转接(select、 poll、epoll)

    调用setsockopt函数:

    #include  
    #include 
    // 设置套接字属性
    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    	参数:
    	- sockfd: 要操作的文件描述符
    	- level: 级别 -> SOL_SOCKET (端口复用的级别)
            - optname: 端口复用的级别(二选一,随便选,对于端口复用都可以)
            	- SO_REUSEADDR
            	- SO_REUSEPORT
            - optval: 端口复用-> 对应的是整形数
            	- 1: 可以复用
            	- 0: 不能复用
            - optlen: optval参数对应的内存大小
     
    // 设置端口复用, 设置的时机: 服务器绑定端口之前, 设置端口复用。例如:
    setsockopt();
    bind();
    

4、内核参数修改

  • 具体参数含义参考文章:Linux内核 TCP/IP、Socket参数调优

  • 修改linux系统文件句柄限制

    执行echo -e "* soft nofile 40960 \n* hard nofile 65536" >> /etc/security/limits.conf 修改linux系统文件句柄限制

     cat /etc/security/limits.conf
    将在limits.conf中添加以下参数
    * soft   nofile   40960
    * hard   nofile   65536
    

    参数

    * 代表所有用户有效
    soft:软限制,超过会报warn
    hard:实际限制
    nofile:文件句柄参数
    number:最大文件句柄数
    

    使用ulimit -n 查看open files 参数是否改变。

  • TCP/IP内核参数优化,建议参数如下:

    /etc/sysctl.conf添加以下代码,重启后永久生效

    net.core.rmem_default = 8388608
    net.core.rmem_max = 16777216
    net.core.wmem_default = 8388608
    net.core.wmem_max = 16777216
    net.core.netdev_max_backlog = 2000
    net.core.somaxconn = 2048
    net.core.optmem_max = 81920
    net.ipv4.tcp_mem = 131072  262144  524288
    net.ipv4.tcp_rmem = 8760  8388608  16777216
    net.ipv4.tcp_wmem = 8760  8388608  16777216
    net.ipv4.tcp_keepalive_time = 60
    net.ipv4.tcp_keepalive_probes = 3
    net.ipv4.tcp_keepalive_intvl = 20
    
    vm.overcommit_memory=1
    

5、设置TcpNodelay和TcpKeepAlive

  • TCPNoDelay和TCPkeepalive都是常用的TCP选项,前者的作用是禁用Nagle 算法6,避免连续发包出现延迟,这对编写低延迟网络服务很重要。后者的作用是定期探查TCP连接是否还存在。一般来说如果有应用层心跳的话,TCPkeepalive不是必需的。

  • TCPNoDelay设置参考代码:

    int sock, flag, ret;
    /* Create new stream socket */
    sock = socket( AF_INET, SOCK_STREAM, 0 );
    /* Disable the Nagle (TCP No Delay) algorithm */
    flag = 1;
    ret = setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) );
    if (ret == -1) {
      printf("Couldn't setsockopt(TCP_NODELAY)\n");
      exit(-1);
    }
    
  • TCPkeepalive内核中设置参考上面第4点,应用程序中设置参考如下:

    int keepAlive = 1; // 开启keepalive属性
    int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 
    int keepInterval = 5; // 探测时发包的时间间隔为5 秒
    int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.
    
    setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
    setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
    setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
    setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
    
  • 见《Linux多线程服务编程》8.9节

6、使用std::vector作为数据缓冲区,具体请参考https://gitee.com/liudegui/finger_server/blob/master/common/Buffer.cpp

你可能感兴趣的:(网络,tcp/ip,c++,性能优化)