• TCP延申


    粘包问题

    粘包问题中的”包“,指的是应用层的数据包
    TCP是基于字节流的,只维护发送出去多少,确认了多少,并不会维护消息和消息的边界,这就导致了粘包问题,他在应用层取数据的时候,不知道从哪里到哪里是一个完整的应用层数据包,面向字节流读文件都会有这种问题

    在这里插入图片描述
    数据就变得混乱了

    怎么解决呢?

    由于找不到应用层的数据始末,所以去TCP 上面做功夫肯定是不行的,所以解决这个问题就是要在应用层协议中加上包和包之间边界,比如在应用层数据报的结尾加上一个**;**这样在读取的时候,就能区分出一个完整的应用层数据报了,

    在这里插入图片描述

    在这里插入图片描述

    读取http请求的

    char buff[1024];
    while(true)
    {
    	getline(sock,buffer);//按行读
    	if(buff=="\n")
    	{
    	break;//报头读完了
    	}
    }
    //在包头里面找到content-length=80
    recv(sck,buffer,80)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 定长发送,缓冲区大小都是1024
    2. 特殊字符,遇到这个字符就分离
    3. 自描述,自己进行定制协议

    TCP 异常处理

    TCP协议传输时会出现以下几种情况
    连接崩溃的情况

    • 机器关机/重启:会释放文件描述符,发送FIN,和正常关闭一样,和进程终止是一样的
    • 进程终止:和机器重启一样,底层会自动4次挥手,文件描述符自动关

    在进程毫无准备的情况下,突然结束进程,
    我们知道TCP 连接是通过socket进行的,socket本质就是打开了一个文件,文件就存在于PCB的文件描述符表之中,每次打开一个socket文件都会在文件描述符表添加一项,删除会减少的一项
    当强制结束进程时,PCB没了,里面的文件描述符表也没了,就相当于自动关闭了,也依然会执行四次挥手的过程

    • 网线断开/机器断电:断电时没有时间给操作系统去反应,来不及四次挥手,如果客户端断电,客户端不会给服务器发送任何数据,服务器会尝试重新连接,重连一定次数的话,就会放弃连接,接收端的连接还在,当接收端对我进行写入,会收到一个reset然后TCP 内不设置一个保活定时器(比较鸡肋),询问对方是否还在,不在就释放连接

      但是最好不要把保活定时器作为一个应用层的工具,因为不同的应用层对于连接是否需要关闭是不一样的,比如QQ,在断线之后,也会定期尝试重新连接

    用UDP实现可靠传输

    • 引入序列号:保证数据的顺序型
    • 引入确认应答机制:对端收到的数据都要进行应答
    • 超时重传:数据隔一段时间没收到进行重新传
    • 校验和:使用校验和,避免发送错误
    • 流量控制:对发送的数据进行管理

    TCP 太重了,udp比较轻量化

    TCP相关实验

    理解Listen的第二个参数

    listen第二个参数+1=在TCP层建立正常连接的个数,不是只能在服务器段维护几个连接

    int main(int argc, char *argv[])
    {
        if (argc != 2)
        {
            cout << "port" << endl;
            exit(1);
        }
        uint16_t port = atoi(argv[1]);
        int sockfd = Sock::Socket();
        Sock::Setoptsocket(sockfd);
        Sock::Bind(sockfd, port);
        Sock::Listen(sockfd);
        while (1)
        {
    	//这里我们没有调用accept
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    在这里插入图片描述
    Linux内核协议栈为一个TCP连接管理使用两个队列

    1. 半连接队列(用来保存处于SYN_RECV和SYN_SEND状态的请求),握手之中
    2. 全连接队列(acceptd队列)(用来保存处于established状态,但是应用层没有调用accept取走的请求,暂时没有被读取),握手成功

    全连接队列长度会受listen第二个参数影响(n+1):长度至少为1

    为什么要维护队列(全链接)?为什么这个队列不能过长?为什么这个队列不能没有?

    在这里插入图片描述

    1. 上层的服务太忙了, 服务打满了,来不及去接收新的服务,只能让它在队列里面(避免内部资源没有被充分利用)
    2. 当内部满的时候,一旦有人退出,就可以立马把队列里面的取走,保证内部资源被100%利用
    3. 如果把队列弄太长了,维护队列也是有成本的,成本太高了,长时间链接没有反应,客户就会把服务关掉,把省出来的,让服务去使用,让服务以较高的效率提供服务
    4. listen就相等于在门口叫号的
  • 相关阅读:
    IDEA的使用设置
    springboot 项目启动检查创建数据库,执行脚本,以及初始化数据源
    亚马逊云科技数据分析为这伴科技赋能,实现“零”中断目标
    【Android Framework系列】第14章 Fragment核心原理(AndroidX版本)
    卷积神经网络超详细介绍(转载)
    flutter报错HTTP Host Availability (the doctor check crashed)的解决办法
    案例|航海知识竞赛需求沟通整理
    深度学习笔记_5 经典卷积神经网络LeNet-5 解决MNIST数据集
    【正点原子】开发板可以ping通电脑,电脑ping不通开发板
    QT object元对象
  • 原文地址:https://blog.csdn.net/m0_61567378/article/details/126697275