深入理解 Linux socket
扫描二维码
随时随地手机看文章
socket fd 长什么样子?
什么是 socket fd ?粗糙的来讲,就是网络 fd,比如我们最常见的 C/S 客户端服务端的编程模式,就是网络通信的一种方式。撇开底层和协议细节,网络通信和文件读写从接口上有本质区别吗?
其实没啥区别,不就是读过来和写过去嘛,简称 IO 。我们先看一下 socket fd 是什么样子的?随便找了个进程
root@ubuntu:~# ll /proc/1583/fd
total 0
lrwx------ 1 root root 64 Jul 19 12:37 7 -> socket:[18892]
lrwx------ 1 root root 64 Jul 19 12:37 8 -> socket:[18893]
这里我们看到 fd 7、8 都是一个 socket fd,名字:socket:[18892]
整数句柄后面一般会跟一些信息,用于帮助我们了解这个 fd 是什么。举个例子,如果是文件 fd,那么箭头后面一般是路径名称。现在拆解一下这个名字:- socket :标识这是一个 socket 类型的 fd
[18892]
:这个是一个 inode 号,能够唯一标识本机的一条网络连接;
/proc/net/tcp
文件。这个文件里面能看到所有的 tcp 连接的信息。root@ubuntu:~# grep -i "18892" /proc/net/tcp
18: 00000000:1F93 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18892 1 ffff880197fba580 100 0 0 10 0
root@ubuntu:~# grep -i "18893" /proc/net/tcp
28: 00000000:1F7C 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18893 1 ffff880197fbad00 100 0 0 10 0
知识点又来了,/proc/net/tcp
这个文件记录了 tcp 连接的信息,这份信息是非常有用的。包含了 TCP 连接的地址(16进制显示),inode 的信息,连接的状态等等。socket fd 是什么?
环境声明:
Linux 内核版本 4.19 为了方便,如果没特意说明协议,默认 TCP 协议;
socket
可能你还没反应过来,中文名:套接字 是不是更熟悉点。Linux 网络编程甚至可以叫做套接字编程。有些概念你必须捋一捋 。我们思考几个小问题:socket 跟 tcp/ip 有什么区别?就不该把这两个东西放在一起比较讨论,就不是一个东西。tcp/ip 是网络协议栈,socket 是操作系统为了方便网络编程而设计出来的编程接口而已。理论基础是各种网络协议,协议栈呀,啥的。但是如果你要进行网络编程,落到实处,对程序猿来讲就是 socket 编程。对于网络的操作,由 socket 体现为 open -> read/write ->close 这样的编程模式,这个统一到文件的一种形式。socket 的 open 就是 socket(int domain, int type, int protocol)
,和文件一样,都是获取一个句柄。
网络模型一般会对应到两种:
- 完美理论的 OSI 七层模型;
- 现实应用的 5 层模型;
- 客户端和服务端都用
socket
调用创建套接字; - 服务端用
bind
绑定监听地址,用listen
把套接字转化为监听套接字,用accept
捞取一个客户端来的连接; - 客户端用
connect
进行建连,用write/read
进行网络 IO;
socket fd 的类型
上面我们提到了套接字,这是我们网络编程的主体,套接字由
socket()
系统调用创建,但你可知套接字其实可分为两种类型,监听套接字和普通套接字。而监听套接字是由 listen()
把 socket fd 转化而成。1 监听套接字
对于监听套接字,不走数据流,只管理连接的建立。
accept
将从全连接队列获取一个创建好的 socket( 3 次握手完成),对于监听套接字的可读事件就是全连接队列非空。对于监听套接字,我们只在乎可读事件。2 普通套接字
普通套接字就是走数据流的,也就是网络 IO,针对普通套接字我们关注可读可写事件。在说 socket 的可读可写事件之前,我们先捋顺套接字的读写大概是什么样子吧。套接字层是内核提供给程序员用来网络编程的,程序猿读写都是针对套接字而言,那么
write( socketfd, /* 参数 */)
和 read( socketfd, /* 参数 */)
都会发生什么呢?- write 数据到 socketfd,大部分情况下,数据写到 socket 的内存 buffer,就结束了,并没有发送到对端网络(异步发送);
- read socketfd 的数据,也只是从 socket 的 内存 buffer 里读数据而已,而不是从网卡读(虽然数据是从网卡一层层递上来的);
- socketfd 可读:其实就是 socket buffer 内有数据(超过阈值 SO_RCLOWAT );
- socketfd 可写:就是 socket buffer 还有空间让你写(阈值 SO_SNDLOWAT );
socket fd 为什么能具备“文件”的语义,从而和 eventfd,ext2 fd 这样的句柄一样,统一提供对外 io 的样子?
核心就是:sockfs ,这也是个文件系统,只不过普通用户看不见,这是只由内核管理的文件系统,位于 vfs 之下,为了封装 socket 对上的文件语义。
// net/socket.c
static int __init sock_init(void)
{
// 注册 sockfs 文件系统
err = register_filesystem(