深入理解 Linux socket(一)

转自:https://mp.weixin.qq.com/s/yXO5hn2PiPmRWTXygnf8zQ
https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzU1MDkzMzQzNQ==&scene=161#wechat_redirect
在这里插入图片描述

一、深入理解 Linux socket

什么是 socket fd ?粗糙的来讲,就是网络 fd,比如我们最常见的 C/S 客户端服务端的编程模式,就是网络通信的一种方式。撇开底层和协议细节,网络通信文件读写从接口上有本质区别吗?
其实没啥区别,不就是读过来和写过去嘛,简称 IO 。
在这里插入图片描述
我们先看一下 socket fd 是什么样子的?随便找了个进程
在这里插入图片描述
这里我们看到 fd 7、8 都是一个 socket fd,名字:socket:[18892]

整数句柄后面一般会跟一些信息,用于帮助我们了解这个 fd 是什么。举个例子,如果是文件 fd,那么箭头后面一般是路径名称。现在拆解一下这个名字:

socket :标识这是一个 socket 类型的 fd
[18892] :这个是一个 inode 号,能够唯一标识本机的一条网络连接;
思考下,这个 inode 号,还能再哪里能看到呢?

在 proc 的 net 目录下,因为我这个是一个走 tcp 的服务端,所以我们看一下 /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 的类型

上面我们提到了套接字,这是我们网络编程的主体,套接字由 socket() 系统调用创建,但你可知套接字其实可分为两种类型,监听套接字和普通套接字。而监听套接字是由 listen() 把 socket fd 转化而成。

1 监听套接字
对于监听套接字,不走数据流,只管理连接的建立。accept 将从全连接队列获取一个创建好的 socket( 3 次握手完成),对于监听套接字的可读事件就是全连接队列非空。对于监听套接字,我们只在乎可读事件。
2 普通套接字
普通套接字就是走数据流的,也就是网络 IO,针对普通套接字我们关注可读可写事件。在说 socket 的可读可写事件之前,我们先捋顺套接字的读写大概是什么样子吧。

套接字层是内核提供给程序员用来网络编程的,程序猿读写都是针对套接字而言,那么 write( socketfd, /* 参数 /) 和 read( socketfd, / 参数 */) 都会发生什么呢?

write 数据到 socketfd,大部分情况下,数据写到 socket 的内存 buffer,就结束了,并没有发送到对端网络(异步发送);
read socketfd 的数据,也只是从 socket 的 内存 buffer 里读数据而已,而不是从网卡读(虽然数据是从网卡一层层递上来的);
也就是说,程序猿而言,是跟 socket 打交道,内核屏蔽了底层的细节。

那说回来 socket 的可读可写事件就很容易理解了。

socketfd 可读:其实就是 socket buffer 内有数据(超过阈值 SO_RCLOWAT );
socketfd 可写:就是 socket buffer 还有空间让你写(阈值 SO_SNDLOWAT );
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
涉及的一些函数调用:

__sys_socket
    // 创建 struct socket 结构体
    -> sock_create
            // 创建 struct socket 结构,并且关联特殊 inode
            -> sock_alloc
            // pf 是根据 family 从 net_families 这个全局表中取出的操作函数表,用来创建具体网络协议结构的;  
            // 比如 IPv4 对应的 family 就是 AF_INET ,对应的函数是 inet_create
            // 在这里面会赋值 sock->ops 为对应协议族的操作函数表(比如 inet_stream_ops)
            -> pf->create
                    // struct sock 结构体的创建(sk->sk_prot 的赋值就在这里,比如 tcp_prot )
                    -> sk_alloc
                    // struct sock 结构体的初始化(比如 sk_receive_queue, sk_write_queue, sk_error_queue 就是在这里初始化的)
                    // 可读写的关键函数 sock_def_readable,sock_def_write_space 也是在这里赋值的
                    -> sock_init_data
    // 创建 struct file 结构体,并且关联 struct socket
    -> sock_map_fd

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后用一张简要的图展示结构体之间的关系:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值