精华内容
下载资源
问答
  • linux内核Tcp性能调优

    千次阅读 2018-08-12 14:35:45
    Tcp/ip协议对网络编程的重要性,进行过网络开发的人员都知道,我们所编写的网络程序除了硬件,结构等限制,通过修改Tcp/ip内核参数也能得到很大的性能提升, 下面就列举一些Tcp/ip内核参数,解释它们的含义并通过...

    前言:

    Tcp/ip协议对网络编程的重要性,进行过网络开发的人员都知道,我们所编写的网络程序除了硬件,结构等限制,通过修改Tcp/ip内核参数也能得到很大的性能提升,

    下面就列举一些Tcp/ip内核参数,解释它们的含义并通过修改来它们来优化我们的网络程序,主要是针对高并发情况。

    这里网络程序主要指的是服务器端

    1. fs.file-max

    最大可以打开的文件描述符数量,注意是整个系统。

    在服务器中,我们知道每创建一个连接,系统就会打开一个文件描述符,所以,文件描述符打开的最大数量也决定了我们的最大连接数

    select在高并发情况下被取代的原因也是文件描述符打开的最大值,虽然它可以修改但一般不建议这么做,详情可见unp select部分。

    2.net.ipv4.tcp_max_syn_backlog

    Tcp syn队列的最大长度,在进行系统调用connect时会发生Tcp的三次握手,server内核会为Tcp维护两个队列,Syn队列和Accept队列,Syn队列是指存放完成第一次握手的连接,Accept队列是存放完成整个Tcp三次握手的连接,修改net.ipv4.tcp_max_syn_backlog使之增大可以接受更多的网络连接。

    注意此参数过大可能遭遇到Syn flood攻击,即对方发送多个Syn报文端填充满Syn队列,使server无法继续接受其他连接

    可参考此文http://tech.uc.cn/?p=1790

    我们看下 man 手册上是如何说的:

    The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for com‐ pletely established sockets waiting to be accepted, instead of the number of incomplete connection requests. The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog. When syncookies are enabled there is no logical maximum length and this setting is ignored. See tcp(7) for more information. If the backlog argument is greater than the value in /proc/sys/net/core/somaxconn, then it is silently truncated to that value; the default value in this file is 128. In kernels before 2.4.25, this limit was a hard coded value, SOMAXCONN, with the value 128.

    自 Linux 内核 2.2 版本以后,backlog 为已完成连接队列的最大值,未完成连接队列大小以 /proc/sys/net/ipv4/tcp_max_syn_backlog 确定,但是已连接队列大小受 SOMAXCONN 限制,为 min(backlog, SOMAXCONN)

    3.net.ipv4.tcp_syncookies

    修改此参数可以有效的防范上面所说的syn flood攻击

    原理:在Tcp服务器收到Tcp Syn包并返回Tcp Syn+ack包时,不专门分配一个数据区,而是根据这个Syn包计算出一个cookie值。在收到Tcp ack包时,Tcp服务器在根据那个cookie值检查这个Tcp ack包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。

    默认为0,1表示开启

    4.net.ipv4.tcp_keepalive_time

    Tcp keepalive心跳包机制,用于检测连接是否已断开,我们可以修改默认时间来间断心跳包发送的频率。

    keepalive一般是服务器对客户端进行发送查看客户端是否在线,因为服务器为客户端分配一定的资源,但是Tcp 的keepalive机制很有争议,因为它们可耗费一定的带宽。

    Tcp keepalive详情见Tcp/ip详解卷1 第23章

    5.net.ipv4.tcp_tw_reuse

    我的上一篇文章中写到了time_wait状态,大量处于time_wait状态是很浪费资源的,它们占用server的描述符等。

    修改此参数,允许重用处于time_wait的socket。

    默认为0,1表示开启

    6.net.ipv4.tcp_tw_recycle

    也是针对time_wait状态的,该参数表示快速回收处于time_wait的socket。

    默认为0,1表示开启

    7.net.ipv4.tcp_fin_timeout

    修改time_wait状的存在时间,默认的2MSL

    注意:time_wait存在且生存时间为2MSL是有原因的,见我上一篇博客为什么会有time_wait状态的存在,所以修改它有一定的风险,还是根据具体的情况来分析。

    8.net.ipv4.tcp_max_tw_buckets

    所允许存在time_wait状态的最大数值,超过则立刻被清楚并且警告。

    9.net.ipv4.ip_local_port_range

    表示对外连接的端口范围。

    10.somaxconn

    前面说了Syn队列的最大长度限制,somaxconn参数决定Accept队列长度,在listen函数调用时backlog参数即决定Accept队列的长度,该参数太小也会限制最大并发连接数,因为同一时间完成3次握手的连接数量太小,server处理连接速度也就越慢。服务器端调用accept函数实际上就是从已连接Accept队列中取走完成三次握手的连接。

    Accept队列和Syn队列是listen函数完成创建维护的。

    /proc/sys/net/core/somaxconn修改

    上面每一个参数其实都够写一篇文章来分析了,这里我只是概述下部分参数,注意在修改Tcp参数时我们一定要根据自己的实际需求以及测试结果来决定。

     

    问题描述

    场景:JAVA的client和server,使用socket通信。server使用NIO。

    1.间歇性得出现client向server建立连接三次握手已经完成,但server的selector没有响应到这连接。

    2.出问题的时间点,会同时有很多连接出现这个问题。

    3.selector没有销毁重建,一直用的都是一个。

    4.程序刚启动的时候必会出现一些,之后会间歇性出现。

     

    分析问题

    正常TCP建连接三次握手过程:

     

    第一步:client 发送 syn 到server 发起握手;

    第二步:server 收到 syn后回复syn+ack给client;

    第三步:client 收到syn+ack后,回复server一个ack表示收到了server的syn+ack(此时client的56911端口的连接已经是established)。

    从问题的描述来看,有点像TCP建连接的时候全连接队列(accept队列,后面具体讲)满了,尤其是症状2、4. 为了证明是这个原因,马上通过 netstat -s | egrep "listen" 去看队列的溢出统计数据:    

    反复看了几次之后发现这个overflowed 一直在增加,那么可以明确的是server上全连接队列一定溢出了。

    接着查看溢出后,OS怎么处理:

    tcp_abort_on_overflow 为0表示如果三次握手第三步的时候全连接队列满了那么server扔掉client 发过来的ack(在server端认为连接还没建立起来)

    为了证明客户端应用代码的异常跟全连接队列满有关系,我先把tcp_abort_on_overflow修改成 1,1表示第三步的时候如果全连接队列满了,server发送一个reset包给client,表示废掉这个握手过程和这个连接(本来在server端这个连接就还没建立起来)。

    接着测试,这时在客户端异常中可以看到很多connection reset by peer的错误,到此证明客户端错误是这个原因导致的(逻辑严谨、快速证明问题的关键点所在)。

    于是开发同学翻看java 源代码发现socket 默认的backlog(这个值控制全连接队列的大小,后面再详述)是50,于是改大重新跑,经过12个小时以上的压测,这个错误一次都没出现了,同时观察到 overflowed 也不再增加了。

    到此问题解决,简单来说TCP三次握手后有个accept队列,进到这个队列才能从Listen变成accept,默认backlog 值是50,很容易就满了。满了之后握手第三步的时候server就忽略了client发过来的ack包(隔一段时间server重发握手第二步的syn+ack包给client),如果这个连接一直排不上队就异常了。

    但是不能只是满足问题的解决,而是要去复盘解决过程,中间涉及到了哪些知识点是我所缺失或者理解不到位的;这个问题除了上面的异常信息表现出来之外,还有没有更明确地指征来查看和确认这个问题。

    深入理解TCP握手过程中建连接的流程和队列

    如上图所示,这里有两个队列:syns queue(半连接队列);accept queue(全连接队列)。

    三次握手中,在第一步server收到client的syn后,把这个连接信息放到半连接队列中,同时回复syn+ack给client(第二步);

    第三步的时候server收到client的ack,如果这时全连接队列没满,那么从半连接队列拿出这个连接的信息放入到全连接队列中,否则按tcp_abort_on_overflow指示的执行。

    这时如果全连接队列满了并且tcp_abort_on_overflow是0的话,server过一段时间再次发送syn+ack给client(也就是重新走握手的第二步),如果client超时等待比较短,client就很容易异常了。

    在我们的os中retry 第二步的默认次数是2(centos默认是5次):

     

    如果TCP连接队列溢出,有哪些指标可以看呢?

    上述解决过程有点绕,听起来懵,那么下次再出现类似问题有什么更快更明确的手段来确认这个问题呢?(通过具体的、感性的东西来强化我们对知识点的理解和吸收。)

    netstat -s

    比如上面看到的 667399 times ,表示全连接队列溢出的次数,隔几秒钟执行下,如果这个数字一直在增加的话肯定全连接队列偶尔满了。

    ss 命令

    上面看到的第二列Send-Q 值是50,表示第三列的listen端口上的全连接队列最大为50,第一列Recv-Q为全连接队列当前使用了多少。

    全连接队列的大小取决于:min(backlog, somaxconn) . backlog是在socket创建的时候传入的,somaxconn是一个os级别的系统参数。

    这个时候可以跟我们的代码建立联系了,比如Java创建ServerSocket的时候会让你传入backlog的值:

    半连接队列的大小取决于:max(64,  /proc/sys/net/ipv4/tcp_max_syn_backlog),不同版本的os会有些差异。

    我们写代码的时候从来没有想过这个backlog或者说大多时候就没给他值(那么默认就是50),直接忽视了他,首先这是一个知识点的盲点;其次也许哪天你在哪篇文章中看到了这个参数,当时有点印象,但是过一阵子就忘了,这是知识之间没有建立连接,不是体系化的。但是如果你跟我一样首先经历了这个问题的痛苦,然后在压力和痛苦的驱动自己去找为什么,同时能够把为什么从代码层推理理解到OS层,那么这个知识点你才算是比较好地掌握了,也会成为你的知识体系在TCP或者性能方面成长自我生长的一个有力抓手。

    netstat 命令

    netstat跟ss命令一样也能看到Send-Q、Recv-Q这些状态信息,不过如果这个连接不是Listen状态的话,Recv-Q就是指收到的数据还在缓存中,还没被进程读取,这个值就是还没被进程读取的 bytes;而 Send 则是发送队列中没有被远程主机确认的 bytes 数。

     

    netstat -tn 看到的 Recv-Q 跟全连接半连接没有关系,这里特意拿出来说一下是因为容易跟 ss -lnt 的 Recv-Q 搞混淆,顺便建立知识体系,巩固相关知识点 。

    比如如下netstat -t 看到的Recv-Q有大量数据堆积,那么一般是CPU处理不过来导致的:

    实践验证一下上面的理解

    把java中backlog改成10(越小越容易溢出),继续跑压力,这个时候client又开始报异常了,然后在server上通过 ss 命令观察到:

    按照前面的理解,这个时候我们能看到3306这个端口上的服务全连接队列最大是10,但是现在有11个在队列中和等待进队列的,肯定有一个连接进不去队列要overflow掉,同时也确实能看到overflow的值在不断地增大。

    Tomcat和Nginx中的Accept队列参数

    Tomcat默认短连接,backlog(Tomcat里面的术语是Accept count)Ali-tomcat默认是200, Apache Tomcat默认100。

    Nginx默认是511

    因为Nginx是多进程模式,所以看到了多个8085,也就是多个进程都监听同一个端口以尽量避免上下文切换来提升性能   

    总结

    全连接队列、半连接队列溢出这种问题很容易被忽视,但是又很关键,特别是对于一些短连接应用(比如Nginx、PHP,当然他们也是支持长连接的)更容易爆发。 一旦溢出,从cpu、线程状态看起来都比较正常,但是压力上不去,在client看来rt也比较高(rt=网络+排队+真正服务时间),但是从server日志记录的真正服务时间来看rt又很短。

    jdk、netty等一些框架默认backlog比较小,可能有些情况下导致性能上不去。

    希望通过本文能够帮大家理解TCP连接过程中的半连接队列和全连接队列的概念、原理和作用,更关键的是有哪些指标可以明确看到这些问题(工程效率帮助强化对理论的理解)。

    另外每个具体问题都是最好学习的机会,光看书理解肯定是不够深刻的,请珍惜每个具体问题,碰到后能够把来龙去脉弄清楚,每个问题都是你对具体知识点通关的好机会。

     

    参考原文:

    https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650121835&idx=1&sn=db8ef06ee4ea237c92ffb7fa969b4135&chksm=f36bbb4ac41c325cf49bb7a29fb078e2a65e6b96abc1511d87c0522092847118c7aaf95dc728&mpshare=1&scene=1&srcid=0812GDo6iaiSfjhXahMk87ND#rd

     

    https://blog.csdn.net/wwh578867817/article/details/46707389

    展开全文
  • TCP三次握手在linux内核中的实现   以下基于linux内核2.4.0源码(转自www.yuanma.org/)  以前一直使用的网络通讯的函数都是工作阻塞模式。看connect实现源码时,突然想到tcp/ip的 三次握手内核如何...

    TCP三次握手在linux内核中的实现

     

    以下基于linux内核2.4.0源码(转自www.yuanma.org/)

        以前一直使用的网络通讯的函数都是工作在阻塞模式。在看connect实现源码时,突然想到tcp/ip的
    三次握手在内核如何实现的,尤其是在非阻塞模式下式,涉及到等待对端回送ack包,而本端又要立即返
    回,想来这种实现肯定是遵循某种规则或是将所有的相关函数组合起来。
        查看一些网络通信书籍,可知果然如此。应用编程如果设置为非阻塞模式,则连接时,connect发送
    SYN包后立即返回-EINPROGRESS,表示操作正在处理中;随后应用可以在connect返回后做一些其它的处理,
    最后在select函数中来捕获socket的连接、读写、异常事件以触发相关操作,下面我们看看内核中的相关
    实现:
    一、tcp/ip连接的三次握手过程:
          client    SYN包--->      server
          client    <---ACK包      server
          client    ACK包--->      server
    二、客户端支持
         client发送2个包,一个SYN包,一个对服务器的响应ACK包。      
         client函数调用链:connect-->sys_connect->inet_stream_connect->tcp_connect...
         看inet_stream_connect中实现的部分代码段:
         ...
         switch (sock->state) {
         ...        
                    /*此处调用tcp_connect函数发送SYN包*/
           err = sk->prot->connect(sk, uaddr, addr_len);       
      if (err < 0)  //出错则退出
       goto out;
           sock->state = SS_CONNECTING;

      /* 此处仅设置socket的状态为SS_CONNECTING表示连接状态正在处理;
       * 不同之处在于非阻塞情况下,返回值设置为-EINPROGRESS表示操作正在处理
       * 而阻塞式情况则在获得ACK包后将返回值置为-EALREADY.
       */
      err = -EINPROGRESS;
      break;
          }
         
          timeo = sock_sndtimeo(sk, flags&O_NONBLOCK); //注意,如果此时设置了非阻塞选项,则timeo返回0
            //如果socket对应的sock状态是SYN包已发送或收到SYN包并发送了ACK包,并等待对端发送第三此的ACK包
     if ((1<<sk->state)&(TCPF_SYN_SENT|TCPF_SYN_RECV)) {
      /* 错误返回码err前面已经设置 */
      if (!timeo || !inet_wait_for_connect(sk, timeo))
      /*注意上面所判断的2中情况,1、如果是非阻塞模式,则!timeo为1,则直接跳到out返回-EINPROGRESS结束connect函数
        2、若为阻塞模式,则在inet_wait_for_connect函数中通过schedule_timeout函数放弃cpu控制权睡眠,等待服务器端
        发送ACK响应包后被唤醒继续处理。如果没有异常出现,则置socket状态为SS_CONNECTED,表示连接成功,正确返回
      */
       goto out;
      
      err = sock_intr_errno(timeo);
      if (signal_pending(current)) /*处理未决信号*/
       goto out;
     }
     ...
     sock->state = SS_CONNECTED;
     err = 0;
         out:
     release_sock(sk);
     return err;
     ...
        上面的描述有一个问题:对服务器的响应ACK包是什么时候发送的?对于非阻塞模式,应该是应用处理过程中
    的某个异步时间;对于阻塞模式,则是在inet_wait_for_connect函数中睡眠时处理。
        即网卡在收到对方的ack包后,上传给对应的socket时发送服务器的响应ACK包
        函数调用链为:netif_rx-->net_rx_action-->...(IP层处理)-->tcp_v4_rcv-->tcp_v4_do_rcv-->
          tcp_rcv_state_process-->tcp_rcv_synsent_state_process-->tcp_send_synack-->tcp_transmit_skb...
        发送SYN包后,socket对应的sock的状态变成TCPF_SYN_SENT,网卡收到服务器的ack传到tcp层时,根据TCPF_SYN_SENT
        状态,做相关判断后再发送用于第三次握手的ack包。至此,将socket的状态改为连接建立,即TCP_ESTABLISHED。
        具体的代码大家可以根据我提供的函数调用链查看。
        注意,以TCPF_前缀开头的状态都表示是中间状态,而已TCP_为前缀的状态才是socket的一个相对稳定的状态。     
       
        这里有一个疑问,,根据接收处理源码,先前的SYN包应该发送给服务器的监听socket,而第三次握手似乎应该发送给
        连接(未真正连接,因为三次握手还没完成呢)的socket,这个问题有待进一步确认

    三、服务器端支持
        服务器端此时必须是监听状态,则其函数调用链为:
              netif_rx-->net_rx_action-->...(IP层处理)-->tcp_v4_rcv-->tcp_v4_do_rcv-->
              tcp_rcv_state_process-->tcp_v4_conn_request-->tcp_v4_send_synack...
        在tcp_v4_conn_request,中部分代码如下:     
        ...
        case TCP_LISTEN:
      if(th->ack) /*监听时收到的ack包都丢弃?*/
       return 1;

      if(th->syn) {/*如果是SYN包,则调用tcp_v4_conn_request*/
       if(tp->af_specific->conn_request(sk, skb) < 0)
        return 1;
        ...
       
    四、后记
        这里只是给了一个tcp/ip三次握手原理的实现框架简述,结合相关理论教材和这个流程,仔细阅读
    linux内核对tcp/ip的实现,对自己理解tcp/ip的精髓不无裨益,期待着与大家一起进步。

    展开全文
  • 由于默认的linux内核参数考虑的是最通用场景,这明显不符合用于支持高并发访问的Web服务器的定义,所以需要修改Linux内核参数,是的Nginx可以拥有更高的性能; 优化内核时,可以做的事情很多,不过,我们通常会...
  • 内核参数

    内核参数

    本文总结Linux内核中关于TCP协议相关的内核参数含义及其相关配置。目的是指出可能在某些情况下提高TCP网络性能的潜在内核可调参数,请确保在进行调整之前和之后进行测试以获得可测量的定量结果。

    TCP状态转移图

    在这里插入图片描述
    TCP连接的任意一端,在任一时刻都处于某一状态,当前状态可以通过netstat命令查看。上图中的粗虚线表示典型的服务器端的状态转移图,粗实线表示典型客户端连接的状态转移图。

    建立连接相关选项

    TCP连接建立需要3次握手,对于服务器侧来说,其维护一个内部的SYNC队列,加大队列长度为可以容纳更多等待连接的网络连接数:

    net.ipv4.tcp_max_syn_backlog = 131072
    

    客户端发起SYNC连接,如果超时会进行重传,重传次数会由下列参数进行设置:

    net.ipv4.tcp_syn_retries = 2
    

    当服务器接收到客户端发送的SYNC连接请求报文后,回应SYNC+ACK报文,并等待客户端的ACK确认,如果超时会进行重传,重传次数由下列参数设置:

    net.ipv4.tcp_synack_retries = 2
    

    TCP进行相应次数的重连操作均失败的情况下,将通知应用层。
    预防半连接攻击,SYN-Cookie是一种有效的机制,它的基本原理非常简单,那就是“完成三次握手前不为任何一个连接分配任何资源”,详细可参考RFC4987 TCP SYN Flooding Attacks and Common Mitigations,参考资料1对具体实现原理进行了讲解,可参考阅读。内核提供了参数,可以用于开启或关闭此项功能:

    net.ipv4.tcp_syncookies = 1
    

    关闭连接相关选项

    当客户端发送FIN结束报文,并接收到服务器的ACK确认报文后,客户端将处于FIN_WAIT_2状态,并等待服务器发送结束报文段,才能转移到TIME_WAIT状态,否则它将一直停留在这个状态。客户端处在这个状态时,连接处于半连接状态,并可以继续接收服务器端发送过来的数据。连接停留在FIN_WAIT_2状态的情况可能发生在:客户端执行半关闭后,未等服务器关闭连接就强行退出。此时服务器端连接由内核来接管,被称之为孤儿连接(orphan)。Linux为了防止孤儿连接长时间存留在内核中,定义了两个内核变量:

    net.ipv4.tcp_max_orphans = 262144
    net.ipv4.tcp_fin_timeout = 5
    

    前者定义了内核接管的最大孤儿连接数,后者指定孤儿连接在内核中生存的时间。
    客户端连接在接收到服务器端端结束报文(FIN)之后,并没有直接进入CLOSED状态,而是转移到TIME_WAIT状态。在这个状态,客户端连接要等待2MSL(Maximum Segment Life, 报文段最大生存时间)长时间,才能完全关闭,标准RFC1122推荐为2分钟。

    net.ipv4.tcp_max_tw_buckets = 1200000
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_tw_reuse = 1
    

    其中tcp_max_tw_buckets表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息;tcp_tw_recycle表示是否开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;tcp_tw_reuse表示是否允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭。

    高性能扩展选项

    RFC7323 TCP Extensions for High Performance文档中详细讨论了为提高性能而对TCP选项的扩展,详细内容可以参考该文档。

    net.ipv4.tcp_sack

    启用或关闭选择确认(Selective Acknowledgement, SACK)选项。TCP通信时,如果某个TCP报文段丢失,则TCP模块会重传被确认的TCP报文段后续的所有报文段。这样已经正确传输的TCP报文段也可能重复发送,从而降低TCP的性能。SACK技术正是为改善这种情况而产生的,它使TCP模块只重新发送丢失的TCP报文段,不用发送所有未被确认的TCP报文段。选择性确认项用于在连接初始化时,表示是否支持SACK功能。

    net.ipv4.tcp_timestamps

    启用或关闭时间戳选项。该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time)RTT的方法,从而为TCP流量控制提供重要的信息。

    net.ipv4.tcp_window_scaling

    启用或关闭窗口扩大因子选项。在TCP的头部中,接收通告窗口大小时用16位表示,故最大为65535字节,但实际上TCP模块允许的接收通告窗口大小远不止这个数(提高TCP通信的吞吐量)。窗口扩大因子解决了这个问题。假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,则TCP报文段段实际接收通告窗口是N乘于2的M次方。M的取值范围为0~14。

    配置

    确保下列选项都被配置为缺省值1,打开相关的配置选项。

    sysctl net.ipv4.tcp_window_scaling
    sysctl net.ipv4.tcp_timestamps
    sysctl net.ipv4.tcp_sack
    

    缓存

    使用setsockopt()增加TCP max缓冲区大小(32MB):

    net.core.rmem_max = 33554432
    net.core.wmem_max = 33554432
    

    要增加Linux自动调整TCP缓冲区限制要使用的最小,默认和最大字节数,1GE的最大值为16MB,10GE的最大值为32M或54M:

    net.ipv4.tcp_rmem = 4096 87380 33554432
    net.ipv4.tcp_wmem = 4096 65536 33554432
    

    net.ipv4.tcp_mem参数用于配置系统对TCP内存配置:
    TCP Autotuning setting. “The tcp_mem variable defines how the TCP stack should behave when it comes to memory usage. … The first value specified in the tcp_mem variable tells the kernel the low threshold. Below this point, the TCP stack do not bother at all about putting any pressure on the memory usage by different TCP sockets. … The second value tells the kernel at which point to start pressuring memory usage down. … The final value tells the kernel how many memory pages it may use maximally. If this value is reached, TCP streams and packets start getting dropped until we reach a lower memory usage again. This value includes all TCP sockets currently in use.”

    tcp_mem有3个参数,单位为页(4096字节):low, pressure, high

    low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
    pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。
    high:允许所有tcp sockets用于排队缓冲数据报的页面量,当内存占用超过此值,系统拒绝分配socket,后台日志输出“TCP: too many of orphaned sockets”。
    一般情况下这些值是在系统启动时根据系统内存数量计算得到的。 根据当前tcp_mem最大内存页面数是1864896,当内存为(1864896*4)/1024K=7284.75M时,系统将无法为新的socket连接分配内存,即TCP连接将被拒绝。

    NIC配置

    NIC上有一个选项txqueuelen可以用来提高TCP性能,对于RTT值比较大的传输路径,可以增大该值:

    [jinguang1@localhost ~]$ ifconfig eth0 txqueuelen 2000
    

    参考资料

    1. SYN-COOKIE概述
    展开全文
  • 使用Linux内核链表实现多客户端连接服务器,里面有三种功能,1、TCP多人连接客户端,服务器可以把信息发给多个用户;2、TCP多人连接客户端,可以把信息发给指定的某个用户;3、服务器接收多个客户端传送过来的文件。
  • 总体需求: 编写tcp文件服务器和客户端。客户端可以上传和下载文件。...help: 显示客户端所有命令和说明, 本地实现即可,help的内容不需要从服务器传回。 list: 显示服务器端可下载文件列表,列表内容需

    总体需求:
    编写tcp文件服务器和客户端。客户端可以上传和下载文件。



    ================================================

    分解需求



    客户端功能描述:

    1)要求支持以下命令:
    help: 显示客户端所有命令和说明, 在本地实现即可,help的内容不需要从服务器传回。
    list: 显示服务器端可下载文件列表,列表内容需要从服务器端传回。
    get <filename>: 根据<filename>下载指定文件,<filename>只包含文件名,如果出现"/"等字符任务是路径,不予支持;下载后的文件存放在当前工作路径下即可。
    put <pathname>: 上传文件 <pathname> 必须为客户端本机的合法路径,客户端搜索到文件后推送给服务器
    quit: 退出客户端
    2)客户端启动后可以反复输入命令,除非用户输入quit才会退出。
    3) 每次命令(list/get/put)建立一个连接,命令执行完毕后关闭该连接。


    服务器端功能:

    1)文件服务器不要求支持并发,同时只支持一个连接,即一个客户端发起的一次命令。一次命令执行完毕后关闭连接并继续等待下一次连接请求。
    2)文件服务器启动后一直执行,除非被人为强制关闭。
    3)文件服务器端需要设定一个目录用于存放所有的文件,该目录路径不支持可配置,且该目录不要求再包含子目录。称其为"文件存放目录"。
    4)对list服务,服务器端从"文件存放目录"下列举出所有的文件名称并发送给客户端。
    5)对get服务,服务器根据用户指定的文件名缺省从"文件存放目录"搜索该文件并推送文件到客户端,推送不会删除原服务器上的文件。
    6)对put服务,服务器将用户推送的文件存储到"文件存放目录",如果存在同名的文件则拒绝。
    7)若执行命令和传输过程中发生错误,关闭当前连接并进入等待下一个连接。


    =================================================
    提示:

    请在编码前仔细设计一个简单的应用层协议规定客户端和服务器端之间命令传输的请求和应答流程和格式。

    二。参考代码:

    1.服务器端参考代码

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<fcntl.h>
    #include<dirent.h>
    #include<errno.h>
    
    #define N 128 
    #define PORT_NUM 8888
    
    typedef struct sockaddr SA;
    
    void ProcessList(int connfd)
    {
    	char buf[N];
    	DIR *mydir;
    	struct dirent *myitem;
    
    	mydir = opendir(".");
    	while((myitem = readdir(mydir)) != NULL){
    		if((strcmp(myitem->d_name, ".") == 0) || (strcmp(myitem->d_name, "..") == 0))
    			continue;
    		strcpy(buf, myitem->d_name);
    		send( connfd, buf, N, 0);
    	}
    	close(connfd);
    
    	return;
    }
    
    void ProcessGet(int connfd, char buf[])
    {
    	int fd,nbyte;
    
    	if(( fd = open(buf+1, O_RDONLY)) < 0){
    		fprintf(stderr, "fail to open %s: %s\n",buf+1,strerror(errno));
    		buf[0] = 'N';
    		send(connfd, buf, N, 0);
    		return;
    	}
    	buf[0] = 'Y';
    	send(connfd, buf, N, 0);
    	while(( nbyte = read( fd, buf, N)) > 0){
    		send(connfd, buf, nbyte, 0);	
    	}
    
    	close(connfd);
    
    	return;
    }
    
    void ProcessPut(int connfd, char buf[])
    {
    	int fd, nbyte;
    
    	if(( fd = open(buf+1, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0){
    		printf("fail to create %s on server\n",buf+1);
    		return;
    	}
    
    	while(( nbyte = recv( connfd, buf, N, 0)) > 0){
    		write(fd, buf, nbyte);	
    	}
    	close(fd);
    
    	return;
    }
    
    int main(int argc, char *argv[])
    {
    	int listenfd, connfd;
    	int optval = 1;
    	char buf[N];
    	struct sockaddr_in server_addr;
    
    	if(( listenfd = socket( AF_INET, SOCK_STREAM, 0)) < 0 ){
    		fprintf(stderr, "fail to socket: %s\n",strerror(errno));
    		exit(-1);
    	}
    
    #ifdef _DEBUG_
    	printf("socket is %d\n", listenfd);
    #endif
    
    	memset(&server_addr, 0, sizeof(server_addr));
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_port = htons(PORT_NUM);
    	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    	
    	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    
    	if( bind( listenfd, (SA *)(&server_addr), sizeof(server_addr)) < 0){
    		perror("fail to bind");
    		exit(-1);
    	}
    	listen( listenfd, 5);
    
    	while(1){
    		if(( connfd = accept(listenfd, NULL, NULL)) < 0){
    			perror("fail to accept");
    			break;
    		}	
    		recv( connfd, buf, N, 0);
    
    		switch(buf[0]){
    			case 'L': ProcessList(connfd);
    				break;
    			case 'G': ProcessGet(connfd, buf);
    				break;
    			case 'P': ProcessPut(connfd, buf);
    				break;
    			default: printf("Input ");
    		}
    		close(connfd);
    	}
    
    	return 0;
    }

    2.客户端参考代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<fcntl.h>
    #include<errno.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    
    #define N 128
    #define PORT_NUM 8888
    
    typedef struct sockaddr SA; 
    
    void PrintHelp()
    {
    	printf("help: display help info\n");
    	printf("list: get file list of server\n");
    	printf("get : get <file>\n");
    	printf("put : put <file>\n");
    	printf("quit: quit the client\n");
    
    	return;
    }
    
    void ProcessList(struct sockaddr_in server_addr)
    {
    	int sockfd, nbyte;
    	char buf[N];
    
    	if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){
    		printf("fail to list\n");
    		return;
    	}
    
    	if( connect(sockfd, (SA *)(&server_addr), sizeof(server_addr)) < 0){
    		printf("fail to connect server\n");
    		goto ERROR_1;
    	}
    
    	strcpy(buf, "L");
    	send(sockfd, buf, N, 0);
    
    	while(( nbyte = recv( sockfd, buf, N, 0)) != 0){
    		printf("%s\n",buf);	
    	}
    
    ERROR_1:
    	close(sockfd);
    
    	return;
    }
    
    void ProcessGet(struct sockaddr_in server_addr, char command[])
    {
    	int sockfd, nbyte, fd;
    	char buf[N];
    
    	if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){
    		printf("fail to get\n");
    		return;
    	}
    
    	if( connect( sockfd, (SA *)(&server_addr), sizeof(server_addr)) < 0){
    		printf("fail to connect server\n");
    		goto ERROR_2;
    	}
    
    	sprintf(buf, "G%s", command+4);
    	send(sockfd, buf, N, 0);
    	recv(sockfd, buf, N, 0);
    
    	if(buf[0] == 'N'){
    		printf("No such file on server\n");
    		goto ERROR_2;
    	}
    
    	if(( fd = open(command+4, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0){
    		printf("fail to create local file %s\n",command+4);
    		goto ERROR_2;
    	}
    
    	while(( nbyte = recv(sockfd, buf, N, 0)) > 0){
    		write(fd, buf, nbyte);	
    	}
    	close(fd);
    
    ERROR_2:
    	close(sockfd);
    
    	return;
    }
    
    void ProcessPut(struct sockaddr_in server_addr, char command[])
    {
    	int sockfd, fd, nbyte;
    	char buf[N];
    
    	if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){
    		printf("fail to get\n");
    		return;
    	}
    	
    	if( connect(sockfd,(SA *)(&server_addr),sizeof(server_addr)) < 0){
    		printf("fail to connect server\n");
    		goto ERROR_3;
    	}
    
    	if((fd = open(command+4, O_RDONLY)) < 0){
    		printf("fail to open %s\n",command+4);
    		goto ERROR_3;
    	}
    	sprintf(buf, "P%s", command+4);
    	send(sockfd, buf, N, 0);
    
    	while(( nbyte = read(fd, buf, N)) > 0){
    		send(sockfd, buf, nbyte, 0);	
    	}
    	close(fd);
    
    ERROR_3:
    	close(sockfd);
    
    	return;
    }
    
    int main(int argc, char *argv[])
    {
    	int sockfd, fd, nbyte;
    	char command[32];
    	struct sockaddr_in server_addr;
    
    	if(argc < 3){
    		printf("Usage: %s <server_ip> <port>\n",argv[0]);
    		exit(-1);
    	}
    
    	if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){
    		fprintf(stderr, "fail to socket: %s\n", strerror(errno));
    		exit(-1);
    	}
    
    #ifdef _DEBUG_
    	printf("socket is %d\n",sockfd);
    #endif
    	
    	memset(&server_addr, 0, sizeof(server_addr));
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_port = htons(atoi(argv[2]));
    	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    	//server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    	while(1){
    		printf("<client>");
    		fgets(command, 32, stdin);
    		command[strlen(command)-1] = '\0';
    		if(strcmp( command, "help") == 0){
    			PrintHelp();	
    		}
    		else if(strcmp( command, "list") == 0){
    			ProcessList(server_addr);	
    		}
    		else if(strncmp( command, "get", 3) == 0){
    			ProcessGet(server_addr, command);	
    		}
    		else if(strncmp( command, "put", 3) == 0){
    			ProcessPut(server_addr, command);
    		}
    		else if(strcmp( command, "quit") == 0){
    			printf("Bye\n");
    			break;
    		}
    		else{
    			printf("Wrong command, 'help' for command list.\n");	
    		}
    	}
    
    	return 0;
    }
    

    3.验证结果(ubuntu14.04)


    展开全文
  • Linux Tcp客户端和服务器端代码实现

    千次阅读 2015-07-30 23:59:22
    1、int socket(int domain, int type, int protocol);函数解析功能:创建socket,也就是创建一个socket... type: 服务类型,主要有SOCK_STREAM流服务(TCP协议使用),SOCK_DGRAM数据报服务(UDP协议使用)  protoc
  • linuxtcp服务器并发连接数限制

    千次阅读 2017-02-22 10:03:59
     在Linux平台上,无论编写客户端程序还是服务端程序,进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个socket句柄,每个...
  • TCP相对的不可靠、无链接、面向数据报的协议UDP,了解UDP客户端与服务器之间通信请戳UDP协议实现服务器与客户端通信 TCP协议建立连接 首先我们通过一个大概的图来了解。 建立连接首先必须是服务器启动,...
  • linux 内核TCP 相关参数解释

    千次阅读 2015-06-10 08:47:38
    对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的...
  • Docker 技术鼻祖系列原文链接:https://blog.csdn.net/u010278923/article/details/102663535今天生产环境遇到了一个奇怪的网络...
  • 本文可以作为《Linux转发性能评估与优化(转发瓶颈分析与解决方案)》的姊妹篇,这两篇文章结合一起,恰好就是整个Linux内核协议栈的一个优化方案。事实上Linux协议栈本来就是面向两个方向的,一个是转发,更多的是...
  • 手把手带你实现一个Linux内核文件系统 简介 作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。Linux...
  • 有关并发处理的技术文档,可做优化的参考。节省成本的情况下,可以修改Linux内核相关TCP参数,来最大的提高服务器性能。当然,最基础的提高负载问题,还是升级服务器硬件了,这是最根本的
  • linux内核TCP相关参数解释【转】

    千次阅读 2016-12-01 09:49:40
     tcp_syn_retries :INTEGER  默认值是5  对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏...
  • #sudo vi /etc/sysctl.conf  $ /proc/sys/net/core/wmem_max 最大socket写buffer,可参考的优化值:873200 ...最大socket读buffer,可参考的优化值:873200 $ /proc/sys/net/ipv4/tcp_wmem TCP写buf
  • 测试以SuSE Linux Enterprise Server v8 (SLES 8)作为测试内核,以IBM pSeries服务器作为测试硬件。使用的是一个特别设计的LTP压力测试场景,使用网络与内存管理的同时并行地运行大范围的内核组件,并测试系统上...
  • 目录: 1、什么是透明代理? 2、透明代理的作用?...4、在实现TCP代理服务器时,遵循以下几点原则 5、应用背景 6、关键技术? 7、如何建立透明代理? 7.1 Tcp透明代理实现的中心思想 7.2 搭建环...
  • Linux下进行TCP简单通信

    千次阅读 2018-05-09 23:03:34
    体会TCP与UDP编程的不同,UDP编程:http://blog.csdn.net/yueguanghaidao/article/details/7055985二、实验平台Linux操作系统三、实验内容编写LinuxTCP服务器套接字程序,程序运行时服务器等待客户的连接,一旦...
  • Linux 内核优化-调大TCP最大连接数

    万次阅读 2017-10-19 19:03:20
    1、服务器如何标识tcp连接? 在tcp应用中,server事先某个固定端口监听,client主动发起连接,经过三路握手后建立tcp连接。 确定最大连接数之前,先来看看系统如何标识一个tcp连接。系统用一个4四元组来...
  • 对于菜鸟接触linux,网络是由浅入深的步步提高深化的源码解析笔记! 其中不乏老鸟也需要深入的技术! (内附赠送linux内核分析图和linux文件解析表) 希望大家互相交流 共同进步!! 拜交好友!
  • Linux内核调优

    千次阅读 2020-03-15 16:58:28
    文章目录一、优化文件打开数和打开系统进程数1、打开文件最大数临时设置方法:2、文件描述符二、TCP的三次握手和四次断开三、内核参数优化1、BAT企业生产环境内核完整参数:2、Linux内核常见参数详解3、Linux内核...
  • TCP服务器IO模型之非阻塞轮询,使用内核链表,单进程实现
  • 通过epoll实现tcp并发回执服务器(客户端给服务器发啥,服务器就给客户端回啥) 代码如下: #include #include #include #include #include #include #include #include #include #include #include #...
  • tid=15 简介 作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中...Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。...
  • Linux服务器内核参数优化

    万次阅读 2018-06-17 00:56:59
    所谓Linux服务器内核参数优化(适合Apache、Nginx、Squid等多种web应用,特殊的业务有可能需要做略微调整),主要是指在Linux系统中针对业务服务应用而进行的系统内核参数调整,优化并无一定的标准。下面是生产环境...
  • linux内核参数

    2015-09-02 22:56:47
    在服务器硬件资源额定有限的情况下,最大的压榨...除此之外,考虑节省成本的情况下,可以修改Linux内核相关TCP参数,来最大的提高服务器性能。当然,最基础的提高负载问题,还是升级服务器硬件了,这是最根本的。
  • Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init进程是进程树的根,所有的进程都直接或者间接...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 110,848
精华内容 44,339
关键字:

在linux内核实现tcp服务器

linux 订阅