TCP客户/服务器程序示例

概述

在这里插入图片描述

TCP回射服务器程序:main函数

服务器为监听套接字bind通配IP地址,是在告诉系统,
接受目的地址为任何本地接口的连接

TCP回射服务器程序:str_echo函数

TCP回射客户程序:main函数

TCP回射客户程序:str_cli函数

正常启动

正常终止

POSIX信号处理

信号就是告知某个进程发生了某个事件的通知,有时也称为软件终端
信号通常是异步发生的,也即进程预先不知道信号的准确发生时刻
信号可:
- 由一个进程发给另一个进程[或自身]
- 由内核发给某个进程
SIGCHLD信号由内核在进程终止时,发给其父进程

每个信号有一个与之关联的处置,称为行为
调用sigaction设定一个信号的处理,有三种选择:
- 可提供一个函数,在特定信号发生时调用[信号处理函数],称为捕获信号
SIGKILL/SIGSTOP不可捕获
信号处理函数原型:
void handler(int signo);
- 可把某个信号的处置设定为SIG_IGN来忽略它
SIGKILL和SIGSTOP不能被忽略
- 可把某个信号的处置设定为SIG_DFL来执行默认处理
SIGCHLD/SIGURG默认处理为什么也不做

POSIX明确规定了调用sigaction时的信号语义
signal由于较为久远,不同实现下有不同语义[有的实现可以自动重启被中断的系统调用,有的不可以]

POSIX允许我们指定一组信号,它们在信号处理函数被调用时阻塞.
任何阻塞的信号不能递交给进程.
POSIX保证被捕获信号在其信号处理函数运行期间总是阻塞的.

POSIX信号处理中,SA_RESTART标志,表示由相应信号中断的系统调用将由内核自动重启.
SA_INTERRUPT表示被相应信号中断的系统调用不自动重启.

POSIX信号语义:
符合POSIX的系统上的信号处理总结为:
- 一旦安装了信号处理函数,它便一直安装着
- 在一个信号处理函数运行期间,正被递交的信号是阻塞的
且安装处理函数时传给sigaction的sa_mask信号集中指定的信号也被阻塞
- 如一个信号在被阻塞期间产生了一次或多次,
则该信号被解阻塞后通常只递交一次[Unix信号默认是不排队的]
POSIX实时标准1003.1b定义的一些排队的可靠信号,则解阻塞后递交次数与产生次数一致
- 利用sigprocmask可选择性地阻塞或解阻塞一组信号

处理SIGCHLD信号

设置僵死状态的目的是维护子进程的信息,
以便父进程在以后某个时刻获取
这些信息包括:子进程的进程ID,终止状态,资源利用信息
如一个进程终止,而该进程有子进程处于僵死状态,
则它的所有僵死子进程的父进程ID将被重置为1[init进程]
继承这些子进程的init进程将清理它们[即init进程将wait它们,从而去除它们的僵死状态]

僵死进程占用内核的空间,耗费进程资源.
无论何时,fork子进程都的wait它们,
以防止它们变成僵死进程.

可建立一个俘获SIGCHID信号的信号处理函数,
在函数体中调用wait/waitpid

- 处理被中断的系统调用
适用于慢系统调用的基本规则是:
当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,
该系统调用可能返回一个EINTR错误
有些内核自动重启某些被中断的系统调用
对此建立统一处理的方式是:
对支持SA_RESTART[即使SA_RESTART,特定实现下个别调用可能仍然不自动重启]的,
sigaction中设置此标志

对不支持SA_RESTART,或最保险做法:
判断函数返回值,如为EINTR,重新调用函数[自行编码来实现]
对connect,如该函数返回EINTR,就不能再次调用它.
必须用select来等待连接完成.

wait和waitpid函数

	#include <sys/wait.h>
	pid_t wait(int* statloc);
	pid_t waitpid(pid_t pid, int *statloc, int options);
返回值为已经终止子进程ID号
statloc用于接收终止状态
	
本节示范的是在网络编程时可能会遇到的三种情况:
- fork子进程时,必须捕获SIGCHLD信号
- 当捕获信号时,必须处理被中断的系统调用
- SIGCHLD的信号处理函数必须正确编写,应使用waitpid以免留下僵死进程
捕获处理要考虑到多个SIGCHLD同时产生时,不遗漏问题,
遗漏的SIGCHLD对应的子程序会一直作为僵死进程存在,直到父进程销毁,转到init中被处理

accept返回前连接中止

在这里插入图片描述

如上图,
三路握手完成连接建立后,
客户TCP发了一个RST
服务端此时,该已连接套接字已经处在队列中,等着accept后续取出
服务器实际取出时,
RST也到达了服务器[标志客户TCP已经中止]

如何处理中止的连接依赖于不同的实现,
源自Berkeley的实现完全在内核中处理中止的连接,服务器进程看不到
多数SVR4实现,
返回一个错误给服务器进程,
作为accept的返回结果
POSIX下errno值此时为ECONNABORTED,服务器可忽略它,再次调accept

服务器进程终止

read一个对端已经关闭的套接字,立即返回0
write一个对端已经关闭[已接收对端的FIN并发回ACK]的套接字,可以write,对端收到后会发回一个RST,告知对端在特定端口无监听套接字.

SIGPIPE信号

一个进程向某个已收到RST的套接字执行写操作时,
内核向该进程发送一个SIGPIPE信号,
信号的默认行为是终止进程,
无论该进程是捕获了该信号/忽略该信号,写操作都返回EPIPE错误.

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/109029481