TCP 的状态(二) : TCP 常见错误码

TCP 协议错综复杂, 很容易出现错误,错误码非常之多。这里探究一些在编程中比较常见的错误码。tcp 错误码 在不同的操作系统中的值不同。这里取用 Linux 与 Windows 两种,如 connection_refused 在windows  中的值为10054, 在 Linux 中为54 .

eof :

  • 2 : End of file

eof 标志着流的结束。当对端关闭连接(调用了 shutdown 或 close) 后,处于 CLOSE_WAIT 状态。 本端会收到 FIN, ACK 。此时如果本端再试图 read ,则会读到 eof

connection_refused

  • 61 : Connection refused
  • 10061 : No connection could be made because the target machine actively refused it . 由于目标计算机积极拒绝,无法连接

在客户端试图与服务端建立连接的时候发生。一般是服务端没有处于监听状态。 客户端发送发 SYN , 但是收到了 RST, ACK

connection_reset

  • 54 : Connection reset by peer
  • 10054 : An existing connection was forcibly closed by the remote host . 远程主机强迫关闭了一个现有的连接。

对端对处于连接状态的socket 进行了异常断开, 如进程中断等。此时如果本端进行读操作,可能会得到此错误。

阅读更多

TCP 的状态(一): TCP有限状态机 (FSM)

最近一段时间在解决一个网络方面的 BUG,发现自己对 TCP 的状态了解得不够,于是复习了一遍 TCP 的各种状态与状态间的转换,并做了一个整理,以加深自己的理解。

网络上的两台设备想要一起工作,就必须使用相同的网络协议。像 TCP 这种复杂的协议,我们很难简洁地描述其各种确切的操作。所以我们试图使用有限状态机来解释这个复杂的协议。

一. 有限状态机

有限状态机 (FSM : Finite State Machine) 又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。FSM 的四个基本概念如下:

  1. 状态 state: 描述机器在特定时间上处于的 环境(circumstance)或状况(status)
  2. 转换 Transition: 从一种状态到另一种状态的行为(act)
  3. 事件 Event: 导致状态发生变化的事情
  4. 动作 Action: 机器从一种状态转换之前对事件所做的响应

FSM通过解释协议可以处于的所有不同状态、可以在每个状态中发生的事件、针对事件采取的操作以及结果发生的转换来描述协议。协议通常在第一次运行时以特定的开始状态启动。尔后,它遵循一系列步骤使其进入常规操作状态,并根据特定类型的输入或其他情况移动到其他状态。状态机之所以称为有限状态机,是因为只有有限数量的状态。

二. TCP 的操作与状态

对于 TCP 来说,可以使用 FSM 来描述一个连接的生命周期 : 一个 TCP 设备和另一个 TCP 设备之间的每个连接,都从一个空状态(null state) 开始,经过一系列的状态变化,直到建立了(established)连接。然后它将保持这种状态,直到遇到某种事件,它将进行一系列状态直到回到关闭状态。
我们使用三个缩写词来表示状态间转换的三种类型消息,它们对应于 TCP 头的标志。(关于 TCP 头的内容可以参见我之前的博客 《TCP 协议概述》) :

阅读更多

TCP/IP中write/read的行为

read/write 为什么会被阻塞

首先应该知道的是,当write成功返回时,只是将buf中的数据复制到了缓冲区,至于数据什么时候被发往网络,什么时候被对方主机接收,什么时候被对方进程读取,系统调用层面不会给予任何保证和通知。
当kernel的该socket的发送缓冲区已满时,write就会被阻塞。每个socket都拥有自己的send buffer和receive buffer,其大小由系统自动调节。

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

已经发送到网络的数据依然需会在send buffer中暂存,只有当收到对方的ack后,kernel才从buffer中将这一部分清除。接收端将收到的数据暂存在receive buffer中,自动进行确认。但如果socket所在的进程来不及时将数据从receive buffer中取出,最终导致receive buffer填满,由于TCP的滑动窗口和拥塞控制,接收端会阻止发送端向其发送数据。这些控制皆发生在TCP/IP栈中,对应用程序是透明的,应用程序继续发送数据,最终导致send buffer填满,write调用阻塞。
一般来说,由于接收端进程从socket读数据的速度跟不上发送端进程向socket写数据的速度,最终导致发送端write调用阻塞。
而read调用的行为则相对容易理解,从socket的receive buffer中拷贝数据到应用程序的buffer中。read调用阻塞,通常是发送端的数据没有到达。

阅读更多