当前位置: 首页 > news >正文

突破编程_C++_网络编程(Windows 套接字(处理 TCP 粘包问题))

1 TCP 协议与粘包问题概述

1.1 TCP 粘包的产生原因

TCP粘包问题的产生原因涉及多个方面,主要的原因如下:

  • 首先,发送方在发送数据时,由于TCP协议为提高传输效率而采用的Nagle算法,可能会将多个小数据包合并成一个大数据包进行发送。这种合并操作在TCP协议中是没有明确的分界线的,因此接收方在接收数据时无法准确区分原本的数据包,导致粘包现象的产生。

  • 其次,接收方的缓冲区大小设置也可能影响粘包问题的发生。如果接收方的缓冲区大小设置不合理,多个小数据包可能会因为缓冲区大小限制而粘在一起传输,形成一个大数据包发送。这种情况下,接收方同样无法准确区分原始的数据包,从而产生粘包问题。

  • 此外,网络状况的不稳定也可能导致TCP粘包问题。例如,网络延迟和抖动可能导致数据包到达顺序与发送顺序不一致,从而破坏了数据包的边界。当数据包在网络传输过程中发生乱序或丢失时,接收方在重新组装数据包时可能会出现错误,导致粘包现象的发生。

  • 最后,多个应用程序利用相同的TCP连接并发发送数据也可能引发粘包问题。由于TCP本身是流式协议,无法识别消息边界,因此当多个应用程序同时发送数据包到同一个Socket连接上时,这些数据包有可能在接收端粘连在一起,形成一个数据包。

1.2 TCP 粘包问题的具体表现

TCP粘包问题的具体表现主要体现在接收端数据的处理上。

首先,接收端收到的数据可能包含了多个发送端发送的消息。这是因为 TCP 协议在传输数据时,可能将多个小数据包合并成一个大数据包发送,而接收端在读取数据时,通常是一次性读取缓冲区中的所有内容。因此,如果发送端连续发送多个小数据包,而接收端的缓冲区大小足够大,那么这些小数据包就有可能被合并成一个大数据包发送给接收端。接收端在读取这个大数据包时,由于无法准确区分原本的数据包边界,就可能将多个发送端的消息混在一起处理,导致数据解析错误。

其次,一个消息可能被分成多个数据包发送。这通常发生在发送端发送的消息长度超过了 TCP 发送缓冲区剩余空间大小或 MSS(最大段大小)时。在这种情况下,TCP 协议会在传输前对消息进行拆包处理,将其分成多个小数据包发送。然而,由于网络传输的不确定性,这些小数据包可能在接收端不是按照发送的顺序到达的,或者由于接收端缓冲区的大小限制,这些小数据包可能被拆分或合并后存入缓冲区。因此,接收端在读取数据时,可能会发现原本应该是一个完整消息的数据被拆分成了多个数据包,导致数据解析和处理的复杂性增加。

此外,TCP 粘包问题还可能导致接收端的数据处理出现混乱。由于粘包现象的发生,接收端在读取数据时可能无法准确判断当前读取的数据是完整的一个消息还是多个消息的拼接。这可能导致接收端在处理数据时发生错误,例如将原本属于不同消息的数据错误地解析为同一个消息的一部分,或者将原本应该是一个完整消息的数据错误地截断或丢弃。这种混乱的数据处理可能导致应用程序的逻辑错误、数据丢失或重复等问题。

综上所述,TCP 粘包问题的具体表现主要包括接收端收到的数据包含多个发送端的消息、一个消息被分成多个数据包发送以及接收端数据处理出现混乱等方面。为了避免和解决这些问题,需要在应用程序的设计和实现中采取适当的措施,如添加消息长度字段、使用定长报文或应用层协议等方式来明确区分数据包边界,以确保数据的正确解析和处理。

2 处理 TCP 粘包问题的策略

处理TCP粘包问题的策略主要包括以下几种:

  1. 设置消息边界

    • 添加特殊字符或标志符号:在消息的末尾添加特定的分隔符,如"\r\n"或者自定义的协议标识。接收方在读取数据时,会不断检查是否有这些分隔符,一旦找到就认为当前的消息已经完整,从而避免了粘包问题。例如,FTP协议就使用了这种方式。
    • 使用消息帧:定义一种消息帧格式,每个消息都被封装在一个帧中,帧的头部包含消息长度等信息。接收方先读取帧头部获取消息长度,再按照该长度读取完整的消息内容。
  2. 定长发送

    • 发送固定长度的数据包:无论实际数据大小如何,都将其填充或截断为固定长度的数据包进行发送。接收方按照同样的固定长度来读取数据,从而避免了粘包问题。但这种方法可能会浪费带宽,特别是当实际数据较小时。
    • 改进版定长发送:对于最后一个长度不足的数据包,可以在其后面填充空白字节,并在数据包头部添加一个长度字段或标志位,以便接收方能够识别并去除这些填充字节。
  3. 延迟发送

    • 发送方等待一段时间:在发送一个数据包后,发送方等待一段时间再发送下一个数据包,以减少多个数据包同时到达接收方造成的粘包问题。但这种方法可能增加数据传输的延迟,影响实时性。
  4. 优化接收方处理

    • 调整接收缓冲区大小:根据实际应用场景和数据量大小,合理设置接收方的缓冲区大小,避免缓冲区过小导致的粘包问题。
    • 多线程或异步接收:使用多线程或异步处理机制来接收数据,以提高数据处理速度和效率,减少粘包问题的影响。
  5. 应用层协议设计

    • 设计明确的应用层协议:在应用层设计明确的协议规范,包括数据包格式、长度字段、消息边界等,以确保发送方和接收方能够正确解析和处理数据。

在实际应用中,可以根据具体场景和需求选择适合的策略来处理TCP粘包问题。同时,还需要考虑网络状况、数据传输量、实时性要求等因素,综合权衡各种策略的优缺点,以达到最佳的处理效果。

3 使用消息头+消息体法处理 TCP 粘包问题

3.1 基本概念

使用消息头(Header)+消息体(Body)的方法来处理 TCP 粘包问题是一种常见且有效的策略。这种方法的核心思想是在每个消息前添加一个消息头,消息头中包含了消息体的长度信息,接收方在读取数据时首先读取消息头,然后根据消息头中的长度信息来读取相应长度的消息体,从而避免了粘包问题。

具体实现步骤如下:

发送方:

  1. 将要发送的消息封装成消息体(Body)。
  2. 生成一个消息头(Header),消息头中至少包含消息体的长度信息。可以使用定长的整数来表示长度,也可以使用可变长度的编码方式(如使用前缀编码来表示长度)。
  3. 将消息头和消息体合并成一个完整的数据包。
  4. 将数据包通过 TCP 连接发送给接收方。

接收方:

  1. 创建一个缓冲区来接收数据。
  2. 不断从 TCP 连接中读取数据到缓冲区,直到缓冲区中有足够的数据来读取一个完整的消息头。
  3. 解析消息头,获取消息体的长度信息。
  4. 根据消息体的长度信息,从缓冲区中读取相应长度的数据作为消息体。
  5. 将消息头和消息体组合成一个完整的消息,并进行后续处理。
  6. 重复步骤 2-5,直到所有数据都被处理完毕。

这种方法的优点在于它能够清晰地界定每个消息的边界,从而避免了粘包问题。同时,它也能够处理变长的消息,提高了数据传输的灵活性。

需要注意的是,在实际应用中,还需要考虑一些边界情况和异常处理。例如,当接收方读取到的数据不足以构成一个完整的消息头时,应该继续等待数据的到来;当接收到的数据长度超过消息头中指定的长度时,应该丢弃多余的数据或进行相应的错误处理。此外,为了提高数据传输的效率,还可以在消息头中添加其他元数据信息,如时间戳、消息类型等,以便接收方更好地理解和处理消息。

3.2 示例

下面是一个简化的 C++ 代码示例,用于使用消息头(固定为0x20)+消息体长度(占一个字节)+消息体的格式来处理TCP粘包问题。

首先,定义粘包处理函数:

#include <iostream>  
#include <cstring>  
#include <vector>  
#include <queue>  
#include <cstdint>  
#include <algorithm>  std::queue<uint8_t> remainingDatas;		// 用于临时存储没有处理完的数据
bool revMsgHeadFlag = false;			// 是否收到消息头
bool revMsgLenFlag = false;				// 是否收到消息体长度  
uint8_t messageLength = 0;				// 消息体长度void clearRemainingDatas() {while (!remainingDatas.empty()) {remainingDatas.pop();}
}// 处理TCP粘包问题的函数  
void processTCPData(const std::vector<uint8_t>& receivedData) {for (auto val : receivedData){remainingDatas.push(val);}while (!remainingDatas.empty()) {// 检查是否接收到消息头  if (!revMsgHeadFlag) {auto val = remainingDatas.front();remainingDatas.pop();if (0x20 != val) {std::cerr << "Invalid header found!" << std::endl;messageLength = 0;clearRemainingDatas();revMsgHeadFlag = false;revMsgLenFlag = false;return; // 可以进行其他的错误处理  }revMsgHeadFlag = true;}if (!revMsgLenFlag && !remainingDatas.empty()) {messageLength = remainingDatas.front();remainingDatas.pop();revMsgLenFlag = true;} if (!revMsgLenFlag) {// 数据不完整,等待更多数据  break;}// 检查是否有足够的数据来读取整个消息体  if (messageLength > remainingDatas.size()) {// 数据不完整,等待更多数据  break;}// 读取消息体  std::vector<uint8_t> messageBody;for (size_t i = 0; i < messageLength; i++){auto val = remainingDatas.front();remainingDatas.pop();messageBody.emplace_back(val);}// 处理消息体(这里只是简单打印)  std::cout << "Received message: ";for (uint8_t byte : messageBody) {std::cout << static_cast<char>(byte);}std::cout << std::endl;// 准备读取下一个消息revMsgHeadFlag = false;revMsgLenFlag = false;messageLength = 0;}// 如果有剩余数据未处理,可能是因为数据不完整,需要等待更多数据  if (remainingDatas.size() > 0) {std::cout << "Remaining data, waiting for more..." << std::endl;}
}

接下来,使用模拟的字节流数据做测试:

int main() {// 模拟TCP接收数据  std::vector<uint8_t> receivedData1 = {0x20, 0x06, 'H', 'e', // 消息1: 消息头 + 长度3 + 消息体"He"   : 注意,这里消息体少了三个字节,放在下一包数据};std::vector<uint8_t> receivedData2 = {'l','l','o',' ', 0x20, 0x06, 'W', 'o', 'r', 'l', 'd', '!',0x20, 0x06, 'H', 'e', 'l', 'l', 'o',' ',0x20,  // 消息2: 上一包消息体的"llo " + 两个完整的消息  +下一包的消息头"0x20"};std::vector<uint8_t> receivedData3 = {0x04, 'T', 'C', 'P', '!' // 消息3: 长度4 + 内容"TCP!"  };// 处理接收到的数据  processTCPData(receivedData1);processTCPData(receivedData2);processTCPData(receivedData3);return 0;
}

上面代码的输出为:

Remaining data, waiting for more...
Received message: Hello
Received message: World!
Received message: Hello
Received message: TCP!

在这个示例中,processTCPData 函数负责处理这些数据,它遍历接收到的字节流,查找消息头,读取消息体长度,并提取消息体。如果数据不完整,函数会停止处理并等待更多数据。

注意:这个示例非常简化,没有处理网络编程中的许多实际问题,比如多线程、异步I/O、错误处理、超时、流量控制等。在实际应用中,可能需要将这些概念整合到实际的网络编程框架中。此外,这个示例假设消息体的长度不会超过255字节(因为一个字节可以表示的最大值是255),如果需要处理更长的消息体,则需要调整消息体长度的编码方式。

相关文章:

突破编程_C++_网络编程(Windows 套接字(处理 TCP 粘包问题))

1 TCP 协议与粘包问题概述 1.1 TCP 粘包的产生原因 TCP粘包问题的产生原因涉及多个方面&#xff0c;主要的原因如下&#xff1a; 首先&#xff0c;发送方在发送数据时&#xff0c;由于TCP协议为提高传输效率而采用的Nagle算法&#xff0c;可能会将多个小数据包合并成一个大数…...

【训练营】DateWhale——动手学大模型应用开发(更新中)

文章目录 写在前面大模型简介LLM简介RAG简介LangChain开发框架开发LLM应用的整体流程 写在前面 大模型时代从GPT爆发开始到现在已有一年多了&#xff0c;深度学习发展之快无法想象&#xff0c;一味感叹技术发展速度超越个人学习速度是没用的&#xff0c;倒不如花点时间参加一些…...

【学习笔记十九】EWM Yard Management概述及后台配置

一、EWM Yard堆场管理业务概述 1.Yard Management基本概念 YARD管理针对的是库房以外的区域&#xff0c;可以理解为入大门开始到库门之前的这部分的区域 堆场结构 像在仓库中一样&#xff0c;将相应仓位映射为堆场仓位&#xff0c;可将其分组到堆场分区。场地中可能具有以下结…...

【环境搭建】(五)Ubuntu22.04安装cuda_11.8.0+cudnn_8.6.0

一个愿意伫立在巨人肩膀上的农民...... 设备配置&#xff1a; 一、安装GCC 安装cuda之前&#xff0c;首先应该安装GCC&#xff0c;安装cuda需要用到GCC&#xff0c;否则报错。可以先使用下方指令在终端查看是否已经安装GCC。 gcc --version 如果终端打印如下则说明已经安装…...

【UE5.1】使用MySQL and MariaDB Integration插件——(3)表格形式显示数据

在上一篇&#xff08;【UE5.1】使用MySQL and MariaDB Integration插件——&#xff08;2&#xff09;查询&#xff09;基础上继续实现以表格形式显示查询到的数据的功能 效果 步骤 1. 在“WBP_Query”中将多行文本框替换未网格面板控件&#xff0c;该控件可以用表格形式布局…...

JVM复习

冯诺依曼模型与计算机处理数据过程相关联&#xff1a; 冯诺依曼模型&#xff1a; 输入/输出设备存储器输出设备运算器控制器处理过程&#xff1a; 提取阶段&#xff1a;输入设备传入原始数据&#xff0c;存储到存储器解码阶段&#xff1a;由CPU的指令集架构ISA将数值解…...

63、ARM/STM32中IIC相关学习20240417

完成温湿度传感器数据采集实验。 【思路&#xff1a;1.通过IIC通信原理&#xff0c;理解其通信过程&#xff0c;通过调用封装的IIC函数达成主机和从机之间&#xff1a;起始信号、终止信号、读、写数据的操作&#xff1b; 2.了解温湿度传感器控制芯片SI7006的工作原理&#…...

离岸人民币与人民币国际化

参考 什么是离岸人民币&#xff1f;它有什么用&#xff1f; - 知乎 “人民币就是人民币&#xff0c;为什么要在它前面加上离岸二字&#xff1f;” “既然有离岸人民币&#xff0c;是否有在岸人民币&#xff1f;” 今天我们就简单了解一下什么是离岸人民币。 离岸/在岸人民币…...

Linux平台上部署和运行Ollama的全面指南

Ollama的安装与配置 Ollama提供了一种简单的安装方法&#xff0c;只需一行命令即可完成安装&#xff0c;但是对于想要更深入了解和自定义安装的用户&#xff0c;我们也提供了手动安装的步骤。 快速安装 Ollama的安装极为简单&#xff0c;只需在终端中执行以下命令&#xff1…...

Web---robots协议详解

在Web中&#xff0c;robots协议&#xff08;也称为robots.txt&#xff09;是一种文本文件&#xff0c;用于向搜索引擎机器人&#xff08;通常称为爬虫&#xff09;提供指导&#xff0c;以指示它们哪些页面可以抓取&#xff0c;哪些页面应该忽略。robots.txt文件位于网站的根目录…...

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第四套

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第四套 (共9套&#xff0c;有答案和解析&#xff0c;答案非官方&#xff0c;仅供参考&#xff09;&#xff08;共九套&#xff0c;每套四十个选择题&#xff09; 部分题目分享&#xff0c;完整版获取&#xff08;WX:didadida…...

clipper一些数据结构(入门初识(一))

clipper一些数据结构&#xff08;一&#xff09; Clipper库是一个用于执行多边形裁剪&#xff08;clipping&#xff09;和偏移&#xff08;offsetting&#xff09;操作的开源C库。在Clipper库中&#xff0c;点和多边形&#xff08;polygon&#xff09;是基本的数据结构。Clipp…...

读《SQL基础教程 第二版 上》的一些总结

1. 数据库语言 DDL: Data Definition Language&#xff0c;数据定义语言&#xff08;库、表的操作&#xff09; DML: Data Manipulation Language&#xff0c; 数据操控语言&#xff08;对表中数据的增删改&#xff09; DQL: Data Query Language&#xff0c;数据库查询语言…...

EDI是什么:EDI系统功能介绍

EDI全称Electronic Data Interchange&#xff0c;中文名称是电子数据交换&#xff0c;也被称为“无纸化贸易”。EDI实现企业间&#xff08;B2B&#xff09;自动化通信&#xff0c;帮助贸易伙伴和组织完成更多的工作、加快物流时间并消除人为错误。 目前国内企业实现EDI通信大多…...

64B/66B GT Transceiver 配置

一、前言 前一篇文章已经讲述了64B/66B的编码原理&#xff0c;此篇文章来配置一下7系列GT的64B/66B编码。并讲述所对应的例子工程的架构&#xff0c;以及部分代码的含义。 二、IP核配置 1、打开7 Series FPGAs Transceiver Wizards&#xff0c;选择将共享逻辑放置在example …...

ES6: promise对象与回调地狱

ES6&#xff1a; promise对象与回调地狱 一、回调地狱二、Promise概述三、Promise的组成四、用函数封装Promise读取文件操作 一、回调地狱 在js中大量使用回调函数进行异步操作&#xff0c;而异步操作什么时候返回结果是不可控的&#xff0c;所以希望一段程序按我们制定的顺序执…...

Qt事件处理机制2-事件函数的传播

所有继承自QObject的类都有event函数&#xff0c;该函数用来处理自身的事件&#xff0c;函数定义如下&#xff1a; virtual bool QObject::event(QEvent *e)&#xff1b;Qt帮助文档&#xff1a; This virtual function receives events to an object and should return true i…...

【PDF.js】PDF文件预览

【PDF.js】PDF文件预览 一、PDF.js二、PDF.js 下载1、下载PDF.js2、在项目中引入3、屏蔽跨域错误 三、项目中使用四、说明五、实现效果 使用PDFJS实现pdf文件的预览&#xff0c;支持预览指定页、关键词搜索、缩略图、页面尺寸调整等等。 一、PDF.js 官方地址 文档地址 二、PD…...

从建表语句带你学习doris_表索引

1、doris建表概述 1.1、doris建表模板 CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [DATABASE.]table_name (column_definition1[,column_deinition2,......][,index_definition1,[,index_definition2,]] ) [ENGINE [olap|mysql|broker|hive]] [key_desc] [COMMENT "tabl…...

Linux CentOS 安装 MySQL 服务教程

Linux CentOS 安装 MySQL 服务教程 1. 查看系统和GNU C库(glibc)版本信息 1.1 查询机器 glibc 版本信息 glibc&#xff0c;全名GNU C Library&#xff0c;是大多数Linux发行版中使用的C库&#xff0c;为系统和应用程序提供核心的API接口。在Linux系统中&#xff0c;特别是在…...

MSSQL 命令行操作说明 sql server 2022 命令行下进行配置管理

说明&#xff1a;本文的内容是因为我在导入Access2019的 *.accdb 格式的数据时&#xff0c;总是出错的背景下&#xff0c;不得已搜索和整理了一下&#xff0c;如何用命令行进行sql server 数据库和用户管理的方法&#xff0c;作为从Access2019 直接导出数据到sql server 数据库…...

【系统分析师】系统安全分析与设计

文章目录 1、安全基础技术1.1 密码相关1.1.1对称加密1.1.2非对称加密1.1.3信息摘要1.1.4数字签名1.1.5数字信封 1.2 PKI公钥体系 2、信息系统安全2.1 保障层次2.2 网络安全2.2.1WIFI2.2.2 网络威胁与攻击2.2.3 安全保护等级 2.3计算机病毒与木马2.4安全防范体系 1、安全基础技术…...

ActiveMQ 07 集群配置

Active MQ 07 集群配置 官方文档 http://activemq.apache.org/clustering 主备集群 http://activemq.apache.org/masterslave.html Master Slave TypeRequirementsProsConsShared File System Master SlaveA shared file system such as a SANRun as many slaves as requ…...

Redis(哨兵模式)

什么是哨兵机制 问题: redis 主从复制模式下, 一旦主节点由于故障不能提供服务, 需要人工进行主从切换, 同时大量客户端需要被通知切换到新的主节点上, 对于有一定规模的应用来说, 对于人力的资源消耗会很大.解决: 通过哨兵对主从结构进行监控, 一旦出现主节点挂了的情况, 自动…...

一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制

简介 在嵌入式开发中&#xff0c;经常有需要用到RingBuffer的概念&#xff0c;在RingBuffer中经常遇到一个Buffer满和Buffer空的判断的问题&#xff0c;一般的做法是留一个单位的buffer不用&#xff0c;这样做最省事&#xff0c;但是当RingBuffer单位是一个结构体时&#xff0…...

【Java开发指南 | 第十一篇】Java运算符

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 算术运算符关系运算符位运算符逻辑运算符赋值运算符条件运算符&#xff08;?:&#xff09;instanceof 运算符Java运算符优先级 Java运算符包括&#xff1a;算术运算符、关系运算符、位运算符、逻辑运算符、赋值…...

【IC前端虚拟项目】验证环境方案思路和文档组织

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 对于mvu的验证环境,从功能角度就可以分析出需要搭建哪些部分,再看一下mvu的周围环境哈: 很明显验证环境必然要包括几个部分: 1.模拟idu发送指令; 2.模拟ram/ddr读写数据; 3.rm模拟mvu的行为; …...

程序设计|C语言教学——C语言基础1:C语言的引入和入门

一、程序的执行 1.定义 解释&#xff1a;借助一个程序&#xff0c;那个程序能够试图理解你的程序&#xff0c;然后按照你的要求执行。下次执行的时候还需要从零开始解释。 编译&#xff1a;借助一个程序&#xff0c;能够像翻译官一样&#xff0c;把你的程序翻译成机器语言&a…...

初学python记录:力扣928. 尽量减少恶意软件的传播 II

题目&#xff1a; 给定一个由 n 个节点组成的网络&#xff0c;用 n x n 个邻接矩阵 graph 表示。在节点网络中&#xff0c;只有当 graph[i][j] 1 时&#xff0c;节点 i 能够直接连接到另一个节点 j。 一些节点 initial 最初被恶意软件感染。只要两个节点直接连接&#xff0c…...

LlamaIndex 组件 - Storing

文章目录 一、储存概览1、概念2、使用模式3、模块 二、Vector Stores1、简单向量存储2、矢量存储选项和功能支持3、Example Notebooks 三、文件存储1、简单文档存储2、MongoDB 文档存储3、Redis 文档存储4、Firestore 文档存储 四、索引存储1、简单索引存储2、MongoDB 索引存储…...

网站建设导向明确/seo模板建站

查看服务器的内存和交换区 内容精选换一换本章节适用于MRS 3.x之前版本。Loader支持以下多种连接&#xff0c;每种连接的配置介绍可根据本章节内容了解。obs-connectorgeneric-jdbc-connectorftp-connector或sftp-connectorhbase-connector、hdfs-connector或hive-connectorOBS…...

网络营销推广品牌/搜seo

函数的调用&#xff0c;一般是对同一个源文件中的其他函数进行调用的&#xff0c;也可以对另外一个源文件中的函数进行调用C语言中&#xff0c;根据函数能否被其他源文件调用&#xff0c;分为内部函数和外部函数外部函数&#xff0c;可以被其他源文件调用的函数内部函数&#x…...

青岛网站建设公司怎么样/小程序seo推广技巧

一 原因 1.内存中加载的数据量过于庞大&#xff0c;如一次从数据库取出过多数据&#xff1b; 2.集合类中有对对象的引用&#xff0c;使用完后未清空&#xff0c;使得JVM不能回收&#xff1b; 3.代码中存在死循环或循环产生过多重复的对象实体&#xff1b; 4.使用的第三方软件中…...

wordpress插件汉化/成都网络营销推广公司

一&#xff1a;实现目标&#xff1a; 1&#xff09;三级分类&#xff1a; 2&#xff09;表结构&#xff1a; 3&#xff09; 一.编写出 查出所有分类&#xff0c;以及子分类。以 树形结构 组装起来 的接口&#xff1a; 1&#xff09;创建用于 数据…...

有做游戏广告的网站/seo课程多少钱

虽然大一入学的时候就装了虚拟机&#xff0c;但是一直装不下VMware Tools&#xff0c;配置环境的时候只能在虚拟机重新敲网址复制粘贴命令或者手敲&#xff0c;要是忘记切换sudo还要再敲一遍…最近又要投入linux的怀抱了&#xff0c;今晚又找了篇教程&#xff0c;结果居然成功了…...

广德做网站设计开发/重庆搜索排名提升

BootStrap知识概括BootStrap简介容器栅格系统分析响应式工具其他工具BootStrap简介 BootStrap简介&#xff1a; Bootstrap是美国Twitter公司的设计师Mark Otto和Jacob Thornton合作基于HTML、CSS、JavaScript 开发的简洁、直观、强悍的前端开发框架&#xff0c;使得 Web 开发…...