【计算机网络】网络编程接口 Socket API 解读(11)

Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。

本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。


shutdown(2)

遵循 POSIX.1-2008

1.库

cpp 复制代码
标准 c 库,libc, -lc

2.头文件

cpp 复制代码
<sys/socket.h>

3.接口定义

cpp 复制代码
       int shutdown(int sockfd, int how);

4.接口描述

shutdown() 调用会将 sockfd 指定的套接字上全双工连接上的一端或者两端关闭。如果 how 指定为 SHUT_RD,那么套接字上将不允许接收;如果 how 指定为 SHUT_WR,那么套接字上将不允许发送。如果 how 是 SHUT_RDWR,那么发送和接收都不允许。

5.返回值

成功时返回 0,失败返回 -1 并设置 errno。

6.注意

SHUT_RD、SHUT_WR、SHUT_RDWR 的值分别为 0、1、2,在 glibc-2.1.91 <sys/socket.h> 中定义。

how 的合法性检查会在相关的域代码中进行,但是 Linux 3.7 前并不是所有的域都会进行可用性检查。Linux 域套接字直接会忽略不可用的值,Linux 3.7 后这个问题修复了。

close(2)

遵顼 POSIX.1-2008

1.库

cpp 复制代码
标准 c 库,libc, -lc

2.头文件

cpp 复制代码
<unistd.h>

3.接口定义

cpp 复制代码
       int close(int fd);

4.接口描述

close() 会关闭一个文件描述符,关闭的描述符不再指向任何文件,关闭后可以重新使用该文件描述符。文件上和文件描述符相关的由该进程所有的记录锁(参考 fcntl(2))将会被移除(不管是用哪个文件描述符获得的该锁)。

如果 fd 是最后一个指向底层打开的文件描述的描述符(参考 open(2)),打开文件描述的相关资源都会被释放;如果文件描述符是最后一个指向通过 unlink(2) 移除的文件,那么文件会被删除。

5.返回值

成功时返回 0,失败返回 -1 并设置 errno。

错误列表如下:

EBADF fd 不是一个可用的文件描述符

EINTR close() 被中断打断;参考 signal(7)

EIO I/O 错误发生

ENOSPE、EDQUOT

在 NFS 上,第一次超出可用空间的写不会报告这些错误,而是在接下来的 write(2)、fsync(2) 或者 close(2) 时报告这些错误。可以参考注意部分查看为什么 close() 在报错后不能再次重试。

6.注意

成功关闭文件描述符并不保证数据成功同步到磁盘上,因为内核会使用缓冲区来做延迟写。通常情况下,文件系统不会在文件关闭时同步这些缓冲区到磁盘上。如果我们想确信数据被存储到底层磁盘,使用 fsync(2) 先进行同步。(从这一点上,也依赖磁盘硬件)。

如果文件描述符标记指定了 close-on-exec,那么就可以保证文件在执行 execve(2) 时自动关闭。参考 fcntl(2) 查看详细信息。

多线程进程和 close()

一个进程中如果有其他线程在使用系统调用访问文件描述符,那么通过 close 关闭文件描述符是非常不明智的。因为文件描述符可以重用,一些条件竞争就可能会导致一些意想不到的结果。

此外,我们可以考虑下面两个线程操作同一个文件描述符的场景:

(1)一个线程正在阻塞在文件描述符的系统调用上,比如向一个满了的管道尝试 write(2) 写,或者从一个没有数据的流套接字上 read(2) 读。

(2)另一个线程关闭这个文件描述符

这种情况在不同的系统上的行为是不一样的,一些系统上一旦文件描述符关闭,其他阻塞系统调用就会返回错误。

在 Linux 以及另一个系统上,行为是不同的:阻塞的 I/O 系统调用一直持有底层打开的文件描述,直到 I/O 系统调用结束。(参考 open(2) 关于打开文件描述的讨论。)因此,阻塞系统调用完全可能在 close() 后仍然成功返回。

close() 返回错误处理

编程人员需要检查 close() 的返回值,因为很可能之前 write(2) 的错误只能在最后的 close() 要释放文件描述时才会报告。不检查错误返回值可能会导致一些数据偷偷的丢掉,这种情况在 NFS 和磁盘配额时是常见的。

然而这些错误返回值只能用来诊断(即向应用发出警告:可能仍然有一些等待的 I/O 或者 I/O 失败)以及重播目的(即再写一次文件或者创建备份)。

失败后重试 close() 是不可行的,因为文件描述符可能会被其他线程重用,导致误关闭其他线程文件描述符,这主要是因为内核会首先释放文件描述符,然后再进行一些会返回错误的操作,比如将数据刷新到文件系统或设备。

一些其他系统实现也会再报告错误的情况下关闭文件描述符(除了 EBADF,表示文件描述符不可用)。POSIX.1 目前对这点并没有提出什么,不过可能在下一个主版本会授权这种做法。

我们可以通过在发生错误的 close(2) 后调用 fsysnc(2) 来了解具体是发生了什么 I/O 错误。

EINTR 错误有点特殊,关于这个错误,POSIX.1-2008 的说法是:

cpp 复制代码
如果 close() 被能被捕捉到的信号中断,它应该返回 -1 并将 errno 设置为 EINTR,fildes 状态未指定

这就允许 Linux 以及其他实现上可以像处理其他错误一样处理这个错误,文件描述符保证被关闭了。然而,也允许返回 EINTR 后仍然保持文件描述符是打开的。(HP-UX 的 close() 就是这样的。)调用者必须重新使用 close() 来关闭文件描述符,避免文件描述符泄露。这种实现的不同导致了程序移植性问题,因为其他实现在失败后不允许重新 close()。下一个 POSIX.1 的主版本也会解决这个问题。

相关推荐
心平气和️2 小时前
HTTP 配置与应用(不同网段)
网络·网络协议·计算机网络·http
心平气和️2 小时前
HTTP 配置与应用(局域网)
网络·计算机网络·http·智能路由器
hunter2062062 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
不会飞的小龙人2 小时前
Docker Compose创建镜像服务
linux·运维·docker·容器·镜像
不会飞的小龙人2 小时前
Docker基础安装与使用
linux·运维·docker·容器
白粥行4 小时前
linux-ubuntu学习笔记碎记
linux·ubuntu
jerry-894 小时前
通过配置核查,CentOS操作系统当前无多余的、过期的账户;但CentOS操作系统存在共享账户r***t
linux
涛ing5 小时前
21. C语言 `typedef`:类型重命名
linux·c语言·开发语言·c++·vscode·算法·visual studio
0xfather5 小时前
在Debian系统中安装Debian(Linux版PE装机)
linux·服务器·debian
workingman_li5 小时前
centos虚拟机异常关闭,导致数据出现问题
linux·运维·centos