套接字编程注意事项

        • 信号
          • SIGCHLD信号和僵尸进程
          • SIGPIPE信号
        • 被中断的系统调用
        • 惊群问题
        • read
        • 内存对齐和字节序
        • TCP粘包问题
          • 处理粘包

信号

SIGCHLD信号和僵尸进程

多进程服务器需要处理SIGCHLD信号,SIGCHLD默认会导致进程终止;不回收子进程会导致僵尸进程太多;循环是因为信号不会累计,避免多个子进程同时结束,而只回收一个子进程。
- 建议做法:捕捉信号,并循环调用waitpid直到返回值小于0。

SIGPIPE信号

当一个进程向某个已收到RST的套接字执行写操作时,内核向该进程发送一个SIGPIPE信号。并且,不论该进程是捕获了该信号还是从其信号处理函数返回,写操作都将返回EPIPE
- 可能产生RST的原因:向某个已关闭的套接字写。
- 可能产生SIGPIPE的原因:向某个已关闭的套接字执行第一次写操作,会受到RST;第二次就会产生SIGPIPE信号,同时返回EPIPE错误。

被中断的系统调用

慢系统调用包括:read、readv、write、writev、wait、waitpid、epoll_wait、epoll_ctl、connect等。这些系统调用在信号处理函数返回后,会阐述EINTR错误。虽然有些系统调用会重启,但是系统实现不同最好都处理。
- 建议做法:判断errno == EINTR再重复继续执行。

惊群问题

父进程epoll_create得到epoll_fd,fork()后子进程执行epoll_Wait,当连接请求过来的时候,只有一个子进程会accept成功,其他子进程返回EAGAIN
解决方案:

  1. accept前加锁;
  2. Linux内核已经解决了;

read

如果在一个套接字上完成一个 read 操作并得到一个为 0 的返回值,这表明远程套接字端的对等层调用了 close API 方法。
同样,可以用 write API 函数来探测对等套接字的闭包。在这种情况下,接收 SIGPIPE 信号,或如果该信号阻塞,write 函数将返回 -1 并设置 errno 为 EPIPE。

内存对齐和字节序

套接字适合发送无结构二进制字节流或 ASCII 数据流,发送结构化的数据时,内存对齐格式不相同就会造成数据无法解析。大小端的问题也一样,不过可以都转化为网络字节序(网络字节序是大端)。
问题:

  • 不同的实现已不同的方式存储二进制数。(大小端)
  • 不同的实现在存储相同的C数据类型上可能存在差异。(32位和64位)
  • 不同的实现给结构打包的方式存在差异。(对齐方式)
    解决方案:
  • 把所有的数值数据作为文本串来发送,要求主机有相同的字符集
  • 显示定义所支持数据类型的二进制格式(位数,大小端,对齐方式)。
  • 可以使用json,或者protobuf

TCP粘包问题

TCP一次会收到多个包,造成粘包等;
UDP自带帧同步,每次read只会返回一个包;

处理粘包

对于粘包,需要clientserver定制报头,说明长度字段。
服务器端能做的:判断字节,每次取包头长度的字段;多余长度放到缓冲区;不足长度直接放到缓冲区;对于长度异常的字段做处理;前面一个流的错误会影响后面的,所以对于每一个用户,定义一个缓冲区;报文完整才放到交付处理。如果异常直接断开连接,清理缓冲区。
实现效果如下图:

RECV 22 22
RECV 18 88
RECV 22 70
RECV 18 48
RECV 30 30

你可能感兴趣的:(Linux,socket)