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

基于udp协议的cs网络通信代码(echo版+命令行输入版+执行指令版),netstat指令

目录

引入

基础版

服务端

思路

头文件+log类

套接字的初始化

思路

代码 

服务器开始运行

思路

代码

注意点 -- ip地址和端口号的来源

ip地址的选择

本地环回地址

端口号

​编辑

运行情况

netstat -nlup

客户端

思路

初始化

思路

代码 

客户端的运行

思路 

代码

运行情况 

运行情况 

改成命令行输入

服务端

客户端

运行情况

远程让服务器执行命令

引入

分层

代码

运行情况 


引入

我们将使用udp协议+ipv4协议来写cs之间的网络通信

  • 将服务端运行在我们的云服务器上,其他机器可以运行客户端来向服务端发送消息
  • 服务端收到消息后,经过一定处理直接发回给相应的客户端
  • 并且,我们将一步一步改善我们的代码,让他变得更加美观+拥有更多功能

基础版

服务端将收到的数据经过包装后直接回传给发送端,类似于echo

服务端

思路

封装成一个类,提供运行接口即可

启动后创建套接字,并保持等待数据的状态

头文件+log类

下面是用到的头文件:

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#include <cstring>#include <string>
#include <iostream>#include "Log.hpp"

以及用来提示消息的log类

数据格式 -- 时间+消息

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define INFO 0
#define DEBUG 1
#define WARNING 2
#define ERROR 3
#define FATAL 4 // 致命的错误#define SIZE 1024class Log
{
public:Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);printf("%s\n", logtxt);}~Log(){}private:std::string levelToString(int level){switch (level){case INFO:return "INFO";case DEBUG:return "DEBUG";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "NONE";}}
};

套接字的初始化

思路

首先创建出套接字文件

  • 这里使用udp协议+ipv4网络协议
  • 所以,可以确定socket函数中使用的参数为AF_INET, SOCK_DGRAM

然后我们需要构建一个存放了网络地址信息(ip+端口号+其他信息)的结构体

  • 传参传的是公共类型的结构体,但实际使用的还是那两个中的一个
  • 所以需要手动构建一个
  • 初始化可以用memset,也可以用bzero
  • 端口号需要在网络中传输,但它最初是在类内初始化的,是跟随系统字节序列来的

    我们无法保证系统一定是大端

  • 所以在将端口号传入结构体中时(网络中使用的端口号就是从这个结构体中存放的),需要转换为网络字节序列

  • 同理,ip地址也需要传输,所以也需要转换为网络序列

  • (ip地址和端口号的选择在后面讲)

我们虽然使用了系统提供的类型,但这里的变量依然是在用户空间中定义的,并没有和内核中的套接字产生关联

  • 套接字需要这个结构体中的数据,将数据传进内核的套接字里,才叫做绑定
  • 前面的都只是预备工作,实际的绑定工作由bind函数完成 -- 通过网络文件描述符和结构体对象,将文件信息和网络信息相关联

以上,服务器端的套接字就建立完成

代码 
    void init(){// 创建套接字文件sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){lg(FATAL, "socket create error, sockfd : %d,%s", sockfd_,strerror(errno));exit(SOCKET_ERR);}// 创建sockaddr结构struct sockaddr_in addr;socklen_t len = sizeof(addr);bzero(&addr, len);addr.sin_addr.s_addr = inet_addr(ip_.c_str());addr.sin_family = AF_INET;addr.sin_port = htons(port_);// 绑定套接字信息int res = bind(sockfd_, reinterpret_cast<const struct sockaddr *>(&addr), len);if (res < 0){lg(FATAL, "bind error, sockfd : %d,%s", sockfd_,strerror(errno));exit(BIND_ERR);}lg(INFO, "bind success, sockfd : %d", sockfd_);}

服务器开始运行

思路

服务端负责获取客户端发送的数据

  • 使用recvfrom函数获得数据报形式的数据(也就是定义一个缓冲区)
  • 且拿到发送方的网络地址信息(结构体),方便将响应数据发回给对方

拿到数据后,进行处理

  • 这里在消息前封装一些标识字段 -- ip+端口号+发送时间

处理完后,就发回给发送方

  • 使用sendto函数发送数据报
代码
std::string generate_id(const std::string ip, const uint16_t port){return "[" + ip + ":" + std::to_string(port) + "]";}
std::string process_info(const std::string &info){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char time_stamp[SIZE];snprintf(time_stamp, sizeof(time_stamp), "[%d-%d-%d %d:%d:%d]:",ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);std::string id = generate_id(ip_, port_);std::string res = id + time_stamp + info + "\n";return res;}void run(){init();// 开始收发数据char buffer[buff_size];std::string message;while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in src_addr;socklen_t src_len = sizeof(src_addr);// 获取数据ssize_t n = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&src_addr), &src_len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::string id = generate_id(inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));message = id + "sever recvfrom success";lg(INFO, message.c_str());// 处理数据std::string echo_info = process_info(buffer);// 响应给发送端sendto(sockfd_, echo_info.c_str(), echo_info.size(), 0, reinterpret_cast<const struct sockaddr *>(&src_addr), src_len);message = id + "sever sendto success";lg(INFO, message.c_str());}}

注意点 -- ip地址和端口号的来源

ip地址的选择

可以使用默认ip,也可以自己手动传入

udp_server(const uint16_t port = 8080,const std::string ip = "0.0.0.0"): ip_(ip), port_(port), sockfd_(0){}
#include "udp_server.hpp"int main()
{udp_server s;//udp_server s(8080,"127.0.0.1");s.run();return 0;
}

这里如果服务端直接连接云服务器的公有ip,会报错:

  • 因为云服务器禁止直接绑定公网ip
  • 一台主机上可能会配置多个网卡,你看到的ip只是其中一个
  • 如果就固定地绑定了那一个,你的服务器就只能收到那一个ip地址收到的报文,其他ip收到的就看不到了
  • 所以,如果我们将ip地址写成0(也就是那个"0.0.0.0"),就可以将发往这台主机(无论是哪张网卡,它会动态识别是否属于该主机)的所有信息都向上交付给对应端口
  • 或者用宏 INIDDR_ANY
  • 虚拟机是可以直接绑定ip的
本地环回地址

这里的127.0.0.1指的是本地环回地址

  • 如果服务器绑定这个ip,就不会将数据发送到网络,而是贯穿网络协议栈后,重新发回本主机
  • 也就是本地通信
端口号

所以,我们一般用大一点的数字分配给我们的服务端

运行情况

#include "udp_server.hpp"
#include <cstdlib>
using namespace std;int main()
{udp_server s;// udp_server s(8080,"127.0.0.1");s.run();return 0;
}

netstat -nlup

  • n表示,把所有可以显示为数字的显示成数字,否则有些会是字符串
  • p表示,显示pid信息

用于检查网络状态和网络连接:

每列表示的含义:

  • 协议   收发报文的个数   本地ip地址+端口号  可以接收什么客户端的消息

只要可以查到信息,就说明这个服务器已经启动成功了

客户端

思路

和服务端的流程差不多,依然是封装成类,调用启动接口就可以启动客户端

启动后,就等待用户进行输入

初始化

思路

客户端也需要创建自己的套接字,但是不需要手动绑定

  • os会为我们自由分配一个端口号(客户端的端口号没有什么要求,只需要保证最基本的唯一性即可),且将本机ip和端口号与创建出的套接字绑定
  • os什么时候为我们绑定?
  • 首次发送消息的时候,也就是需要网络通信的时候再绑定

因为需要将输入的数据发给服务端,所以就需要提前知道服务端的套接字地址信息

  • 日常生活中,我们一般直接通过网址获得网站提供的服务

  • 网址经过处理后,就是ip地址,而端口号是固定的,一个ip地址就会有对应的固定端口号

  • 所以我们知道网址=客户端知道了ip地址=服务端的套接字被我们获取到

  • 但这里我们写的很简陋,只能手动提供服务端的ip和端口号,然后构建出服务端的网络地址信息(结构体)

(使用的接口还是那些)

代码 
struct data
{int sockfd_;struct sockaddr_in *paddr_;socklen_t len_;
};data *init(){// 创建套接字文件int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){lg(FATAL, "socket create error, sockfd : %d", sockfd);exit(SOCKET_ERR);}// 创建sockaddr结构struct sockaddr_in *svr_paddr = new sockaddr_in;socklen_t svr_len = sizeof(*svr_paddr);bzero(svr_paddr, svr_len);svr_paddr->sin_addr.s_addr = inet_addr(ip_.c_str());svr_paddr->sin_family = AF_INET;svr_paddr->sin_port = htons(port_);return new data({sockfd, svr_paddr, svr_len});}

客户端的运行

思路 

就是等待用户输入,然后发送给服务端

然后等待服务端的响应数据

最后将服务端处理后的数据打印出来

代码
void run(){data *d = init();std::string info;char buffer[buff_size];while (true){std::cout << "Please enter:";std::getline(std::cin, info);// 将消息发送给服务器sendto(d->sockfd_, info.c_str(), info.size(), 0, reinterpret_cast<const struct sockaddr *>(d->paddr_), d->len_);info.clear();struct sockaddr_in addr; // 仅用于填充参数,拿到自己的地址信息没啥意义socklen_t len = sizeof(addr);// 获取数据ssize_t n = recvfrom(d->sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&addr), &len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::cout << buffer << std::endl;memset(buffer, 0, sizeof(buffer));}}

运行情况 

#include "udp_client.hpp"
#include <cstdlib>
using namespace std;int main()
{udp_client c;//udp_client c(8080,"127.0.0.1");c.run();return 0;
}

运行情况 

这样我们就完成了cs互相通信的过程

改成命令行输入

上面我们是在代码中定义了ip地址和端口号,也可以以命令行的形式,将服务器的ip地址和端口号输入

  • 也就是利用main函数的参数
  • 只需要改一下主函数代码即可

服务端

#include "udp_server.hpp"
#include <cstdlib>
using namespace std;//./udp_server port
int main(int argc, char *argv[])
{if (argc != 2){std::cout << "./udp_server port(port>=1024)" << std::endl;exit(1);}udp_server s(atoi(argv[1]));s.run();return 0;
}

客户端

#include "udp_client.hpp"
#include <cstdlib>
using namespace std;//./udp_client ip port
int main(int argc, char *argv[])
{while (argc != 3){std::cout << "./udp_client ip port(port>=1024)" << std::endl;exit(1);}udp_client s(atoi(argv[2]), argv[1]); // port,ips.run();return 0;
}

运行情况

如果输入的内容不足,就会提示:

然后cs端都运行起来,并发送消息:

远程让服务器执行命令

引入

除了简单的转发消息,我们也可以发送命令,远程让服务器执行命令,且将执行结果传给客户端,让我们可以看到

  • 这不就是我们的shell吗
  • 其实之前有模拟过简单的shell(创建子进程,使用exec系列函数,让子进程去执行命令)

或者直接用popen函数

它会在内部帮助我们创建一条管道,且创建子进程+执行命令,然后把执行后的结果写入到管道文件中,以文件指针的形式返回给我们

command

  • 要执行的shell命令

mode

  • 表示要打开的管道的模式
  • 看你想要如何与命令交互:
  • 如果是只想要查看命令的输出,就以"r"打开 (ls)
  • 如果想要将一些数据发送给它作为输入,就以"w"打开 (cat)

分层

所以我们考虑改变一下代码结构,将处理数据与服务器收到数据解耦

  • 也就是在服务端处理数据时,使用自定义的处理方法(以回调的方式使用)

代码

服务端类里面修改的不多

在处理数据那里,我们直接调用参数:

void run(func_t func){init();// 开始收发数据char buffer[buff_size];std::string message;while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in src_addr;socklen_t src_len = sizeof(src_addr);// 获取数据ssize_t n = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&src_addr), &src_len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::string id = generate_id(inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));message = id + "sever recvfrom success";lg(INFO, message.c_str());// 处理数据std::string echo_info = func(buffer);// 响应给发送端sendto(sockfd_, echo_info.c_str(), echo_info.size(), 0, reinterpret_cast<const struct sockaddr *>(&src_addr), src_len);message = id + "sever sendto success";lg(INFO, message.c_str());}}

因为我们需要将处理函数放在类外,所以多增加了一个接口,用于外部调用

 static std::string get_id() //外部使用{udp_server obj;return obj.generate_id(obj.ip_, obj.port_);}private:std::string generate_id(const std::string ip, const uint16_t port){return "[" + ip + ":" + std::to_string(port) + "]";}

主函数这里多修改了一些:

  • 分出两个处理函数
  • 把获取时间的功能也单拎了出来
#include "udp_server.hpp"
#include <cstdlib>
#include <cstdio>
using namespace std;std::string get_time()
{time_t t = time(nullptr);struct tm *ctime = localtime(&t);char time_stamp[SIZE];snprintf(time_stamp, sizeof(time_stamp), "[%d-%d-%d %d:%d:%d]:",ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);return time_stamp;
}std::string process_info(const std::string &info)
{std::string time_stamp = get_time();std::string id = udp_server::get_id();std::string res = id + time_stamp + info;return res;
}std::string process_command(const std::string &cmd)
{std::string time_stamp = get_time();std::string id = udp_server::get_id();std::string res;FILE *fp = popen(cmd.c_str(), "r");if (fp == nullptr){lg(ERROR, "popen error, errno: %d, err string: %s", errno, strerror(errno));return "";}char buffer[buff_size];memset(buffer, 0, sizeof(buffer));if (fgets(buffer, sizeof(buffer) - 1, fp) == nullptr){res = id + time_stamp + "command error";return res;}while (true){memset(buffer, 0, sizeof(buffer));if (fgets(buffer, sizeof(buffer) - 1, fp) != nullptr){res += buffer;}else{break;}}return res;
}
int main()
{udp_server s;// udp_server s(8080,"127.0.0.1");s.run(process_command);return 0;
}

运行情况 

如果输入的不是命令,会提示给用户:

如果是命令,就会返回给我们运行结果:

相关文章:

基于udp协议的cs网络通信代码(echo版+命令行输入版+执行指令版),netstat指令

目录 引入 基础版 服务端 思路 头文件log类 套接字的初始化 思路 代码 服务器开始运行 思路 代码 注意点 -- ip地址和端口号的来源 ip地址的选择 本地环回地址 端口号 ​编辑 运行情况 netstat -nlup 客户端 思路 初始化 思路 代码 客户端的运行 思…...

centos7网络命令:ping、dig、nsloopup、tcpdump

目录 一、ping1、命令参数&#xff1a;2、示例-将当前的信息打印到一个文件中3、示例-结束进程 二、dig1、安装2、语法格式选项说明 3、示例4、示例-将当前的信息打印到一个文件中 三、nslookup1、安装2、语法格式选项说明 3、示例 四、tcpdump抓包1、安装2、语法格式&#xff…...

Excel判断CD两列在EF两列的列表中是否存在

需求 需要将CD两列的ID和NAME组合起来&#xff0c;查询EF两列的ID和NAME组合起来的列表中是否存在&#xff1f; 比如&#xff0c;判断第二行的“123456ABC”在EF的第二行到第四行中是否存在&#xff0c;若存在则显示Y&#xff0c;不存在则显示N 实现的计算公式 IF(ISNUMBER…...

基于斑翠鸟优化算法(Pied Kingfisher Optimizer ,PKO)的无人机三维路径规划(MATLAB)

一、无人机路径规划模型介绍 二、算法介绍 斑翠鸟优化算法(Pied Kingfisher Optimizer ,PKO),是由Abdelazim Hussien于2024年提出的一种基于群体的新型元启发式算法,它从自然界中观察到的斑翠鸟独特的狩猎行为和共生关系中汲取灵感。PKO 算法围绕三个不同的阶段构建:栖息…...

同程旅行前端面试汇总

一、同程旅行一面 自我介绍技术提问 打开新的tab页、window.open是否共享sessionStorage存储的数据vue、react 源码有没有看过&#xff0c;说一下react17 与 react18区别webpack中loader与plugin&#xff0c;有没有自己写过vuex、reactx 刷新数据丢失 怎么做的持久化 反问 总…...

小美的平衡矩阵_dp思路

小美的平衡矩阵 写在前面: 本博客只是一种解题思路的提供。 小美的平衡矩阵 题目描述&#xff1a; 小美拿到了一个n*n 的矩阵&#xff0c;其中每个元素是 0 或者 1。 小美认为一个矩形区域是完美的&#xff0c;当且仅当该区域内 0 的数量恰好等于 1 的数量。 现在&#xf…...

json展示curl 请求接口返回结果

使用curl发送请求并将返回结果以JSON格式展示&#xff0c;通常需要确保请求的响应本身就是JSON格式。可以结合jq这个JSON处理工具来格式化输出。 首先要安装jq 工具。 Linux发行版中&#xff0c;你可以使用包管理器来安装它。 sudo yum install jq # 对于CentOS/RHEL 安装成…...

2024 年排名前 5 名的 Mac 数据恢复软件分享

如果您已经在 Mac 上丢失了数据并且正在寻找恢复数据的方法&#xff0c;那么您来对地方了。互联网上有超过 50 个适用于 Mac 的数据恢复程序。哪个是最好的 Mac 数据恢复软件&#xff1f;不用担心。本文列出了 5 款 Mac 数据恢复软件&#xff0c;可帮助您在 Mac OS 下恢复丢失的…...

请描述一下Spring MVC的工作流程。在Spring MVC中,DispatcherServlet的作用是什么?

请描述一下Spring MVC的工作流程。 Spring MVC 的工作流程是基于请求驱动的&#xff0c;它围绕 Servlet 设计&#xff0c;将请求映射到处理器&#xff0c;处理器处理请求并返回响应。以下是 Spring MVC 的基本工作流程&#xff1a; 发送请求&#xff1a; 客户端&#xff08;例…...

2023年终总结——跌跌撞撞不断修正

目录 一、回顾1.一月&#xff0c;鼓足信心的开始2.二月&#xff0c;焦躁不安3.三月&#xff0c;路还是要一步一步的走4.四月&#xff0c;平平淡淡的前行5.五月&#xff0c;轰轰烈烈的前行6.六月&#xff0c;看事情更底层透彻了7.七月&#xff0c;设计模式升华月8.八月&#xff…...

OPPO后端二面,凉了!

这篇文章的问题来源于一个读者之前分享的 OPPO 后端凉经&#xff0c;我对比较典型的一些问题进行了分类并给出了详细的参考答案。希望能对正在参加面试的朋友们能够有点帮助&#xff01; Java String 为什么是不可变的? public final class String implements java.io.Seri…...

Unity3d版白银城地图

将老外之前拼接的Unity3d版白银城地图&#xff0c;导入到国内某手游里&#xff0c;改成它的客户端地图模式&#xff0c;可以体验一把手游的快乐。 人物角色用的是它原版的手游默认的&#xff0c;城内显示效果很好&#xff0c;大家可以仔细看看。 由于前期在导入时遇到重大挫折&…...

【PCL】(二十八)点云超体素分割

&#xff08;二十九&#xff09;点云超体素分割 论文&#xff1a;Voxel Cloud Connectivity Segmentation - Supervoxels for Point Clouds supervoxel_clustering.cpp #include <pcl/console/parse.h> #include <pcl/point_cloud.h> #include <pcl/point_ty…...

Socket通信Demo(Unity客户端和C#)

Socket通信基本流程 首先要启动服务器创建Socket&#xff0c;然后要绑定服务器的一个端口这样客户端通过服务器IP端口号就能连接到服务器了服务器接下来会设置监听队列&#xff0c;监听并等待要连接到它的客户端客户端在服务器启动之后也建立自己的Socket&#xff0c;然后使用…...

Lucene 自定义词库

import org.apache.lucene.analysis.hunspell.Dictionary; import org.apache.lucene.analysis.hunspell.HunspellStemFilter; import...

【LeetCode热题100】73. 矩阵置零(矩阵)

一.题目要求 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 二.题目难度 中等 三.输入样例 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0…...

使用Barrier共享鼠标键盘,通过macos控制ubuntu系统

之前文章写过如何使用barrrier通过windows系统控制ubuntu系统&#xff0c;该文章将详细介绍如何使用barrier通过macos系统控制ubuntu系统 一、macOS安装barrier macOS版本barrier链接 1、双击点开安装包 2、将安装包里的barrier拷贝到macOS的达达->应用程序中 3、在达达…...

c++:类和对象中:拷贝构造和赋值运算符重载详解

c:类和对象 构造函数和析构函数详解 文章目录 c:类和对象构造函数和析构函数详解 前言一、拷贝构造怎么写拷贝构造1.拷贝构造也是构造函数的一种,构造函数没有值.所以拷贝构造也没有返回值**2.拷贝构造只有一个形参,正常这个形参是自定义类型对象的引用.3. 如果我们没有显示写…...

Day33:安全开发-JavaEE应用SQL预编译Filter过滤器Listener监听器访问控制

目录 JavaEE-预编译-SQL JavaEE-过滤器-Filter JavaEE-监听器-Listen 思维导图 Java知识点 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;序列化数据&#xff0c;身份验证&#xff0c;框架开发&#xff0c;第三方库使用等. 框架库&#xff1a;MyBatis&#…...

Log4j如何支持多线程环境?你如何优化Log4j的性能?

Log4j如何支持多线程环境&#xff1f; Log4j 通过其内部设计来支持多线程环境&#xff0c;确保在多线程应用程序中能够安全地使用。以下是 Log4j 支持多线程环境的一些关键方面&#xff1a; 线程安全性&#xff1a; Log4j 的 Logger 类和 Appender 类都是设计为线程安全的。这…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...