2009年(41)
分类: LINUX
2009-04-13 18:00:43
1 正常建立连接:
·服务器端调用accept,客户端调用connect,客户端发送SYN包;
·服务器收到SYN包,回复ACK,并发送一个SYN包;
·客户端收到SYN包,从connect返回,发送SYN包的ACK。此时从客户端角度看,连接已建立,即进入 ESTABLISHED状态;
·服务器端收到ACK,此时从服务器角度来看,连接建立,进入ESTABLISH状态,然后从accept返 回;
由此可见,客户端进入ESTABLISHED状态要早于服务器端,即客户端在收到“三次握手”的第二个数据包以后就进入ESTABLISHED状态,而服务器要收到第三个数据包。
1.1 服务器端调用accept前,客户端终止连接
·服务器端休眠,客户端调用connect,客户端发送SYN包;
·接下来两个过程同正常情况;
·服务器端收到ACK,此时服务器端进入ESTABLISHED状态,但进程依然在睡眠,没有调用 accept;
·客户端发送一个RST包终止连接;
·唤醒服务器端,调用accept;
对于不同的系统,处理方式不一样。在POSIX标准的处理方式是,从accept返回,返回值为负表示出错,同时errno为ECONNABORTED。
2 通信过程中杀死服务端进程
·客户端与服务器正常通信一段时间后,使用kill杀死服务器端进程;
·和正常退出程序一样,被杀死的服务进程打开的文件描述符将被关闭,这将导致一个FIN发往客户 端;
·客户端发送FIN包的ACK,表示此TCP连接的服务器一端一关闭(即服务器不会产生更多数据,但是 有可能还在接收数据);
·此TCP连接服务器端进入FIN_WAIT2状态,客户端进入CLOSE_WAIT状态;
·此时若客户端继续向服务器端写写入数据,则服务器端会返回一个RST包。若读取数据,则会返回 0,表示收到FIN包(遇到EOF);
·若客户端向服务器端写入数据,然后立即读取,有两种情况,如果RST在执行读取动作前到达,则会返回错误,errno为ECONNREST,否则返回0;
另外,说一下SIGPIPE。如果向一个收到了RST包的socket写入数据,则将会产生一个SIGPIPE信号,此信号的默认操作是终止进程。在进行网路编程的时候要考虑进去。
3 通信过程中服务器崩溃
这里说的崩溃是指突然断电、网络连接突然断开或突然重启等意外情况,服务器正常关机和重启等情况不算。
这种情况比较简单,由于服务器崩溃,因此没有进行socket的关闭操作,即没有发送FIN包。这个时候,客户端并不知道服务器端已经崩溃,如果它向服务器发送数据,则协议栈底层将一直重发数据,直到一定时间后,产生ETIMEDOUT错误。如果中间的路由器提前发现服务器已经崩溃,则会向客户端发送ICMP包,指示“目标不可达”,此时客户端将产生EHOSTUNREACH或ENETUNREACH错误。
注意,这种情况仅在,服务器崩溃后客户端向服务器端写入数据才会发生,如果服务器崩溃后客户端没有发送数据,而是阻塞在读操作中,则将会一直阻塞。为了避免这种情况,可以使用SO_KEEPALIVE选项对socket进行设置。
3.1 服务器崩溃以后重启
在这种情况下,如果服务器重启以后,客户端的写操作还没有返回超时,则数据到达服务器端以后,服务器将产生一个RTS包。后续的步骤参考1、2。
4 关闭服务器
这里的关闭指的是服务器管理员在通信进行中,使用关机命令关闭服务器。
在这种情况下,操作系统会先发SIGTERM给所有进程,然后一段时间后,会向还在运行中的进程发送SIGKILL终止进程。如果服务进程不截获SIGTERM信号,然后在信号处理函数中关闭连接,则将又有SIGKILL来处理,步骤参考2。
相关内容见,unp1第三版。
连载:
chinaunix网友2009-09-13 23:13:59
1,2我比较认同,但3“为什么”我不敢苟同:UDP也是有源目的地址也端口的,只要协议实现正确,应该不至于会“将P1和P2”的数据合并到一块吧,至于为什么要这样做,我也还不太清楚