[转载] 读《UNIX网络编程 卷1:套接字联网API》

原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/html

文章写的很清楚, 适合初学者shell

 

最近看了《UNIX网络编程 卷1:套接字联网API》, 英文名叫Unix Network Programming啦,后来上网查了查, 通常都叫UNP逼格会高一点, 就像APUE同样。 他们的做者都是W. Richard Stevens。 另外,他也是TCP/IP Illustrated的做者。 靠,看完做者简介,简直崇拜得五体投地了。编程

说说这本书中比较让我印象深入的内容吧,我只看了书中关于关于TCP和UDP的主要部分,略过了一些章节,因此可能有一些遗漏。服务器

  • TCP和UDP的工做过程
  • TCP链接在“非正常”状况下的工做情况
  • 各类I/O模型(阻塞/非阻塞/IO复用/信号驱动/异步)
  • 守护进程和inetd的工做原理
  • 服务器程序设计范式
  • 客户端程序设计范式

TCP和UDP的工做过程

UDP的工做过程是简单的,仅仅将用户数据封装到一个IP数据报中发送到目的地而已,而不关注其余方面。网络

TCP倒是一个极其复杂的协议,如下只是冰山一角并发

  • 创建链接的三次握手
    1. 主动方发送(SYN J),进入SYN_SENT状态
    2. 被动方收到(SYN J),并往回发送(SYN K, ACK J+1),进入SYN_RCVD状态
    3. 主动方收到(SYN K, ACK J+1),并往回发送(ACK K+1),进入ESTABLISHED状态
    4. 被动方收到(ACK K+1),也进入ESTABLISHED状态

    以上过程以下图所示:异步

    establish

    注意到在TCP三次握手的过程当中,服务器有这么一条:tcp

    2. 被动方收到(SYN J),并往回发送(SYN K, ACK J+1),进入SYN_RCVD状态设计

    服务器进入SYN_RCVD状态(此时链接称为半开链接)后,应当期待再收到一个ACK。 若是超时未收到客户端的ACK,服务器将重发(SYN K, ACK J+1)。 因而,就有一种叫作SYN Flooding的攻击方式。 攻击者向服务器高速发送(SYN J)(并且能够将SYN分节中的IP地址设为随机数), 而且在随后收到服务器回复的(SYN K, ACK J+1)以后再也不继续回复, 这使得服务器上存在不少的半开链接,这些半开链接通常状况下会持续63秒 (在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,第5次发出后还要等32s都知道第5次也超时了,因此,总共须要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会把断开这个链接)。 它的危害有两方面,一方面天然是占用了服务器的资源;另外一方面是填充了半开链接的队列,使得合法的SYN分节没法排队。unix

    根据SYN Flooding的攻击原理,它的防范主要有如下措施:

    1. 过滤掉最大嫌疑攻击的IP或IP段
    2. tcp_synack_retries设为0,表示回应第二个握手包(SYN K, ACK J+1)给客户端后,若是收不到ACK,不进行重试,加快回收“半开链接”。
    3. tcp_max_syn_backlog参数根据内存状况适当调大,该参数通常指的是维护的半开链接的队列的长度(不一样OS不同)。
    4. 设置tcp_abort_on_overflow选项,处理不过来就直接拒绝掉。
  • 断开链接的四次握手
    1. 主动方发送(FIN M),进入FIN_WAIT_1状态
    2. 被动方收到(FIN M),并往回发送(ACK M+1),进入CLOSE_WAIT状态
    3. 主动方收到(ACK M+1),进入FIN_WAIT_2状态
    4. 被动方发送(FIN N),进入LAST_ACK状态
    5. 主动方收到(FIN N),并往回发送(ACK N+1),进入TIME_WAIT状态
    6. 被动方收到(ACK N+1),进入CLOSED状态
    7. 主动方在TIME_WAIT状态中超时后,进入CLOSED状态

    以上过程以下图所示:

    close

    其实就是2次,只不过TCP是全双工的,因此,发送方和接收方都须要FIN和ACK。 只不过,有一方是被动的,因此看上去就成了所谓的4次挥握手。

    注意到最后有这么一条涉及到TIME_WAIT的状态

    7. 主动方在TIME_WAIT状态中超时后,进入CLOSED状态

    须要通过一个TIME_WAIT超时的状态而不是直接进入CLOSED的缘由有两个,一是确保有足够的时间让对端收到ACK,二是容许老的分节在网络中慢慢的消逝。

    然而,若是系统中存在着大量的短连接,那么大量的TIME_WAIT状态就会成为系统的累赘。网上一些资料提到的tcp_tw_reusetcp_tw_recycle选项来解决这个问题,可是最好仍是别乱用,好像coolshell中有提到过,可能会出不少诡异的问题。还能够调整tcp_max_tw_buckets,当并发的TIME_WAIT过多时,会直接把多的给destory掉,而后在日志里打一个警告。引用一句“其实,TIME_WAIT表示的是你主动断链接,因此,这就是所谓的no zuo, no die”。

TCP链接在“非正常”状况下的工做情况

  • 服务器进程终止

    首先,服务器进程终止(收到SIGKILL信号)。做为进程停止处理的工做之一,该进程全部打开着的描述符将被关闭,这会致使向对端(客户端)发送(FIN N),而客户端则回复(ACK N+1),这就是TCP断开链接的前半部分。

    而后,此时客户端收到(FIN N)并不意味着链接断开(虽然在这个例子中,确实断开了),只是意味着服务器再也不向客户端发送数据了,客户端还能够继续向服务器发送数据。若是此时客户端还继续向服务器发送数据,服务器TCP将发现以前的打开该套接字的进程已终止,因而回应一个RST。客户端在收到这个RST以前的read操做将会返回EOF,在收到这个RST后的read操做会返回ECONNRESET错误,在收到这个RST后的write操做会使当前进程收到SIGPIPE信号。

    以上过程以下图所示:

    server_kill

  • 服务器主机崩溃

    服务器主机崩溃的意思是,没有任何预兆,来不及在网络上发送任何消息,主机就没法工做了。这种状况等价于直接切断网络,或者通俗的说,能够直接拔掉网线来模拟这一状况。

    这时,若是客户端向服务器发送数据,后调用read操做,TCP会一直等待服务器的ACK确认消息,而且不断的超时重传(按照Berkeley的实现,重传12次,共需9分钟),直到到达重传次数,返回ETIMEOUT错误。若是是由中间的路由器断定服务器主机不可达,响应“destination unreasonable”的ICMP消息,将返回EHOSTUNREACHENETUNREACH错误。

  • 服务器主机崩溃后重启

    重启以后的服务器已经丢失了以前的TCP信息,因此即便收到了客户端发来的TCP数据,也会回复RST,日后的状况和“服务器主机崩溃”中提到的相似。

  • 服务器主机关机

    Unix系统关机时,init进程一般会给其余进程发送SIGTERM信号,而后等待10s左右给仍在运行的进程发送SIGKILL信号。因此若是进程不捕获SIGTERM信号,则将由SIGKILL信号终止,和“服务器进程终止”中提到的相似。

再日后的I/O模型、守护进程和inetd的工做原理、服务器程序设计范式、客户端程序设计范式过几天有时间再写吧。

相关文章
相关标签/搜索