Linux网络——TCP的运用
系列文章目录
文章目录
- 系列文章目录
- 一、服务端实现
- 1.1 创建套接字socket
- 1.2 指定网络接口并bind
- 2.3 设置监听状态listen
- 2.4 获取新链接accept
- 2.5 接收数据并处理(服务)
- 2.6 整体代码
- 二、客户端实现
- 2.1 创建套接字socket
- 2.2 指定网络接口
- 2.3 发起链接connect
- 2.4 发送数据并接收
- 2.5 整体代码
- 2.6 绑定问题
- 三、问题与改进
- 3.1 问题描述
- 3.2 解决方法
- 3.2.1 问题版本
- 3.2.2 多进程版
- 3.4.3 进程池(暂未实现)
- 3.4.4 多线程版
- 3.4.5 线程池版
- 四、TCP与UDP的对比
一、服务端实现
1.1 创建套接字socket
和上篇文章UDP的使用一致,创建套接字
调用系统接口socket函数,帮助我们创建套接字,本质是把文件和网卡关联起来
参数介绍:
domain
:一个域,标识了这个套接字的通信类型(网络或者本地)
只用关注上面三个类,第一个与第二个
AF_UNIX/AF_LOCAL
表示本地通信,而AF_INET
表示网络通信
type
:套接字提供服务的类型
我们用UDP实现,所以使用SOCK_DGRAM
protocol
:想使用的协议,默认为0即可,因为前面的两个参数决定了,就已经决定了是TCP还是UDP协议了
返回值:
成功则返回打开的文件描述符(指向网卡文件,其实就是文件描述符),失败返回-1
创建套接字的本质其实就是创建了一个文件描述符,并返回该文件描述符的值
只是该文件描述符是用于对应服务的网路数据传输
// 1. 创建套接字_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _listensock);exit(1);}lg.LogMessage(Info, "socket success, sockfd : %d\n", _listensock);
1.2 指定网络接口并bind
和上篇文章UDP的使用一致,服务端需要手动bind
参数介绍:
socket
:创建套接字的返回值
address
:通用结构体(上一章Linux网络——网络套接字有详细介绍)
address_len
:传入结构体的长度
我们要先定义一个sockaddr_in
结构体,将结构体内对应的字段填充好,再将结构体作为参数传递
struct sockaddr_in {short int sin_family; // 地址族,一般为AF_INET或PF_INETunsigned short int sin_port; // 端口号,网络字节序struct in_addr sin_addr; // IP地址unsigned char sin_zero[8]; // 用于填充,使sizeof(sockaddr_in)等于16
};
创建结构体后要先清空数据(初始化),我们可以用memset,也可以用系统接口:
#include <strings.h>void bzero(void *s, size_t n);
填充端口号的时候要注意端口号是两个字节的数据,涉及到大小端问题
在计算机中的普遍规定:在网络中传输的数据都是大端的
所以为了统一,无论我们机器是大端还是小端,在调用接口的时候,都将IP与端口号从主机序列转化为网路序列
端口号的接口
#include <arpa/inet.h>
// 主机序列转网络序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
// 网络序列转主机序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
IP的接口
对于IP,其实有两步:首先将字符串转换为整型,再解决大小端问题
系统给了直接能解决这两个问题的接口
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int inet_aton(const char *cp, struct in_addr *inp);in_addr_t inet_addr(const char *cp);// 点分十进制字符串in_addr_t inet_network(const char *cp);char *inet_ntoa(struct in_addr in);struct in_addr inet_makeaddr(int net, int host);in_addr_t inet_lnaof(struct in_addr in);in_addr_t inet_netof(struct in_addr in);
这里的inet_addr
就是把一个点分十进制的字符串转化成整数再进行大小端处理
代码:
// 2. 指定网络接口并bindstruct 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;int n = bind(_listensock, (struct sockaddr *)&local, sizeof(local));if (n != 0){lg.LogMessage(Fatal, "bind error\n");exit(2);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);
2.3 设置监听状态listen
这里TCP跟UDP有所不同
要把socket套接字的状态设置为listen状态,只有这样才能一直获取新链接,接收新的链接请求
举个例子:
我们买东西如果出现了问题会去找客服,如果客服不在那么就回复不了,所以规定了客服在工作的时候必须要时刻接收回复消息,这个客服所处的状态就叫做监听状态
关于第二个参数backlog后边讲TCP协议的时候介绍,目前先直接用
const static int default_backlog = 1;// 3. 设置socket为监听状态int m = listen(_listensock, default_backlog);if (m != 0){lg.LogMessage(Fatal, "listen error\n");exit(3);}lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);
做完这些,初始化工作就完成了,总代码
void Init(){// 1. 创建套接字_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _listensock);exit(1);}lg.LogMessage(Info, "socket success, sockfd : %d\n", _listensock);// 2. 指定网络接口并bindstruct 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;int n = bind(_listensock, (struct sockaddr *)&local, sizeof(local));if (n != 0){lg.LogMessage(Fatal, "bind error\n");exit(2);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);// 3. 设置socket为监听状态int m = listen(_listensock, default_backlog);if (m != 0){lg.LogMessage(Fatal, "listen error\n");exit(3);}lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);}
2.4 获取新链接accept
上面初始化完毕,现在开始就是要运行服务端,而TCP不能直接发数据,因为它是面向链接的,必须要先建立链接。
参数介绍:
sockfd
文件描述符,找到套接字
addr
输入输出型参数,是一个结构体,用来获取客户端的信息
addrlen
输入输出型参数,客户端传过来的结构体大小
返回值:
成功返回一个文件描述符
失败返回-1
而我们知道sockfd本来就是一个文件描述符,那么这个返回的文件描述符是什么呢?
举个例子:
我们去吃饭的时候会发现每个店铺门口都会有人来招揽顾客,这个人把我们领进去门店后,然后他就会继续站在门口继续招揽顾客,而我们会有里面的服务员来招待我们,给我们提供服务
这里的揽客的人就是_listensock,而服务员就是返回值的文件描述符
意思就是_listensock的作用就是把链接从底层获取上来,返回值的作用就是跟客户端通信
从这里就知道了成员变量中的_listensock`并不是通信用的套接字,而是专门用来获取链接的套接字
void Start(){_is_running = true;while (_is_running){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, (struct sockaddr *)&peer, &len);if (sockfd < 0){lg.LogMessage(Fatal, "socket accept error");continue;}lg.LogMessage(Debug, "accept socket success, get a new sockfd: %d\n", sockfd);Service(sockfd);close(sockfd);}}
2.5 接收数据并处理(服务)
当客户访问服务器的时候,必定是想要完成某件事,并且得到某件事完成的结果,我们称这个过程为服务
服务端收到客户端发来的信息或请求后,进行分析判断,完成客户端想要完成的任务,并返回给客户端
我们这里写一个简单的运用,此处的服务就是读取发来的信息并发回给客户端
void Service(int sockfd){char buffer[1024];while (true){int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << "client say# " << buffer << std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error");break;}}}
2.6 整体代码
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "Log.hpp"
#include "nocopy.hpp"static const int default_fd = -1;
const static int default_backlog = 1;class TcpServer : public nocopy
{
public:TcpServer(const uint16_t port): _port(port), _listensock(default_fd), _is_running(false){}void Init(){// 1. 创建套接字_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _listensock);exit(1);}lg.LogMessage(Info, "socket success, sockfd : %d\n", _listensock);// 2. 指定网络接口并bindstruct 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;int n = bind(_listensock, (struct sockaddr *)&local, sizeof(local));if (n != 0){lg.LogMessage(Fatal, "bind error\n");exit(2);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);// 3. 设置socket为监听状态int m = listen(_listensock, default_backlog);if (m != 0){lg.LogMessage(Fatal, "listen error\n");exit(3);}lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);}void Service(int sockfd){char buffer[1024];while (true){int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << "client say# " << buffer << std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error");break;}}}void Start(){_is_running = true;while (_is_running){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, (struct sockaddr *)&peer, &len);if (sockfd < 0){lg.LogMessage(Fatal, "socket accept error");continue;}lg.LogMessage(Debug, "accept socket success, get a new sockfd: %d\n", sockfd);Service(sockfd);close(sockfd);}}~TcpServer(){}private:uint16_t _port;int _listensock;bool _is_running;
};#include "TcpServer.hpp"
#include <memory>void Usage(const std::string s)
{std::cout << "Usagr: " << s << " local_port" << std::endl;
}int main(int argc,char *argv[])
{if (argc != 2){Usage(argv[0]);return 1;}std::unique_ptr<TcpServer> tcpser = std::make_unique<TcpServer>(std::stoi(argv[1]));tcpser->Init();tcpser->Start();return 0;
}
二、客户端实现
2.1 创建套接字socket
// 1. 创建socketint _sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _sockfd);exit(1);}lg.LogMessage(Info, "socket success, sockfd : %d\n", _sockfd);
2.2 指定网络接口
// 2. 指定网络接口struct sockaddr_in send;memset(&send, 0, sizeof(send));send.sin_family = AF_INET;send.sin_port = htons(stoi(argv[2]));send.sin_addr.s_addr = inet_addr(argv[1]);
2.3 发起链接connect
参数说明:
这里的
addr
和addrlen
填入的是服务端信息
在UDP通信中,客户端在sendto的时候会自动绑定IP和port,TCP这里就是在connect的时候绑定
// 3. 进行连接int n = connect(_sockfd, (struct sockaddr *)&send, sizeof(send));
2.4 发送数据并接收
while (true){std::string inbuffer;std::cout << "Please Enter# ";getline(cin, inbuffer);int n = write(_sockfd, inbuffer.c_str(), inbuffer.size());if (n > 0){char buffer[1024];int m = read(_sockfd, buffer, sizeof(buffer) - 1);if (m > 0){buffer[m] = 0;cout << "get a echo messsge -> " << buffer << endl;}else if (m == 0 || m < 0){break;}}else{break;}}
2.5 整体代码
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "Log.hpp"void usage(std::string s)
{std::cout << "Usagr: " << s << " server_ip server_port" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){usage(argv[0]);return 0;}// 1. 创建socketint _sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _sockfd);exit(1);}lg.LogMessage(Info, "socket success, sockfd : %d\n", _sockfd);// 2. 指定网络接口struct sockaddr_in send;memset(&send, 0, sizeof(send));send.sin_family = AF_INET;send.sin_port = htons(stoi(argv[2]));send.sin_addr.s_addr = inet_addr(argv[1]);// 3. 进行连接int n = connect(_sockfd, (struct sockaddr *)&send, sizeof(send));while (true){std::string inbuffer;std::cout << "Please Enter# ";getline(cin, inbuffer);int n = write(_sockfd, inbuffer.c_str(), inbuffer.size());if (n > 0){char buffer[1024];int m = read(_sockfd, buffer, sizeof(buffer) - 1);if (m > 0){buffer[m] = 0;cout << "get a echo messsge -> " << buffer << endl;}else if (m == 0 || m < 0){break;}}else{break;}}return 0;
}
2.6 绑定问题
首先bind的作用是允许应用程序指定一个端口号用于监听传入的数据报或数据流
对于服务端:
需要绑定一个公开的端口号,允许大家访问,如果是随机的,其他人不知道,也就访问不了,所以服务端需要绑定
对于客户端:
客户端在给服务器发信息的同时,服务器也可能给客户端发信息,所以客户端为了监听服务器有没有给自己回信息也需要bind一个端口号用于监听,但客户端不需要显式的bind,因为客户端的端口号不会被所有人访问,别人不需要知道,所以OS自动帮我们bind一个随机的端口号,另外也是为了防止端口号重复,避免破坏唯一性
那么为什么前面服务端必须显示的绑定port呢?
因为服务器的端口号是众所周知的,不能改变,如果变了就找不到服务器了
而客户端只需要有就可以,只用标识唯一性即可
举个例子:
我们手机上有很多的app,而每个服务端是一家公司写的,但是客户端却是多个公司写的
如果我们绑定了特定的端口,万一两个公司都用了同一个端口号呢?这样就直接冲突了
OS会自动填充主机IP和随机生成端口号进行绑定(在发送数据的时候自动绑定)
所以创建客户端我们只用创建套接字即可
三、问题与改进
3.1 问题描述
上述图片是服务端的代码,由于整个服务端都是单进程的,所以这意味着在同一时间只能有一个客户端的链接能被accept,其他客户端的信息是接受不到的,因为一但某个客户端被成功accept了,那么单进程就会走到Service中,Service是一个死循环服务,所以只要客户端不退出,服务端是无法accept到其他链接的
3.2 解决方法
3.2.1 问题版本
3.2.2 多进程版
使用多进程,主进程进行accept,子进程进行Service服务
引入新问题:
主进程需要阻塞等待子进程退出,还是accept不了新连接
两个解决办法
- 用孙子进程执行服务
pid_t pid = fork();if (pid < 0){lg.LogMessage(Fatal, "fork error");close(sockfd);continue;}else if (pid == 0){close(_listensock);if (fork() == 0){Service(sockfd);close(sockfd);exit(0);}exit(0);}close(sockfd);waitpid(pid, nullptr, 0);
- 自定义信号,让父进程忽略子进程结束时发送给父进程的信号
signal(SIGCHLD,SIG_IGN);pid_t pid = fork();if (pid < 0){lg.LogMessage(Fatal, "fork error");close(sockfd);continue;}else if(pid == 0){close(_listensock);Service(sockfd);close(sockfd);exit(0);}close(sockfd);
3.4.3 进程池(暂未实现)
存在问题:
如果先创建子进程备用,子进程拿不到主进程accept的sockfd
3.4.4 多线程版
篇幅较长,Gitee连接:多线程版
3.4.5 线程池版
篇幅较长Gitee连接:线程池版
四、TCP与UDP的对比
对比UDP服务器,TCP服务器多了获取新链接和监听的操作
因为UDP是不可靠传输,而TCP是是可靠传输,所以TCP在传输数据之前会进行连接的建立
UDP和TCP的区别:
对于TCP协议有几个特点:
- 传输层协议
- 有连接(正式通信前要先建立连接)
- 可靠传输(在内部帮我们做可靠传输工作)
- 面向字节流
对于UDP协议有几个特点:
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
注意:
这里的可靠与不可靠并不是贬义词,而是中性词,可靠或者不可靠形容的是特点,而不是优劣
并不是说可靠传输就好用,而是要区分应用场景和需求
相关文章:
Linux网络——TCP的运用
系列文章目录 文章目录 系列文章目录一、服务端实现1.1 创建套接字socket1.2 指定网络接口并bind2.3 设置监听状态listen2.4 获取新链接accept2.5 接收数据并处理(服务)2.6 整体代码 二、客户端实现2.1 创建套接字socket2.2 指定网络接口2.3 发起链接con…...
Elasticsearch 数据存储底层机制详解
Elasticsearch 数据存储底层机制详解 Elasticsearch 的底层存储机制依赖 Lucene 来实现数据的组织和管理。下面从数据存储的 流转过程 和 管理机制 两个方面来详细说明。 1. 数据存储流程 当一个文档通过 REST API 被写入 Elasticsearch 时,会经历以下流程&#x…...
Spring Boot 中 Map 的最佳实践
在Spring Boot中使用Map时,请遵循以下最佳实践: 1.避免在Controller中 直接使用Map。应该使用RequestBody 接收-个DTO对象或者 RequestParam接收参数,然后在Service中处 理Map。 2.避免在Service中 直接使用原始的Map。应该使用Autowired 注入-个专门…...
es6 字符串每隔几个中间插入一个逗号
const insertCommaEveryNChars (str, n) > {// 将字符串转换为数组,以便我们可以更容易地操作每个字符const chars str.split();// 使用map遍历数组,并在每隔n个字符后插入逗号const result chars.map((char, index) > {// 检查当前位置是否是n…...
区块链共识机制深度揭秘:从PoW到PoS,谁能主宰未来?
区块链的技术背后,最大的挑战之一就是如何让多个分布在全球各地的节点在没有中心化管理者的情况下达成一致,确保数据的一致性和安全性。这一切都依赖于区块链的核心——共识机制。共识机制不仅决定了区块链的安全性、效率和去中心化程度,还对…...
SQL Server 新建 用户 登录失败。 (Microsoft SQL Server,错误: 18456)
新建用户后用SQLserver shen身份验证一直提示用户登录用户 登录失败。 (Microsoft SQL Server,错误: 18456)。 问题: 新建标题: 连接到服务器 无法连接到 DESKTOP-GKBXLEE。 其他信息: 用户 ‘’ 登录失败。 (Microsoft SQL Server,错误: 18456) 解…...
AW36518芯片手册解读(3)
接前一篇文章:AW36518芯片手册解读(2) 二、详述 3. 功能描述 (1)上电复位 当电源电压VIN降至预定义电压VPOR(典型值为2.0V)以下时,该设备会产生复位信号以执行上电复位操作&#x…...
有没有免费提取音频的软件?音频编辑软件介绍!
出于工作和生活娱乐等原因,有时候我们需要把音频单独提取出来(比如歌曲伴奏、人声清唱等、乐器独奏等)。要提取音频必须借助音频处理软件,那么有没有免费提取音频的软件呢?下面我们将为大家介绍几款免费软件࿰…...
一次医院RIS系统的升级
2020-03-11 目录 数据库升级... 1 数据结构升级... 1 系统配置... 2 WEB服务器准备... 3 启动ASP.NET State Service服务... 3 检查IIS. 4 发布站点... 4 添加应用程序池... 4 发布网站... 5 处理打印模板... 6 web.config的配置... 6 处理图片文件目录... 6 修改W…...
clickhouse测试报告
一、背景 针对当前实施的项目,面临着两个主要挑战:一是需要存储更详细的原始数据和中间数据,二是现有基于MySQL的数据存储解决方案在数据量增长时性能受限,特别是在进行跨年历史数据的即时分析时。为了解决这些问题…...
Elasticsearch安装和数据迁移
Elasticsearch安装和数据迁移 Elasticsearch安装 下载并解压Elasticsearch 首先下载Elasticsearch的tar.gz文件,并将其解压: wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.8.2-linux-x86_64.tar.gz tar -xzf elastics…...
K8s证书过期
part of the existing bootstrap client certificate is expired: 2023-11-27 12:44:12 0000 UTC 查看运行日志: journalctl -xefu kubelet 重新生成证书: #重新生成证书 kubeadm alpha certs renew all #备份旧的配置文件 mv /etc/kubernetes/*.conf…...
JSONException:java.lang.String cannot be converted to JSONObject异常的解决方法
在用org.json.JSONObject解析从网络获取的json数据时,遇到JSONException:java.lang.String cannot be converted to JSONObject,打印字符串,查看json字符串没有问题,研究了好长时间,终于找到问题,造成问题的…...
[源码解析] 模型并行分布式训练Megatron (2) --- 整体架构
link [源码解析] 模型并行分布式训练Megatron (2) --- 整体架构 目录 [源码解析] 模型并行分布式训练Megatron (2) --- 整体架构 0x00 摘要0x01 启动 1.1 分布式启动1.2 构造基础 1.2.1 获取模型1.2.2 获取数据集1.2.3 步进函数 1.2.3.1 广播数据0x02 Pretrain0x03 初始化 3.1 …...
kubeadm搭建k8s集群
前置环境: 准备三台虚拟机 192.168.1.104(用来做k8s的mater节点) 192.168.1.105(节点node2) 192.168.1.109(节点node3) 关闭防火墙 systemctl stop firewalld systemctl disable firewalld安装…...
家用无线路由器的 2.4GHz 和 5GHz
家中的无线路由器 WiFi 名称有两个,一个后面带有 “5G” 的标记,这让人产生疑问:“连接带‘5G’的 WiFi 是不是速度更快?” 实际上,这里的 “5G” 并不是移动通信中的 5G 网络,而是指路由器的工作频率为 5G…...
#渗透测试#漏洞挖掘#红蓝攻防#漏洞挖掘#未授权漏洞-Es未授权漏洞
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...
Windows 使用 非安装版MySQL 8
1.下载MySQL 8 https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.40-winx64.zip 2.创建my.ini 下载解压后,发现根目录没有my.ini文件,需手动创建 my.ini # For advice on how to change settings please see # http://dev.mysql.com/doc/refma…...
nginx Rewrite 相关功能
一、Nginx Rewrite 概述 定义 Nginx 的 Rewrite 模块允许对请求的 URI 进行重写操作。它可以基于一定的规则修改请求的 URL 路径,然后将请求定向到新的 URL 地址,这在很多场景下都非常有用,比如实现 URL 美化、网站重构后的 URL 跳转等。主要…...
2024年AI相关的论文写作经验(附实践资料下载)
在撰写AI相关的论文时,以下是一些实用的经验和技巧: 明确写作目标:在开始写作之前,明确你的论文类型(期刊论文、毕业论文等)和目标,这将影响你的写作方式和工具选择。 AI辅助文献检索ÿ…...
List详解
List详解 在Java中,List是一个接口,它继承自Collection接口。List接口为数据的有序集合提供了操作接口,其中可以包含重复的元素。这个接口的实现类以特定的方式存储元素,允许元素根据索引进行访问,同时还支持通过迭代…...
Flutter实现可拖拽操作Draggable
文章目录 1. Draggable 控件的构造函数主要参数: 2. Draggable 的工作原理3. 常见用法示例 1:基本的拖拽控件解释:示例 2:与 DragTarget 配合使用解释: 4. Draggable 的回调详解5. 总结 Draggable 是 Flutter 中一个用…...
【QSS样式表 - ⑥】:QPushButton控件样式
文章目录 QPushBUtton控件样式QSS示例 QPushBUtton控件样式 常用子控件 常用伪状态 QSS示例 代码: QPushButton {background-color: #99B5D1;color: white;font-weigth: bold;border-radius: 20px; }QPushButton:hover {background-color: red; }QPushButton:p…...
DPO(Direct Preference Optimization)算法解释:中英双语
中文版 DPO paper: https://arxiv.org/pdf/2305.18290 DPO 算法详解:从理论到实现 1. 什么是 DPO? DPO(Direct Preference Optimization)是一种直接基于人类偏好进行优化的算法,旨在解决从人类偏好数据中训练出表现…...
springboot495基于java的物资综合管理系统的设计与实现(论文+源码)_kaic
摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统物资综合管理系统信息管理难度大,容错率低&am…...
JavaScript语言的编程范式
JavaScript:面向对象与函数式编程的双重奏 在编程世界中,JavaScript 无疑是一颗璀璨的明星,它不仅主宰着前端开发领域,还在后端、桌面应用、甚至物联网设备上展现出了强大的生命力。JavaScript 的魅力在于其灵活多变的编程范式&a…...
MyBatis动态 SQL 的执行原理
MyBatis 动态 SQL 是 MyBatis 框架中的一个重要特性,它允许开发者根据条件动态地生成不同的 SQL 语句。通过使用动态 SQL,开发者可以根据传入的参数动态地构建 SQL 查询,这样就避免了写多个 SQL 语句,提升了代码的灵活性和可维护性…...
PostgreSQL自带的一个命令行工具pg_waldump
pg_waldump是PostgreSQL自带的一个命令行工具,用于以人类可读的形式显示PostgreSQL数据库集簇的预写式日志(Write-Ahead Logging,WAL)。以下是对pg_waldump的详细介绍: 一、主要用途 pg_waldump主要用于调试或教育目…...
K8s 常用资源介绍
在 Kubernetes 中,资源指的是可以在集群中管理的对象(Objects)。这些资源用来定义和控制应用、服务、以及集群的状态。以下是 Kubernetes 中常见的资源及其用途介绍: 1. 工作负载资源(Workloads Resources)…...
基于 Python 大数据的拼团购物数据分析系统的设计与实现
标题:基于 Python 大数据的拼团购物数据分析系统的设计与实现 内容:1.摘要 本文设计并实现了一个基于 Python 大数据的拼团购物数据分析系统。通过对拼团购物数据的收集、清洗和分析,系统能够为商家提供用户行为分析、商品销售情况分析等功能,帮助商家更…...
finalshell密码解密
finalshell密码解密 在线网站运行java https://c.runoob.com/compile/10/ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.N…...
利用Java爬虫速卖通按关键字搜索AliExpress商品
在这个信息爆炸的时代,数据的价值日益凸显。对于电商领域的从业者来说,能够快速获取商品信息成为了一项重要的技能。速卖通(AliExpress)作为全球领先的跨境电商平台,拥有海量的商品数据。本文将介绍如何使用Java语言编…...
每天40分玩转Django:Django缓存
一、Django缓存概述 在高并发的Web应用中,缓存是提高性能的重要手段。通过缓存频繁访问的数据,可以显著减少数据库查询和渲染模板的时间,从而加快响应速度,提升用户体验。Django提供了多层级的缓存方案,可以灵活地满足不同场景下的缓存需求。 Django支持的缓存方式包括: 视图…...
matrix-breakout-2-morpheus
将这一关的镜像导入虚拟机,出现以下页面表示导入成功 以root身份打开kali终端,输入以下命令,查看靶机ip arp-scan -l 根据得到的靶机ip,浏览器访问进入环境 我们从当前页面没有得到有用的信息,尝试扫描后台 发现有一个…...
第七节:GLM-4v-9b模型的视觉模型源码解读
文章目录 前言一、EVA2CLIPModel视觉编码模块结构二、PatchEmbedding图像分块源码解读三、GLM的transformer结构源码解读四、GLU映射方法源码解读前言 清华智普的GLM-4v-9b模型,作为优化的多模态大模型,特别适用于国内应用场景,解决了国外模型本地化不足的问题。本专栏提供…...
@RestControllerAdvice和@ControllerAdvice的区别
RestControllerAdvice 和 ControllerAdvice 都是 Spring 框架中的注解,用于定义全局的异常处理、数据绑定、模型属性共享等功能。它们的区别主要体现在返回值的处理和适用的场景。 1. ControllerAdvice 功能: ControllerAdvice 是 Spring MVC 提供的全局…...
c++ 类似与c# 线程 AutoResetEvent 和 ManualResetEvent的实现
在 C 中,没有直接类似于 C# 的 AutoResetEvent 和 ManualResetEvent 的类,但可以通过一些线程同步机制来实现类似的功能。C 提供了一些线程同步原语,如 std::condition_variable 和 std::mutex,这些可以用来模拟类似于 C# 中 Auto…...
简单贪吃蛇小游戏的设计与实现
文章目录 1、知识预备1.1 WIN32 API1.1.1 什么是WIN32 API1.1.2 了解部分WIN32 API1.1.2.1 控制台坐标1.1.2.2 控制台光标1.1.2.3 获取键盘按键情况 2.1 宽字符2.1.1 C语言的国际化2.1.2 宽字符的打印 2、 贪吃蛇游戏设计2.1 游戏开始2.2 游戏运行2.2.1 更新分数2.2.2 按键检测…...
动态规划<五> 子数组问题(含对应LeetcodeOJ题)
目录 引例 经典LeetcodeOJ题 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 6.第六题 7.第七题 引例 OJ传送门 Leetcode<53> 最大子数组和 画图分析: 使用动态规划解决 1.状态表示 dp[i]表示以i位置为结尾的所有子数组中的最大和 2.状态转移方程 子数组的问题可以…...
计算机网络——期末复习(4)协议或技术汇总、思维导图
思维导图 协议与技术 物理层通信协议:曼彻斯特编码链路层通信协议:CSMA/CD (1)停止-等待协议(属于自动请求重传ARQ协议):确认、否认、重传、超时重传、 (2)回退N帧协…...
在 RK3568 Linux 系统上使用 TUN 设备:详细教程
RK3568 是一个基于 ARM 架构的处理器,广泛应用于嵌入式系统和物联网设备。Linux 系统上的 TUN(网络隧道)设备提供了一个虚拟的网络接口,允许用户空间程序通过内核与网络栈进行交互。本文将详细介绍如何在 RK3568 上配置和使用 TUN 设备,适用于搭建 VPN 或容器网络等应用场…...
记录一次前端绘画海报的过程及遇到的几个问题
先看效果 使用工具 html2canvas import html2canvas from html2canvas// 绘画前的内容 我就不过多写了<div class"content" ref"contentRef" v-show"!imgShow"><img :src"getReplaceImg(friendObj.coverUrl)" alt"&qu…...
费舍尔信息矩阵全面讲述
费舍尔信息矩阵(Fisher Information Matrix) 费舍尔信息矩阵是统计学中一个非常重要的概念,尤其在参数估计、最大似然估计(MLE)和贝叶斯推断中具有广泛的应用。它反映了参数估计的不确定性程度,也可以用来…...
【CSS in Depth 2 精译_094】16.2:CSS 变换在动效中的应用(下)——导航菜单的文本标签“飞入”特效与交错渲染效果的实现
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…...
webpack3 webpack4 webpack5 有什么区别
性能优化 Webpack 3 性能优化主要依赖开发者手动配置各种插件。例如,在代码分割方面,需要通过CommonsChunkPlugin来实现公共模块的提取,其配置相对复杂。如果配置不当,可能会导致模块重复打包等问题,影响构建效率和最终…...
vue2 升级为 vite 打包
VUE2 中使用 Webpack 打包、开发,每次打包时间太久,尤其是在开发的过程中,本文记录一下 VUE2 升级Vite 步骤。 安装 Vue2 Vite 依赖 dev 依赖 vitejs/plugin-vue2": "^2.3.3 vitejs/plugin-vue2-jsx": "^1.1.1 vite&…...
[创业之路-206]:《华为战略管理法-DSTE实战体系》- 6-关键成功因素法CSF
目录 一、概述 1、定义与起源 2、关键成功因素的定义 3、关键成功因素的来源 4、关键成功因素的确认方法 5、关键成功因素法的步骤 6、关键成功因素法的应用 7、关键成功因素法的优势与局限性 二、 关键成功因素法CSF的应用 1、企业战略管理 2、项目管理 3、绩效管…...
WebRTC服务质量(08)- 重传机制(05) RTX机制
WebRTC服务质量(01)- Qos概述 WebRTC服务质量(02)- RTP协议 WebRTC服务质量(03)- RTCP协议 WebRTC服务质量(04)- 重传机制(01) RTX NACK概述 WebRTC服务质量(…...
Go的select的运行原理
Go语言中的select语句是一种专门用于处理多个通道(channel)操作的控制结构。其运行原理可以概括为以下几点: 1. 监听多个通道 select语句能够同时监听多个通道上的操作,这些操作可以是发送操作或接收操作。每个通道操作都对应se…...
操作002:HelloWorld
文章目录 操作002:HelloWorld一、目标二、具体操作1、创建Java工程①消息发送端(生产者)②消息接收端(消费者)③添加依赖 2、发送消息①Java代码②查看效果 3、接收消息①Java代码②控制台打印③查看后台管理界面 操作…...