Unix网络编程——chap5

TCP客户/服务器程序

POSIX信号处理

信号:告知某个进程发生了某个事件的通知。由于信号是异步的,也就是进程无法得知信号发生的准确时间;

两个传播路径:

  • 由一个进程发给另外一个进程(或自身);
  • 由内核发给某个进程;

函数声明:

1
2
3
#include <signal.h>

void (*signal(int signo, void (*func)(int)))(int);

POSIX的信号处理:

  1. 一旦安装了信号处理函数,它便一直安装着;
  2. 在一个信号处理函数运行期间,正在被递交的信号是阻塞的;
  3. 如果一个信号在被阻塞期间产生了多次,那么信号不在阻塞之后只会被递交一次,也就是不进行队列排序;

Zombie进程

设置僵死(zombie)进程的一个目的就是维护子进程的信息,以便父进程在未来要使用的时候能获取诸如子进程的ID,进程终止状态和资源利用信息。

由于僵死进程会占用内核空间,因此我们通常利用函数wait或者waitpid来防止其变成僵死进程。

wait和waitpid函数

1
2
pid_t wait(int *staloc);
pid_t waitpid(pid_t pid, int *staloc, int options);

比较:

进程在调用wait之后,如果没有已终止的子进程,它会一直阻塞,直到现有的子进程第一个终止为止。

而对于waitpid来说,它在阻塞非阻塞方面有更多的控制权,首先是pid能指定等待的进程pid,options则是附加选项,设置为WNOHANG则可以告诉内核在没有子进程完成时不要阻塞。

accept返回前连接中止

在三次握手建立之后,客户端的TCP却发来了一个RST。那么一般来说,服务器端的accept函数将会返回一个错误。

服务器进程终止

假设我们在两端建立起连接之后,杀死了服务器端的进程。那么服务器端会向客户端发去一个FIN,而如果此时客户端阻塞于fgets,当我们在客户端输入要发送的内容之后,客户端仍然照常将数据发去服务端。

由于服务端的进程已经关闭了,所以服务端会响应一个RST。然而此时客户端还看不到RST,而是接受FIN,也就是在read中返回0,由于不像预期那样接收到EOF,所以客户端会因为出错而退出。

SIGPIPE

还是上面的那个情况,如果我们不是执行一次写操作,而是在读回数据之前执行两次写操作,那么会引发EPIPE错误。这是因为第一次的写操作会导致RST的接收,而第二次向接收到RST的套接字进行写操作是不允许的,因此返回了错误。

服务器主机崩溃后重启

假设客户端发送数据前,重启处于崩溃状态的服务器,那么由于服务器已经丢失了之前的连接信息,所以会响应一个RST。