【Hello Network】TCP协议相关理解
作者:@小萌新
专栏:@网络
作者简介:大二学生 希望能和大家一起进步
本篇博客简介:补充下对于TCP协议的各种理解
TCP协议相关实验
- TCP相关试验
- 理解CLOSE_WAIT状态
- 理解TIME_WAIT状态
- 解决TIME_WAIT状态引起的bind失败的方法
- 理解listen的第二个参数
- SYN洪水攻击
- TCP和UDP
- TCP和UDP协议对比
- 用UDP实现可靠传输(经典面试题)
TCP相关试验
理解CLOSE_WAIT状态
我们要理解CLOSE_WAIT状态 首先要知道什么时候会出现CLOSE_WAIT状态
当我们的客户端和服务器通信的时候 客户端调用close函数关闭其文件描述符 此时客户端在底层就会像服务器发送FIN报文 服务器给予FIN报文响应的ACK报文之后就会变成CLOSE_WAIT状态 而客户端会在第一次发送FIN报文的时候变为FIN_WAIT1状态 在收到ACK报文后变为FIN_WAIT2状态
我们可以编写一个简单的TCP服务器来模拟出该现象 不过事实上我们只需要写出服务器的代码就可以了 客户端的代码我们可以使用一些现成的工具比如说TELNET
服务器代码如下
#include <iostream> #include <cstring> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netinet/in.h> #include <pthread.h> using namespace std; const int PORT = 8081; const int NUM = 5; void* RUN(void* arg) { pthread_detach(pthread_self()); int fd = *(int*)arg; delete arg; while(1) { cout << "sockfd" << fd << "is severing the client" << endl; sleep(1); } return nullptr; } int main() { int listen_socket = socket(AF_INET , SOCK_STREAM , 0); if (listen_socket < 0) { cerr << "listen_socket error" << endl; exit(1); } struct sockaddr_in local; memset(&local , '\0' , sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(PORT); local.sin_addr.s_addr = INADDR_ANY; if (bind(listen_socket , (struct sockaddr*)&local , sizeof(local)) < 0) { cerr << "bind error" << endl; exit(2); } if(listen(listen_socket , NUM) < 0) { cerr << "listen error" << endl; exit(3); } // peer struct sockaddr_in peer; memset(&peer , '\0' , sizeof(peer)); socklen_t len = sizeof(peer); for(;;) { int sockfd = accept(listen_socket , (struct sockaddr*)&peer , &len); if (sockfd < 0) { cerr << "accept error" << endl; continue; } cout << "get a new linl:" << endl; int* p = new int(sockfd); pthread_t tid; pthread_create(&tid , nullptr , RUN , (void*)p); } return 0; }
我们使用netstat查看 可以发现两条连接 一条是客户端的连接 一条是服务器的连接
当我们现在将Telnet推出后客户端维护的连接状态会变为FIN_WAIT2 (因为它调用close函数到收到响应这段时间太短了 以至于我们捕捉不到FIN_WAIT1状态) 服务器的状态就会变为CLOSE_WAIT状态 因为我们在内部没有调用close函数 所以说服务器不会发起最后的第三次挥手
理解TIME_WAIT状态
当客户端和服务器进行通信的时候 客户端调用close函数关闭对应的文件描述符 如果服务器收到后也调用close函数进行了关闭 那么此时双方将正常完成四次挥手
但是主动发起四次挥手的一方 在四次挥手之后并不会立即进入COLSED状态 而是会短暂的进入TIME_WAIT状态中等待一段时间
我们继续刚刚的试验 由于telnet推出后服务器没有调用close函数 所以说客户端的变为了TIME_WAIT 服务器的状态变为了CLOSE_WAIT
之后如果我们想要见到TIME_WAIT状态 我们就只需要方服务器调用close函数就可以 但是我们在服务器端的源码中并没有调用close函数 所以说我们要想办法关闭服务器打开的文件描述符
我们在前面一篇博客 TCP协议详解中说明了TCP连接异常断开的几种情况 其中我们说过 如果进程崩溃那么其实是和正常完成四次挥手没有区别的 而进程崩溃实际上就是操作系统给进行发送了信号 所以说我只需要我们使用键盘让OS向该进程发送信号就能够模拟服务器正常调用close函数的情况
再刨根问底一点 为什么操作系统向进程发送信号之后就相当于会调用close函数呢? 这里其实是因为文件描述符的生命周期是随进程的 而进程在被操作系统杀死后操作系统会回收资源 这些资源中肯定就包括文件描述符了
所以说此时我们先让telnet退出 紧接着让服务器退出 就能见到TIME_WAIT状态了
解决TIME_WAIT状态引起的bind失败的方法
我们在前面介绍过 四次挥手的时候主动发起挥手一方在四次挥手结束的时候会进入TIME_WAIT状态一段时间
如果服务器在有客户端的情况下主动退出了 就相当于是服务器先进行了四次挥手 那么服务器就将要进入TIME_WAIT一段时间
如果我们此时想要重新启动服务器绑定该端口号我们会发现我们绑定失败了
这是因为在TIME_WAIT期间 这个连接并没有被完全释放 这也就意味着我们的端口是被占用着的 此时服务器想要继续绑定该端口号就只能等待TIME_WAIT时间结束
当时我们服务器崩溃时最重要的就是让服务器重新启动并且是绑定原来的端口号 (因为一般一些服务的端口号都是固定的 如果你修改了端口号的话在网络中别人可能就不知道你了)如果想要让服务器崩溃后在TIME_WAIT期间也能立马重新启动 需要让服务器在调用socket函数创建套接字后 继续调用setsockopt函数设置端口复用 这也是编写服务器代码时的推荐做法
setsockopt函数
setsockopt函数可以设置端口复用 该函数的函数原型如下:
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
返回值说明:
- 设置成功返回0 失败返回-1 同时错误码会被设置
参数说明:
- sockfd:需要设置的套接字对应的文件描述符
- level:被设置选项的层次 比如在套接字层设置选项对应就是SOL_SOCKET
- optname:需要设置的选项 该选项的可取值与设置的level参数有关
- optval:指向存放选项待设置的新值的指针
- optlen:待设置的新值的长度
我们这里要设置的就是监听套接字 将监听套接字在套接字层设置端口复用选项SO_REUSEADDR 该选项设置为非零值表示开启端口复用
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
当我们主动发送信号让服务器崩溃之后我们立刻再次启动服务器 我们发现此时就不会出现bind error错误了
理解listen的第二个参数
在我们编写TCP套接字服务器的时候 在进行了套接字的创建和绑定之后需要调用listen函数将服务器变为监听状态 在这之后我们就能使用accept函数建立连接了
我们在调用listen函数的时候需要输入两个参数 第一个参数是我们要设置为监听状态的套接字 第二个参数是一个数字 它表示服务器可以建立的最大连接数
下面我们会通过一个试验来试验下listen的第二个参数是不是这个意思 试验步骤如下 :
- 首先编写tcp套接字的服务器代码 关于我们这次的代码 我们只需要创建套接字 绑定 监听但是服务器初始化之后我们不调用accept函数建立连接
- 为了方便验证 我们这里将listen的第二个参数设置为2
服务器代码如下
#include <iostream>
using namespace std;
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
using namespace std; int main()
{ // create socket int listen_sockfd = socket(AF_INET , SOCK_STREAM , 0); if (listen_sockfd < 0 ) { cerr << "socket error" << endl; exit(1); } int opt = 1; setsockopt(listen_sockfd , SOL_SOCKET , SO_REUSEADDR , &opt , sizeof(opt)); // bind struct sockaddr_in local; memset(&local , '\0' , sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(8081); local.sin_addr.s_addr = INADDR_ANY; if (bind(listen_sockfd , (struct sockaddr*)&local , sizeof(local)) < 0 ) { cerr << "bind error" << endl; exit(2); } // listen listen(listen_sockfd , 2); for(;;) { ; } return 0;
}
在运行./sever
服务之后 我们可以使用 netstat -nlp
t命令 来查看处于监听状态的进程
接下来使用Postman向我们的服务器发送一个请求
关于怎么使用Postman大家可以参考这个视频
如何使用Postman
说明: 需要说明的是 我们这里之所以不选用浏览器(浏览器的就是基于TCP协议的) 是因为浏览器如果没有收到服务端的响应可能会重复发送 这个现象在我们介绍应用层的HTTP协议的时候有涉及
我们通过Postman发起请求之后可以使用下面的命令查看建立完毕的连接
netstat -npt | head -2 && netstat -npt | grep 8081
我们可以发现该连接现在处于ESTABLISHED状态 接下来我们继续时候Postman发送第二 三个请求
由于我们的服务器并没有调用accept函数获取已经建立好的连接 所以说服务器已经建立好的连接是三个
我们继续尝试下使用Postman发送第四个请求
我们查看当前服务器的网络连接之后发现了这样子的现象
此时我们并没有增加ESTABLISHED状态了 而是增加了一个SYN_RECV状态
而我们回看三次握手的场景
我们可以发现SYN_RECV状态出现在客户端给服务器发送建立连接请求但是服务器没有响应的时候
也就是说刚刚的Postman发送请求之后 我们服务器并没有回应客户端的请求
过一段时间之后我们继续服务建立连接的状态
我们可以发现在一段时间过后三次握手失败 该SYN_RECV连接就被自动释放了
而在Postman上表现为这样子
总结下上面的试验现象
- 不论有多少的客户端请求连接 最终在我们的服务器端只会有三个连接建立成功
- 当发来第四个SYN请求的时候 服务只是收到了却不做响应 等待三次握手失败后自动释放连接
listen的第二个参数
我们直接对于上面的现象给出解释
实际上我们在进行TCP连接管理的时候会用到两个连接队列
- 全连接队列(accept队列) 全连接队列用于保存处于ESTABLISHED状态 但没有被上层调用accept取走的连接
- 半连接队列 半连接队列用于保存处于SYN_SENT和SYN_RCVD状态的连接 也就是还未完成三次握手的连接
我们在这里谈及Listen第二个参数的原因是 在linux操作系统中 全连接队列的长度就等于Listen的第二个参数+1 所以说我们服务器的Listen参数设置为2 此时服务器的全连接队列长度就为3 也就是说此时能够建立ESTABLISHED状态的连接就为3个
而当全连接队列满了之后服务器此时就不能够建立ESTABLISH连接了 此时如果客户端请求建立连接那么服务器不会进行SYN+ACK响应 而是会将该连接放在半连接队列中 状态变为SYN_RECV 在一段事件后如果三次握手还没有成功则会自动释放连接
此外如果半连接队列也满了 服务器会自动拒绝所有连接请求
如果被accept了全连接队列减少一个连接吗?
如果全连接中的队列被accept之后全连接队列会减少的
我们可以把服务器想象成一个饭店 这个饭店可以提供128人吃饭(服务器可以接收的连接数默认是128)
但是这个饭店太火爆了 几乎去的时候人都是满的 (参考下海底捞) 于是饭店老板便在饭店外面放了X+1个椅子(X为Listen的第二个参数 ) 坐在饭店椅子上的人就可以认为他们正处于EXTABLISHED状态 当饭店里面有人吃饭完饭店服务员就是使用accept函数让他们进来吃饭 此时我们就可以认为全连接队列少了一个连接 (此时在旁边站着的 也就是半连接队列的人就可以坐上椅子 变为EXTABLISHED状态)
为什么底层要维护连接队列?
其实我们在服务器压力较大的时候才能看出来连接队列的作用 我们上面的例子就能很好的说明原因 如果饭店没有满员的话服务员完全可以直接使用accept函数让人进去吃饭 没有必要让别人坐在外面的椅子上了
事实上 在服务器运行的时候我们就预先在服务器上面预先创建了多个线程 当主线程从底层accept上来连接之后这些连接就会交由其他线程处理
在服务器不繁忙的情况下连接一旦在底层建立好就会交由我们的服务线程处理了
可是当服务器繁忙的时候如果没有连接队列 我们的服务器就会拒绝其他的连接请求
如果拒绝之后我们的线程有处理完毕手上的连接了 但是此时没有任何新的连接请求发过来 那么此时该线程就会处于闲置状态
所以说连接队列的存在是必要的 它能够保证我们的服务器满负荷工作
连接队列长度问题
全连接队列的长度我们一般设置为5
事实上全连接的队列的长度不能太长也不能太短
如果说全连接的队列长度太长
- 这就意味着位于队列尾部的连接需要较长的时间才能享受到服务 此时客户端的请求需要较长时间才能收到响应
- 服务器维护这些连接也需要成本 如果能够维护很长的连接不如直接扩建“饭店”了
当然服务器全连接的长度也不能太短 比如说如果全连接队列的长度只有一 那么在连接数很大的情况下和没有连接队列没什么两样
全连接队列的长度
全连接队列的长度由两个值决定
- 用户层调用listen时传入的第二个参数backlog
- 系统变量net.core.somaxconn 默认值为128
事实上我们全连接队列的长度就等于这两个队列长度的较小值+1
SYN洪水攻击
在这个小节中我们会讲解SYN洪水攻击是什么以及应用层和传输层防范SYN洪水攻击的方式
正常三次握手方式
如上图所示
- 首先客户端会向服务器发送SYN连接请求 服务器收到之后会响应SYN+ACK给客户端并且将该连接放入半连接队列中
- 此时如果客户端接收到了服务器发送的SYN+ACK报文并回复ACK给服务器 那么服务器就会将该连接从半连接队列中放到全连接队列中
- 此时上层就可以通过调用accept函数 从全连接队列当中获取建立好的连接了
SYN洪水攻击
异常情况:
- 客户端在发起连接建立请求后突然死机或掉线 那么服务器发出的SYN+ACK就得不到对应的ACK应答
- 这时候服务器由于收不到对方的ACK就会触发超时重传机制 如果经过多次重发之后还是收不到ACK应答此时服务器就会关闭这个连接 我们将该时间称为SYN timeout
- 在SYN timeout时间内这个连接一直维护在半连接队列中
此时服务器虽然需要短暂维护这些异常连接 但这种情况毕竟是少数 不会对服务器造成太大影响
但是如果有用户恶意大量模拟这些情况
- 此时服务器就需要一个非常大的半连接队列 并且实际上这些连接都是无用的
- 当半连接队列被占满之后 新来的连接都会被服务器直接拒绝
- 我们把这种发送大量的SYN 但是却不对服务器的 SYN+ACK请求进行响应的行为 叫做SYN洪水攻击
如何防范SYN洪水攻击呢?
防范SYN洪水攻击的时候肯定不光要考虑传输层 还要考虑应用层
应用层:
- 我们应用层可以建立黑名单机制 如果一个ip在短时间内发送了大量的SYN请求我们就可以将这个ip封禁 对于此ip发送的SYN请求一律置之不理
传输层:
- 增大半连接队列长度 但是其实这个方案能够起到的效果很少 因为都说了是SYN洪水肯定 肯定能够发送大量的SYN请求如果我们增大长度对方只需要发送更多的SYN请求就好了
- 减少超时重发的次数 同样的这个也是治标不治本的做法
传输层最有效的方式应该是引入syncookie机制 当服务器收到一个连接建立请求后 会根据这个SYN包计算出一个cookie值 将其作为将要返回的SYN+ACK包的初始序号 然后将这个连接放到一个暂存队列当中
当服务器收到客户端的ACK响应时 会提取出当中的cookie值进行对比 对比成功则说明是一个正常连接 此时该连接就会从暂存队列当中移到全连接队列供上层读取
引入了syncookie机制的好处:
- 引入了syncookie机制之后 这些异常连接就不会堆积到半连接队列中了 而是会放在暂存队列中
- 对于正常的SYN请求 一般会立刻对于服务器的SYN+ACK进行ACK应答 所以说会很快建立正常连接成功
- 而异常连接或者说是SYN洪水攻击 不会对于服务器的SYN+ACK请求进行ACK应答 所以说异常连接都会堆积到队列中
TCP和UDP
TCP和UDP协议对比
在对比他们之间我们首先要知道TCP和UDP协议是什么
TCP协议
TCP协议叫做传输控制协议(Transmission Control Protocol)它是一种面向连接的 可靠的 基于字节流的传输层协议
TCP协议是面向连接的 如果两台主机之间想要进行通信就需要先建立连接 连接建立成功之后进行数据传输
其次TCP协议是保证可靠的协议 如果数据在传输的过程中出现了丢包 乱序等问题 TCP协议都有自己的相解决方案
UDP协议
UDP协议叫做用户数据报协议(User Datagram Protocol) 它是一种无需连接的 不可靠的 基于数据包的传输层协议
使用UDP协议的时候无需建立连接 如果两台主机之间想要进行通信 那么直接将数据发送给对端主机就可以了
上面的描述其实也就概括了UDP协议是不可靠的 如果数据在传输的过程中出现了丢包 乱序等问题 UDP协议都不知道 更别说解决了
TCP/UDP对比
TCP协议是可靠的 UDP协议是不可靠的 但是这并不意味着TCP协议比UDP协议要好 UDP协议不可靠也就意味着UDP协议足够简单 TCP可靠也就意味着TCP所做的准备工作也就越多 但是需要注意的是TCP所做的准备工作越多并不意味着TCP越慢 事实上它们的效率是差不多的
- TCP常用于需要可靠传输的情况 比如说文件传输 重要更新等场景
- UDP常用于对高速传输和实时性较高的通信领域 比如说QQ 视频传输等 此外UDP也可以用于广播
UDP协议和TCP协议不存在孰优孰劣这种说法 只有在某个场景中使用哪个协议更合适
用UDP实现可靠传输(经典面试题)
这里有一道经典的面试题:
使用UDP来实现可靠传输
我们提到传输层的可靠协议就能想到TCP协议了 所以说这道面试题的本质其实是在考查你对于TCP协议的了解程度
当我们知道了这道面试题的时候我们首先应该问面试官具体的应用场景 之后根据场景的需要往TCP协议上套
比如说:
- 引入序列号 保证数据按序到达
- 引入确认应答 确保对端接收到了数据
- 引入超时重传 如果隔一段时间没有应答 就进行数据重发
- … …
相关文章:
【Hello Network】TCP协议相关理解
作者:小萌新 专栏:网络 作者简介:大二学生 希望能和大家一起进步 本篇博客简介:补充下对于TCP协议的各种理解 TCP协议相关实验 TCP相关试验理解CLOSE_WAIT状态理解TIME_WAIT状态解决TIME_WAIT状态引起的bind失败的方法理解listen的…...
实施CRM目标有哪几步?如何制定CRM目标?
在当今竞争激烈的商业环境中,与客户建立持久的关系是企业重要的工作。CRM客户管理系统能有效帮助企业管理优化流程、管理客户,提高销售成功率,推动收入增长。那么您了解如何实施CRM吗?下面说说实施CRM目标是什么,如何设…...
船舶建造概论(船舶建造工艺任务与现代造船模式)
船舶建造概论 1 船舶建造概论1.1 船舶建造工艺主要任务1.2 船舶建造流程(1)钢材料预处理(2) 钢材料加工(3)分段制作(4)总段制作(5)船台合拢(6&…...
项目内训(2023.5.6)
目录 Nacos是什么? 领域模型是什么? domain模块一般是干什么的? 在小乌龟中合并其他分支的作用是什么? nacos的配置文件 服务集群、服务提供、服务更加灵活庞大、消费服务、访问比较麻烦,A和B服务一起访问 系统结…...
【操作系统OS】学习笔记第二章 进程与线程(下)【哈工大李治军老师】
基于本人观看学习 哈工大李治军老师主讲的操作系统课程 所做的笔记,仅进行交流分享。 特此鸣谢李治军老师,操作系统的神作! 如果本篇笔记帮助到了你,还请点赞 关注 支持一下 ♡>𖥦<)!! 主页专栏有更多࿰…...
Linux命令集(Linux文件管理命令--rmdir指令篇)
Linux命令集(Linux文件管理命令--rmdir指令篇) Linux文件管理命令集(rmdir指令篇)5. rmdir(remove directory)1. 删除空的目录 folder12. 强制删除目录 folder1(包括非空目录)3. 递归删除目录及其目录下所有…...
在技术圈超卷的当下,学历到底是敲门砖还是枷锁?
前言 最近,突然之间被“孔乙己文学”刷屏了,短时间内“孔乙己文学”迅速走红,孔乙己是中国文学中的一位经典人物,他的长衫被认为是他的象征之一,孔乙己的长衫折射出很多现象,既有社会的,也有教育…...
Linux cgroup
前言 Cgroup和namespace类似,也是将进程进程分组,但是目的与namespace不一样,namespace是为了隔离进程组之前的资源,而Cgroup是为了对一组进程进行统一的资源监控和限制。 Cgroup的组成 subsystem 一个subsystem就是一个内核模…...
PID整定二:基于Ziegler-Nichols的频域响应
PID整定二:基于Ziegler-Nichols的频域响应 1参考2连续Ziegler-Nichols方法的PID整定2.1整定方法2.2仿真示例 1参考 1.1根轨迹图的绘制及分析 1.2计算机控制技术01-3.4离散系统的根轨迹分析法 1.3PID控制算法学习笔记 2连续Ziegler-Nichols方法的PID整定 2.1整定…...
【tkinter 专栏】专栏前言
文章目录 前言本章内容导图1. tkinter 工具及特点2. 为什么使用 Python 进行 GUI 设计?2.1 Python 可以做什么2.2 使用 tkinter 可以干什么?3. 如何学习使用 tkinter 进行 GUI 设计?4. 开发环境搭建4.1 Python 的版本4.2 安装 Python4.2.1 下载 Python 安装包4.2.2 安装 Pyt…...
解决Linux中文字体模糊的4种方法
在Linux中,字体是非常重要的一部分,因为它们直接影响到用户的视觉体验。如果Linux字体模糊不清,那么用户将很难阅读文本,这将极大地降低用户的工作效率。本文将介绍Linux Mint中文字体模糊的问题,并提供一些解决方案。…...
【Android入门到项目实战-- 7.3】—— 如何调用手机摄像头和相册
目录 一、调用摄像头拍照 二、打开相册选择照片 学完本篇文章可以收获如何调用手机的摄像头和打开手机相册选择图片功能。 一、调用摄像头拍照 先新建一个CameraAlbumTest项目。 修改activity_main.xml,代码如下: 按钮打开摄像头,ImageView将拍到的…...
浅聊AIOT
引言 IoT是(Internet of Things)的简称,也就是人们常说的物联网;随着智能硬件的发展和推广,制造成本也随之下降,很多的厂家也慢慢地拥抱网络互联,逐步实现设备互联,也就进入了人们常说的万物互联时代。虽然…...
Python之模块和包(九)
1、模块 1、模块概述 模块是一个包含了定义的函数和变量等的文件。模块可以被程序引入,以使用该模块中的函数等功能。通俗讲:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块。 2、import 在P…...
C++-----动态规划
目录 一、动态规划的基本思想 二、设计动态规划法的步骤 三、动态规划问题的特征 4.1 矩阵连乘积问题 4.1.1 分析最优解的结构 4.1.2 建立递归关系 4.1.3 计算最优值 4.1.3 计算最优值 4.1.3 构造最优解 4.2 动态规划算法的基本要素 4.2.1 最优子结构 4.2.2 重叠子问题 …...
2.2 Linux控制台访问CLI
系列文章目录 第1章 Linux Shell简介 第2章 Shell基础 <本章所在位置> 第3章 Bash Shell基础命令 第4章 Bash Shell命令进阶 第5章 Linux Shell深度理解 第6章 Linux环境变量 第7章 Linux文件权限 第8章 Linux文件系统的管理 第9章 Linux软件安装 第10章 Linux文本编辑器…...
代码随想录补打卡 509 斐波那契数列
代码如下 //斐波那契数列的第0项是0 第一项是1 func fib(n int) int { if n < 1 { return n } dp : make([]int,n1) dp[0] 0 dp[1] 1 for i : 2 ; i < n ; i { dp[i] dp[i-1] dp[i-2] } return dp[n] } 70 爬楼梯 代码如下 func climbStairs(n int) int …...
【每日一题Day195】LC1003检查替换后的词是否有效 | 栈
检查替换后的词是否有效【LC1003】 给你一个字符串 s ,请你判断它是否 有效 。 字符串 s 有效 需要满足:假设开始有一个空字符串 t "" ,你可以执行 任意次 下述操作将 t 转换为 s : 将字符串 "abc" 插入到 t…...
简单理解什么是序列化
为什么要序列化 序列化的目的就是为了对象可以在网络层进行传输, 比如通过后端传给前端数据。 什么是序列化 我们以Java为例。 序列化就是把对象转化为可传输的字节序列过程,这个字节序列可以是字符串,比如JSON格式的字符串,把…...
Django初识
1、简介 Django,是用python语言写的开源web开发框架,并遵循MVC设计。劳伦斯出版集团为了开发以新闻内容为主的网站,而开发出来了这个框架,于2005年7月在BSD许可证下发布。这个名称来源于比利时的爵士音乐家DjangoReinhardt&#…...
ARM嵌入式编译器-volatile关键字对编译器优化的影响
volatile限定符告知计算机,其他agent(而不是变量所在的程序)可以改变该变量的值。通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。要求编译器不要对其描述的对象作优化处理,对它的读写都需要从内存中访问。 使用…...
销售数据分析怎么做?这篇文章说清楚了
如何分析销售数据?分析销售数据有哪些指标?销售数据分析有什么作用? 销售数据是不是得通过数据分析软件啊? 本文将为您解答疑惑—— 一、分析销售数据的指标 从两个层面上来讲,一个是对销售情况的整体把控…...
二十六、ISIS技术总结
文章目录 ISIS 概述一、路由协议总结1、路由优先级2、分类 二、ISIS 协议特点1、特点2、ISIS 路由器的种类 三、ISIS 配置1、基础配置2、network-entity含义3、router id 和系统id转换规则 四、ISIS 开销计算1、Narrow 模式2、Wide 模式 五、 ISIS 和 OSPF 的区别 ISIS 概述 I…...
三菱m70 m80系统解密 三菱m80机床到期解锁
我们从操作系统的发展讲起,为什么要有线程这个概念出现。《Java多线程学习笔记(一) 初遇篇》讲Java平台下的线程,如何使用和创建,以及引入线程后所面临的问题,为了解决线程安全问题,Java引入的机制,这也是《…...
InnoDB 磁盘结构之数据字典和双写缓冲区
数据字典(InnoDB Data Dictionary) MySQL中,数据字典包括了: 表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、MySQL版本信息、存储过程、触发器等内容 InnoDB数据字典由内部系统表组成,这些表包含用于查找表…...
Django模型层part two - 多表关系创建和多表操作
前言 继续上面一篇文章的内容,本文介绍多表操作。使用django ORM可以创建多表关系,并且也支持多张表之间的操作,以创建表关系和查询两部分说明django ORM的多表操作。以作者、图书、出版社和作者信息几张表作为案例进行说明。 创建表关系 …...
智能优化算法:浣熊优化算法-附代码
智能优化算法:浣熊优化算法 文章目录 智能优化算法:浣熊优化算法1.浣熊优化算法1.1 初始化1.2 阶段一:狩猎和攻击(探索阶段) 2.实验结果3.参考文献4. Matlab 摘要:浣熊优化算法(Coati Optimizat…...
【51单片机】数码管显示(样例展示以及异常分析)
🎊专栏【51单片机】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 ⭐数码管 比如要显示“6”,那么下面图片中,AFEDCG=1,B=0 对应到数码管上,就是 ⭐原理 🎊P22~P24控制LED1~...
Android InputChannel事件发送接收系统分析
本文基于Android12。 InputChannel表示其他进程通过文件描述符传递输入事件到View的通道,因为需要跨进程传输,实现了Parcelable序列化接口,所以也能够理解Java层的InputChannel后面为什么使用copyTo()方法初始化。 输入事件的接收方是View&…...
Java时间类(五)-- LocalDate()类
目录 引言: 1. LocalDate的概述: 2. LocalDate的常用方法: 引言: (1)Date存在的缺陷: 如果不格式化,打印出的日期可读性差://获取当前时间Date date = new Date();System.out.println("date = " + date); //date = Wed May 03 22:30:24 CST...
网站突然消失了/域名怎么注册
构造思路: 1.socket 连接获取 Banner --> 2.与存在漏洞的 Banner 集合进行对比 中间细节: 1.需要判断用户所给参数是否存在且是否有读权限 2.需要判断 Banner 是否存在,处理异常 学习 os sys socket 各个模块的基本使用 直接上代码&…...
网站开发商品管理/seo外链友情链接
非打印字符 非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列: 字符描述\cx匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 c 字符…...
上海进博会?/廊坊自动seo
ASP.NET Core断点续传 在ASP.NET WebAPi写过完整的断点续传文章,目前我对ASP.NET Core仅止于整体上会用,对于原理还未去深入学习,由于有园友想看断点续传在ASP.NET Core中的具体实现,于是借助在家中休息时间看了下ASP.NET Core是否…...
郑州网站制作多少钱/网络推销
先放两篇整理内置对象较全的博客: https://segmentfault.com/a/1190000011467723 https://www.cnblogs.com/liuluteresa/p/6413988.html 再来一篇面试题 https://blog.csdn.net/mino_miao/article/details/81167867 对下述定时器面试题中同步异步问题的详解…...
成都设计网站的公司名称/代理怎么引流推广
首先需要安装python环境 见本人发表的:python及pycharm安装python及pycharm安装_susan花雨的博客-CSDN博客 用pip install 安装 pip install xlrd pip install xlwt pip install xlsxwriter pip list查看 python合并多个表格:可以将每个表的不同she…...
网站建设模板源码/九易建网站的建站模板
所以:D 110-131(-21)10FFEDH(补码)故:转移转移指令第二字节为:EBH,第三字节为:FFH。5.7 某计算机有变址、间接和相对等三种寻址方式,设指令由操作码、寻址方式特征位和地址码三部分组成,且为单字长指令。设…...