前言:我们经常说TCP是面向字节流的, TCP是面向字节流的。 但是, 到底是什么事面向字节流呢? 另外, 我们知道sockfd其实就是文件fd。 但是,为什么sockfd是文件fd呢? 这些问题都在本节内容中的到回答, 同时外加一些小知识点。现在废话不多说, 开始我们的学习吧!
ps:本节内容建议即便不熟悉TCP报文报头也可以学习哦!
目录
面向字节流
什么是面向字节流
面向字节流的问题
TCP连接异常问题
进程终止
机器重启
机器掉电,网络断开
打通socket与文件fd
面向字节流
什么是面向字节流
由于缓冲区的存在,当我们写100个字节的时候可以一次发送100个字节,也可以发送100次一个字节。我们还可以一次接收100个字节或者接收100次一个字节。
换句话说就是由于缓冲区的存在,TCP程序的读和写不需要一一匹配。
而面向数据报就是类似于我们的发邮件,发快递,就是我们发送端发送了几次报文,接收方就得接收几次报文。并且发多少接受多少。
就比如上层有四个请求,一个请求40个字节。当应用层将数据发送到内核缓冲区的时候内核
缓冲区不关心有几个报文, 它只关心一共多少个字节。就比如现在是160个字节。然后TCP发送
的时候要做的,就是把这160个字节安全稳定的发送到对面!对方的内核缓冲区也只关心一共有
多少个字节的数据要接收!至于报文有多少,报头和有效载荷分离那是上层用户要关心的,下层不关心。
这其中,TCP传输层只关心字节的流动,所以叫做面向字节流。
面向字节流的问题
用户层才有报文的概念,我们发送方是发送多个请求, 接收方就是对报文一个一个的处理
将字节流变成一个一个的请求。这个时候就有数据报粘包问题。
什么是数据粘包问题——当上层进行读取时,如果不对报文进行一个一个的分离,就可能会多处理或者少处理请求,这种情况, 叫做数据报粘包问题。
这个数据报粘包问题, 是相对于用户层的概念。解决粘报: 定协议。
这个定协议其实就是我们在写tcp协议序列化反序列化的时候那个encode和decode。encode和decode的目的就是为了解决粘报问题。
所以, 我们以后如何解决数据报的粘报问题?——核心思想就是明确报文和报文之间的边界。
- 1、定长报文
- 2、使用特殊字符。就比如今天的报文没有换行符。 所以为了区分报文与报文,我们就可以使用 n来进行区分
- 3、使用自描述字段 + 定长报头
- 4、使用自描述字段 + 特殊字符
所以,面向字节流衍生出了数据报粘报问题,为了解决这个问题,就要使用特定的方案。
TCP连接异常问题
进程终止
我们的客户端假设此时和服务端三次握手建立了链接,但是此时客户端突然终止了。那么链接要怎么样?这里我们就要知道,链接和进程 (客户端服务器)之间本身没什么关系,链接和文件是直接相关的。但是我们曾经讲过文件生命周期是随进程的。 所以链接本质是随进程的。
所以, 结论就是: 对于一个进程来说,它是正常退出还是异常退出, 对于操作系统来说都是退出。那么进程终止, 链接就要断开。就要进行四次挥手。 即: 链接正常自动断开。
机器重启
当我们进行关机的时候,是不是所有的进程都要先关闭? windows有的时候就会问我们是否关闭这些进程,所以,关机的时候,就要先杀掉所有的进程。对于tc来说, 本质也是要断开连接。
机器掉电,网络断开
客户端一旦将网线拔掉,客户端时没有机会向服务器中进行四次挥手。因为报文发不出去了。所以服务器不知道客户端已经挂了,他的链接是正常维护的。当客户端再重新联网, 就要重新进行连接,这也是认知不一致问题。
当服务端发现客户端长时间不发消息,也不回。那么服务端也不能一直保持连接,这就用到了一个叫做:保活机制。 就是一旦时间太长,那么服务端就要认为客户端断开连接了, 就要将连接断掉。
打通socket与文件fd
这是我们的进程:
假如此时打开了一个文件struct file。然后struct file对象里面包含了函数方法以及缓冲区(对于网络来说不重要)以及一个函数指针:private_date。
当我们创建网络套接字时,在我们的操作系统当中,会为我们创建很多数据结构,其中一个就叫做socket 。那private.data就会指向socket这个文件。
这个socket结构体里面也有一个变量file,能够回指向我们的网络文件。
所以,在我们的进程层面上,我们只需要利用fd找到对应的文件,就能找到对应的socket全部信息。
然后这里有一个wait等待队列,这个就是当我们的网络不就虚的时候,就将改网络文件连接到等待队列里。
socket结构体也有对应的成员函数,也就是说struct file和socket都有自己的方法集。
sock也是一个重要的属性。 这个sk指针就是指向的sock,这个sock是UDP或者TCP协议结构体的一个成员。什么意思?意思就是说UDP和TCP本身还有一个关于sock的结构体。即udp_sock和tcp_sock。然后,实际上在我们创建tcp套接字时,在底层其实创建的tcp_sock,在我们创建tcp套接字时,在底层其实创建的是udp_sock;然后sock指向的其实是udp_sock或者tcp_sock里面的sock结构体。
那么他怎么知道指向的时udp或者tcp呢? 其实sock当中有一个lag,区分socket类型
以后,想要访问某一个字段,比如是tcp访问字段,只需要(struct tcp.sock*sk->???就行了。所以,其实这个sk的本质,就是多态。
我们以前学习文件管理的时候知道struct fle里面的op其实是指向的是网卡的一堆方法。然后soket里面又有网络相关方法。这两个有什么区别呢?
这个file里面的op其实是对上的,上层数据交给下层。 然后socket里面的op是对下的。
所以协议栈, 其实就是用特定数据结构表述的协议,用特定协议匹配的方法集合!!!
服务器收到多个报文,而报文是要被管理的。如何管理呢? 用的是一个sk_buff的结构体进行描述。这个结构体里面有head, data, tail, end。其中head指针就是指向报头的指针。当这个结构体向上或者向下的过程中, 就是报斗指针的向下或者向的过程即封装和解包,本质只要移动指针就可以。
——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!!