UNIX网络编程卷一 笔记 第五章 TCP客户/服务器程序示例

19 篇文章 0 订阅
7 篇文章 0 订阅

1     概述

本章主要实现的程序模型:


2     TCP回射服务器程序

服务器与客户程序约定一个固定的端口,要比5000大,比49152小。

fork后子进程第一件事就是关掉listenfd,父进程的第一件事是关掉connfd。

在等待客户的read调用返回出错后,如果是因为被信号打断,要重新调用read。

3     正常启动

监听套接字处于LISTEN状态。

客户的connect在三路握手的第二个分节就返回了,而服务器要直到第三个分节才返回,即客户的connect返回时服务器还没有accept。

服务器的连接套接字处于ESTABBLISHED状态。

4     正常终止

客户端主动关闭时,客户TCP发送一个FIN给服务器,服务器TCP响应ACK,此时服务器处于CLOSE_WAIT状态,客户处于FIN_WAIT_2状态。

服务器关闭时,服务器发送FIN给客户,客户发送ACK给服务器,连接完全终止,客户进入TIME_WAIT状态。

服务器子进程终止时给父进程发送一个SIGCHLD信号。默认行为是忽略,但我们必须捕捉此信号,清理僵死进程。

5     POSIX信号处理

信号是异步发生的。

SIG_DFL是信号的默认处置,通常是终止进程,有些还会产生core文件,SIGCHLD和SIGURG等信号是忽略。SIGKILL和SIGSTOP不能被捕获。

信号处理程序运行期间要阻塞捕获的信号。

Unix信号默认是不排队的,阻塞期间同一信号产生了多次,阻塞解除后也只能收到一次。

6     处理SIGCHLD信号

多进程下父进程必须捕捉SIGCHLD信号以回收终止状态的子进程资源。可以在信号处理函数中用wait或waitpid。

慢速系统调用会被信号处理函数打断,可能会返回EINTR错误,也可能会自动重启。我们编写捕获信号的程序时,必须对此有所准备。例如,对accept的处理:

connect被打断后就不能被使用了。

7     wait和waitpid函数

通过wait和waitpid都可以获得终止的子进程的pid和状态,waitpid还能指定想等待的pid,options参数允许指定附加选项,最常用的是WNOHANG,在没有终止子进程时不阻塞。

信号阻塞期间如果该信号产生了多次,解除阻塞后只能接收到一次,因此要用waitpid(-1,*,WNOHANG)来循环回收所有结束的子进程。

8     accept返回前连接中止

三路握手完成,连接建立后,客户TCP发送了RST,服务器端在调用accept前收到了这个RST。

如何处理这种中止依赖于不同的实现。BSD的实现是在内核中处理,服务器的accept继续阻塞,SVR4的实现是返回一个错误给进程。

如果返回了一个错误,再次调用accept就行。

9     服务器进程终止

客户与服务器连接成功后,服务器进程如果终止(被动),套接字被关闭,向客户发送FIN,客户响应ACK,此时客户进程可能阻塞在用户输出上,看不到这个RST,此时如果进行write,再read,就会收到预期外的EOF。

本例的问题是:客户不能单纯阻塞在用户与服务器中某个特定源的输入上,而是应该阻塞在其中任一源的输入上。这就是select和poll的目的之一,可同时监视多个文件描述符的状态。

10  SIGPIPE信号

写一个已收到FIN的套接字会收到RST,写一个已收到RST的套接字会产生SIGPIPE信号。

如果没有特殊的事情要做,就将SIGPIPE设置为SIG_IGN,忽略它,并在后面的读写操作中检查返回的错误。如果需要采取特殊措施(如写入日志),就要捕捉该信号。但如果用了多个套接字,信号处理程序无法分辨是哪个套接字出的错。如果需要知道出错的位置,要么不理会该信号,要么从信号处理函数返回后再处理write的EPIPE。

11  服务器主机崩溃

服务器主机崩溃时,已有的网络连接上不发出任何东西(如FIN)。客户对服务器的写操作会持续重传数据,试图接收一个ACK,直到超时。客户随后的readline调用会返回一个错误。

可以对readline设置一个超时。

如果不主动向服务器发送数据也想检测出服务器主机的崩溃,需要SO_KEEPALIVE套接字选项。

12  服务器主机崩溃后重启

服务器主机在崩溃后客户发数据前重启完成,客户不知道服务器主机的崩溃,发送数据,但服务器TCP丢失了之前的连接信息,因此响应RST,客户TCP收到RST时,客户进程正阻塞于readline调用,该调用返回一个错误。

13  服务器主机关机

与服务器进程终止时的情况相同。

14  数据格式

14.1        例子:在客户与服务器之间传递文本串

用sscanf获取文本中的指定数据,再用snprintf把结果转换为文本串。

14.2        例子:在客户与服务器之间传递二进制结构

当这样的客户和服务器程序运行在字节序不一样的或某些类型长度不一致的两个主机上时,工作将失常。

不同的实现在存储二进制数据的格式上(大端小端)、相同类型的长度上、给结构打包的方式(对齐)上都可能不同,因此直接传送二进制结构绝不明智。

解决方法:

1.     所有的数值数据作为文本串来传递。

2.     显式定义所支持数据类型的进进制格式,并以这样的格式在客户与服务器间传递所有数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值