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…...
Vue3之状态管理Vuex
Vuex作为Vue.js的官方状态管理库,在大型或复杂的前端项目中扮演着至关重要的角色。本文将从Vuex的原理、特点、应用场景等多个方面进行深入解析,并通过代码示例展示如何在Vuex中实现特定功能。 一、Vuex原理 Vuex是一个专为Vue.js应用程序开发的状态管…...
DPO(Direct Preference Optimization)算法解释:中英双语
中文版 DPO paper: https://arxiv.org/pdf/2305.18290 DPO 算法详解:从理论到实现 1. 什么是 DPO? DPO(Direct Preference Optimization)是一种直接基于人类偏好进行优化的算法,旨在解决从人类偏好数据中训练出表现…...
Hostapd2.11解析笔记
最近在调试Hostapd,尝试通过配置使能一个支持MLO的AP,不过不知道hostapd conf里面哪些选项开启后可以使能,所以对Hostapd做一个整体解析. 简介 hostapd 是用于接入点和身份验证服务器的用户空间守护程序。它实现 IEEE 802.11 接入点管理、IEEE 802.1X/WPA/WPA2/WPA3/EAP 身份…...
js控制文字溢出显示省略号
.text{display: -webkit-box;overflow: hidden;white-space: normal;text-overflow: ellipsis;word-wrap: break-word;-webkit-line-clamp: 2;-webkit-box-orient: vertical; }本人有个需求就是在一个盒子内有一段文本,然后控制文本显示两行,第二行要显示…...

WPF+MVVM案例实战与特效(四十七)-实现一个路径绘图的自定义按钮控件
文章目录 1、案例效果2、创建自定义 PathButton 控件1、定义 PathButton 类2、设计样式与控件模板3、代码解释3、控件使用4、直接在 XAML 中绑定命令3、源代码获取4、总结1、案例效果 2、创建自定义 PathButton 控件 1、定义 PathButton 类 首先,我们需要创建一个新的类 Pat…...

操作002:HelloWorld
文章目录 操作002:HelloWorld一、目标二、具体操作1、创建Java工程①消息发送端(生产者)②消息接收端(消费者)③添加依赖 2、发送消息①Java代码②查看效果 3、接收消息①Java代码②控制台打印③查看后台管理界面 操作…...
odoo中@api.model, @api.depends和@api.onchange 装饰器的区别
文章目录 1. api.model用途特点示例 2. api.depends用途特点示例 3. api.onchange用途特点示例 总结 在 Odoo 中,装饰器(decorators)用于修饰方法,以指定它们的行为和触发条件。api.model、api.depends 和 api.onchange 是三个常用…...

有哪些精益工具可以帮助企业实现转型?
为了在激烈的市场竞争中立于不败之地,许多企业开始寻求通过精益转型来优化运营、降低成本、提高效率和客户满意度。然而,精益转型并非一蹴而就,而是需要一系列精益工具的辅助,这些工具能够帮助企业识别浪费、优化流程、提升质量&a…...

以太网帧结构
以太网帧结构 目前,我们局域网当中应用最广的技术或者协议啊,就是以太网。我们首先来看一下以太网的真结构。这块内容这里边再系统的来给大家去展开说一下,以太网真格式就如下面这个图。所示前面有八个字节,是用于时钟同步的&…...

QT调用Sqlite数据库
QT设计UI界面,后台访问数据库,实现数据库数据的增删改查。 零售商店系统 数据库表: 分别是顾客表,订单详情表,订单表,商品表 表内字段详情如下: 在QT的Pro文件中添加sql,然后添加头…...

前端
前端页面 Web页面 PC端程序页面 移动端APP页面 ... HTML页面 HTML超文本标记页面 超文本:文本,声音,图片,视频,表格,链接 标记:由许多标签组成 HTML页面运行到浏览器上面 vscode便捷插件使用 vs…...

【Git】—— 使用git操作远程仓库(gitee)
目录 一、远程仓库常用命令 1、从远程仓库克隆项目 2、查看关联的远程仓库 3、添加关联的远程仓库 4、移除关联的远程仓库 5、将本地仓库推送到远程仓库 6、从远程仓库拉取项目 二、分支命令 1、查询分支 2、创建分支 3、切换分支 4、推送到远程分支 5、合并分支 …...
Paddler负载均衡器
Paddler负载均衡器 Paddler本身是用Go语言编写的,没有直接的Python接口,但可以通过以下方式在Python中使用: 执行命令行调用 在Python中可以使用 subprocess 模块来调用Paddler的命令行工具,实现负载均衡功能 。例如: import subprocessdef start_paddler_agent():com…...

OSCP - Proving Grounds - Slort
主要知识点 文件包含 windows的reveseshell 具体步骤 执行nmap,依旧是很多端口开放,尝试了ftp,smb等均失败 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-13 12:00 UTC Nmap scan report for 192.168.53.53 Host is up (0.00095s latency). Not sho…...
YoloV9改进策略:Head改进|DynamicHead,利用注意力机制统一目标检测头部|即插即用
摘要 论文介绍 本文介绍了一种名为DynamicHead的模块,该模块旨在通过注意力机制统一目标检测头部,以提升目标检测的性能。论文详细阐述了DynamicHead的工作原理,并通过实验证明了其在COCO基准测试上的有效性和效率。 创新点 DynamicHead模块的创新之处在于它首次尝试在一…...

BFD综合详细实验配置案例
前言 本实验的目的是通过配置BGP(边界网关协议)、OSPF(开放式最短路径优先协议)、VRRP(虚拟路由冗余协议)以及BFD(双向转发检测)等网络协议,模拟企业级网络环境中的多协…...

自然语言处理与知识图谱的融合与应用
目录 前言1. 知识图谱与自然语言处理的关系1.1 知识图谱的定义与特点1.2 自然语言处理的核心任务1.3 二者的互补性 2. NLP在知识图谱构建中的应用2.1 信息抽取2.1.1 实体识别2.1.2 关系抽取2.1.3 属性抽取 2.2 知识融合2.3 知识推理 3. NLP与知识图谱融合的实际应用3.1 智能问答…...

c# RSA加解密工具,.netRSA加解密工具
软件介绍 名称: c# RSA加解密工具,.netRSA加解密工具依赖.net版本: .net 8.0工具类型: WinForm源码下载 c# RSA加解密工具,.netRSA加解密工具 依赖项 WinFormsRSA.csproj <Project...
Metricbeat安装教程——Linux——Metricbeat监控ES集群
Metricbeat安装教程——Linux 一、安装 下载安装包: 官网下载地址:https://www.elastic.co/cn/downloads/beats/metricbeat 上传包到linux 切换到安装目录下 解压:tar -zxvf metricbeat-7.17.1-linux-x86_64.tar.gz 重命名安装文件夹 mv met…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...