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

Linux--TCP编程--0216 17

观前提示:本篇博文的一些接口需要前几篇博文实现的

  • 线程池的实现

Liunx--线程池的实现--0208 09_Gosolo!的博客-CSDN博客

  • 线程池的单例模式

Linux--线程安全的单例模式--自旋锁--0211_Gosolo!的博客-CSDN博客


 1.TCP编程需要用的接口

创建 socket 文件描述符

int socket(int domain, int type, int protocol);

type 给成  SOCK_STREAM 表示是流式套接

listensock=socket(AF_INET,SOCK_STREAM,0);

开始监听socket

int listen(int socket, int backlog);

TCP是面向连接的,listen其实也是一个套接字,不过他的用途在于建立连接,而不真正提供服务。类似拉人的,提供服务的是服务员。

接收请求

int accept(int socket, struct sockaddr* address,socklen_t* address_len);

相当于拉客的和服务员进行了交接,返回值是真正提供服务的套接字(fd)

 建立连接

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

在正式通信之前,需要先建立连接。客户端需要连接到连接。

 2.TCP编程的框架

 2.1 服务端 TcpServer的框架  tcp_server.hpp

class TcpServer
{
private:const static int gbacklog = 20;  //后面再说public:TcpServer(uint16_t port, std::string ip="0.0.0.0"):_port(port),_ip(ip),listensock(-1){}void initServer(){}void Start(){}~TcpServer(){}
private:uint16_t _port;std::string _ip;int listensock;//listensock套接字仅用于建立连接
};

 头文件在这里一次性给出

#pragma once#include <iostream>
#include <string>
#include <unordered_map>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <signal.h>
#include <unistd.h>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <pthread.h>
#include <ctype.h>//这几个头文件在线程池里面  其中Task.hpp会在用时给出
#include "ThreadPool/log.hpp"
#include "ThreadPool/ThreadPool.hpp"
#include "ThreadPool/Task.hpp"

 2.1.2 TcpServer的调用 tcp_server.cc

#include "tcp_server.hpp"
#include <memory>static void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}// ./tcp_server port
int main(int argc, char *argv[])
{if(argc != 2){usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);std::unique_ptr<TcpServer> svr(new TcpServer(port));svr->initServer();svr->Start();return 0;
}

2.2 客户端 TcpClient的实现 tcp_client.cc

网络间通信首先需要 struct  sockaddr_in 结构体,这里定义一个对象 server

结构对象有了需要对他进行初始化(建议先全部置零,避免出现一些bug)

  • server 有三个地方需要做初始化,sin_familysin_portsin_addr.s_addr

server.sin_family=AF_INET; //跟申请套接字传入的参数一样即可

server.sin_port=htos(serverport); //记得从本地转为网络字节序

server.sin_addr.s_addr=inet_addr(serverip,c_str());

  • connect()接口  客户端需要连接到服务上

 connect(sock, (struct sockaddr *)&server, sizeof(server) );

  •  send() 向服务端发送消息

 ssize_t s = send(sock, line.c_str(), line.size(), 0);

  •  recv() 接收服务器发来的消息

 recv(sock, buffer, sizeof(buffer) - 1, 0);

  •  close() 关闭套接字

 close(sock);

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " serverIp serverPort\n"<< std::endl;
}// ./tcp_client targetIp targetPort
int main(int argc, char *argv[])
{if (argc != 3){usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = atoi(argv[2]);int sock = 0;while (true) // TODO{sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){std::cerr << "socket error" << std::endl;exit(2);}// client 要不要bind呢?不需要显示的bind,但是一定是需要port// 需要让os自动进行port选择// 连接别人的能力!struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0){std::cerr << "connect error" << std::endl;exit(3); // TODO}std::cout << "connect success" << std::endl;std::cout << "请输入# ";std::string line;std::getline(std::cin, line);if (line == "quit"){close(sock);break;}ssize_t s = send(sock, line.c_str(), line.size(), 0);if (s > 0){char buffer[1024];ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s] = 0;std::cout << "server 回显# " << buffer << std::endl;close(sock);}else if (s == 0){close(sock);}}else{close(sock);}}return 0;
}

3. 服务端的实现

3.1 TcpServer::initServer() 

  • 初始化,首先需要建立一个监听套接字listensock,这个监听套接字的作用是建立连接。
listensock=socket(AF_INET,SOCK_STREAM,0);

我们是网络之间进行通信,所以需要借助 struct sockaddr_in 类型的对象,local,创建出来之后最好先清空一下,保证不会出现一些奇怪的问题。

  • local 有三个地方需要做初始化,sin_familysin_portsin_addr.s_addr

local.sin_family=AF_INET; //跟申请套接字传入的参数一样即可

local.sin_port=htos(_port); //记得从本地转为网络字节序

local.sin_addr.s_addr=_ip=="0.0.0.0"?INADDR_ANY:inet_addr(_ip.c_str());

  •  接着和udp一样,进行绑定操作

//注意 虽然使用struct sockaddr_in 结构体类型 但是接口中的参数依然是 struct sockaddr*

bind(listensock, (struct sockaddr *)&local, sizeof(local);

  •  (tcp新增)建立连接

 listen(listensock, gbacklog);//这个gbacklog以后再谈

3.1.1 完整代码

class TcpServer
{
private:const static int gbacklog = 20;  //后面再说public:TcpServer(uint16_t port, std::string ip="0.0.0.0"):_port(port),_ip(ip),listensock(-1){}void initServer(){listensock=socket(AF_INET,SOCK_STREAM,0);if(listensock<0){logMessage(FATAL, "create socket error, %d:%s", errno, strerror(errno));exit(2);}logMessage(NORMAL, "create socket success, listensock: %d", listensock);struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_port);//local.sin_addr.s_addr=_ip.empty()?INADDR_ANY:inet_addr(_ip.c_str());local.sin_addr.s_addr=_ip=="0.0.0.0"?INADDR_ANY:inet_addr(_ip.c_str());//inet_pton(AF_INET, _ip.c_str(), &local.sin_addr);if (bind(listensock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));exit(3);}if(listen(listensock, gbacklog)<0){logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));exit(4);}logMessage(NORMAL, "init server success");}
}

3.2 TcpServer::Start()

 要想服务端程序开始运行,首先需要有人来提供服务,记得listensock的作用仅是建立连接吗,那谁来做具体的服务呢?

使用accept接口的返回值!

首先,网络通信需要struct sockaddr_in结构体,先创建一个,struct sockaddr_in src;,len就是这个结构体的长度。

int servicesock=accept(listensock,(struct sockaddr*)&src,&len);

连接成功时候,就可以从我们创建的src中获取端口号、ip信息了。

 uint16_t client_port=ntohs(src.sin_port);

 std::string client_ip=inet_ntoa(src.sin_addr);

 接下来就可以开始进行服务了,自定义让做什么。这里让服务端去做这个任务

service(servicesock, client_ip, client_port);

static void service(int sock, const std::string &clientip, const uint16_t &clientport)
{//echo serverchar buffer[1024];while(true){// read && write 可以直接被使用!ssize_t s = read(sock, buffer, sizeof(buffer)-1);if(s > 0){buffer[s] = 0; //将发过来的数据当做字符串std::cout << clientip << ":" << clientport << "# " << buffer << std::endl;}else if(s == 0) //对端关闭连接{logMessage(NORMAL, "%s:%d shutdown, me too!", clientip.c_str(), clientport);break;}else{ // logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));break;}write(sock, buffer, strlen(buffer));}close(sock);
}

 3.2.1  完整代码——单进程阻塞循环版

     void Start(){while(true){struct sockaddr_in src;socklen_t len=sizeof(src);//建立连接int servicesock=accept(listensock,(struct sockaddr*)&src,&len);if(servicesock < 0){logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));continue;}uint16_t client_port=ntohs(src.sin_port);std::string client_ip=inet_ntoa(src.sin_addr);logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n",servicesock, client_ip.c_str(), client_port);//版本1————单进程阻塞循环版service(servicesock, client_ip, client_port);}

问题在于,一次只能够处理一个进程。因为我们调用的函数service是一个死循环函数,如果一个客户端没有终止访问,其他客户端都不能正常来使用。

 3.2.2 完整代码——多进程带信号屏蔽版

那我使用多进程来解决这个问题,可是使用多进程也有问题,我创建了子进程,那我是不是要等待子进程结束啊?如果我使用阻塞等待那和上面有什么本质区别呢?

注:使用非阻塞等待成本很大,可以但不建议。

所以我们还需要用到信号,当子进程结束后,会给父进程发SIGCHLD信号!

 如果我们主动忽略SIGCHLD信号,子进程退出的时候,会自动释放自己的僵尸状态。

      void Start(){        signal(SIGCHLD, SIG_IGN); while(true){struct sockaddr_in src;socklen_t len=sizeof(src);//建立连接int servicesock=accept(listensock,(struct sockaddr*)&src,&len);if(servicesock < 0){logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));continue;}uint16_t client_port=ntohs(src.sin_port);std::string client_ip=inet_ntoa(src.sin_addr);logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n",servicesock, client_ip.c_str(), client_port);pid_t id = fork();assert(id != -1);if(id == 0){close(listensock);//关闭不需要的文件描述符service(servicesock, client_ip, client_port);exit(0); }//关闭父进程不需要的文件描述符 不关闭会导致父进程可用的文件描述符越来越少close(servicesock);}}

 3.3.3 完整代码——多进程版

能不能不屏蔽SIGCHLD信号呢?那我们就需要有人等待子进程,让谁等呢?

让bash领养,让bash等!

    void Start(){while(true){//...跟上面一样//版本3————多进程孤儿进程版// 利用孤儿进程被系统回收pid_t id=fork();if(id==0){close(listensock);if(fork()>0) {//子进程本身 exit(0);}//子进程的子进程service(servicesock, client_ip, client_port);exit(0);}waitpid(id,nullptr,0);//由于子进程创建子进程后立即退出 所以父进程不会阻塞close(servicesock);}}

 3.3.4 完整代码——多线程版

相较于使用多进程,多线程的开销明显小。

class ThreadData
{
public:int _sock;std::string _ip;uint16_t _port;
};class TcpServer
{
private:const static int gbacklog = 20;  //后面再说//设置的回调函数 必须是static的 不然会多一个this指针static void *threadRoutine(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);service(td->_sock, td->_ip, td->_port);delete td;return nullptr;}void Start(){while(true){//...//版本4————多线程版本//因为创建一个进程的代价还是比较大的,创建一个线程相对简便//不使用在栈上创建是为了保证线程安全 不会被覆盖 发生拷贝ThreadData *td=new ThreadData(); td->_sock=servicesock;td->_ip=client_ip;td->_port=client_port;pthread_t tid ;//如果不join 一定会造成内存泄漏 可以在threadRoutine中设置等待pthread_create(&tid,nullptr,threadRoutine,td); }}};

 3.3.5 完整代码——线程池版本

 线程的创建也是一笔开销,能省就省

线程池版本的服务函数

//线程池版本的服务函数
static void service(int sock, const std::string &clientip,const uint16_t &clientport, const std::string &thread_name)
{// echo server//  同时在线10人//  所以,我们一般服务器进程业务处理,如果是从连上,到断开,要一直保持这个链接, 长连接char buffer[1024];while (true){// read && write 可以直接被使用!ssize_t s = read(sock, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0; // 将发过来的数据当做字符串std::cout << thread_name << "|" << clientip << ":" << clientport << "# " << buffer << std::endl;}else if (s == 0) // 对端关闭连接{logMessage(NORMAL, "%s:%d shutdown, me too!", clientip.c_str(), clientport);break;}else{ //logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));break;}write(sock, buffer, strlen(buffer));}close(sock);
}
class TcpServer
{
public:TcpServer(uint16_t port, std::string ip="0.0.0.0"):_port(port),_ip(ip),listensock(-1), _threadpool_ptr(ThreadPool<Task>::getThreadPool()){}void Start(){//引入线程池_threadpool_ptr->run();//signal(SIGCHLD, SIG_IGN); // 对SIGCHLD,主动忽略SIGCHLD信号,子进程退出的时候,会自动释放自己的僵尸状态while(true){//...略//线程池版本Task t(servicesock, client_ip, client_port, service);//Task t(servicesock, client_ip, client_port, dictOnline);_threadpool_ptr->pushTask(t);}}~TcpServer(){}
private:uint16_t _port;std::string _ip;int listensock;//仅用于建立连接//定义一个线程池先std::unique_ptr<ThreadPool<Task>> _threadpool_ptr;
};

pushTask

#pragma once#include <iostream>
#include <string>
#include <functional>
#include "log.hpp"//typedef std::function<void (int , const std::string &, const uint16_t &)> func_t;
using func_t = std::function<void (int , const std::string &, const uint16_t &, const std::string &)>;class Task
{
public:Task(){}Task(int sock, const std::string ip, uint16_t port, func_t func): _sock(sock), _ip(ip), _port(port), _func(func){}void operator ()(const std::string &name){_func(_sock, _ip, _port, name);}
public:int _sock;std::string _ip;uint16_t _port;// int type;func_t _func;
};

相关文章:

Linux--TCP编程--0216 17

观前提示&#xff1a;本篇博文的一些接口需要前几篇博文实现的 线程池的实现Liunx--线程池的实现--0208 09_Gosolo&#xff01;的博客-CSDN博客 线程池的单例模式Linux--线程安全的单例模式--自旋锁--0211_Gosolo&#xff01;的博客-CSDN博客 1.TCP编程需要用的接口 创建 sock…...

关于设计模式的记录

############### 先弄清楚类模型的关系 ############### 万物的抽象关系 ############### 1.组合 composition实菱形 实线 无填充箭头整体与部分的关系同生共死代码体现&#xff1a;成员变量如&#xff1a;生命体与器官&#xff0c;http请求&#xff08;请求行&#xff0c;请求…...

Lambda-常见的函数式接口

如果需要使用Lambda接口&#xff0c;就必须要有一个函数式接口 函数式接口是有且仅有一个抽象方法的接口, 对应的注解是FunctionalInterface Java中内置的常见函数式接口如下: 1.Runnable/ Callable /*** The <code>Runnable</code> interface should be implem…...

P1196 [NOI2002] 银河英雄传说 带权并查集

[NOI2002] 银河英雄传说 题目背景 公元 580158015801 年&#xff0c;地球居民迁至金牛座 α\alphaα 第二行星&#xff0c;在那里发表银河联邦创立宣言&#xff0c;同年改元为宇宙历元年&#xff0c;并开始向银河系深处拓展。 宇宙历 799799799 年&#xff0c;银河系的两大军…...

【项目实战】快来入门Groovy的基础语法吧

一、Groovy是什么? 1.1 与Java语言的关系 下一代的Java 语言,增强Java平台的唯一的脚本语言跟java一样,它也运行在 JVM 中。支持Java平台,无缝的集成了Java 的类和库;Groovy是一种运行在JVM上的动态语言,跑在JVM中的另一种语言编译后的.groovy也是以class的形式出现的。1…...

Mybatis中的动态SQL

Mybatis中的动态SQL 当存在多条件查询的SQL时&#xff0c;当用户某个条件的属性没有写时&#xff0c;就会存在问题&#xff0c;在test中则不能很好的运行 所以Mybatis提出了动态SQL。 即判断用户是否输入了某个属性 动态SQL中的一些问题 方法一 这个里的and是为了确保if条…...

VUE常用API

1.$set数据变了&#xff0c;视图没变 this.$set(targe&#xff0c;key&#xff0c;value)2.$nextTick:返回参数[函数]。是一个异步的&#xff0c;功能获得更新后DOM$nextTick(callback){return Promise.resolve().then(()>{callback();}) }3.$refs获取dom4.$el获取当前组件根…...

25 openEuler管理网络-使用nmcli命令配置ip

文章目录25 openEuler管理网络-使用nmcli命令配置ip25.1 nmcli介绍25.2 设备管理25.2.1 连接到设备25.2.2 断开设备连接25.3 设置网络连接25.3.1 配置动态IP连接25.3.1.1 配置IP25.3.1.2 激活连接并检查状态25.3.2 配置静态IP连接25.3.2.1 配置IP25.3.2.2 激活连接并检查状态25…...

如何安装和使用A-ops工具?

一、pip配置 1.配置信任域 ​ pip3 config set global.trusted-host mirrors.tools.huawei.com2.配置pip源的url地址pip3 config set global.index-url http://mirrors.tools.huawei.com/pypi/simple 二、npm安装及配置 npm -v检测系统有无安装npm,如果没有的话需要配置ope…...

MySql数据库环境部署

MySql基础与Sql数据库概述基础环境的建立MYSQL数据库的连接方法MySql的默认数据库数据库端口号数据库概述 数据库&#xff08;DataBase&#xff0c;DB)∶存储在磁带、磁盘、光盘或其他外存介质上、按定结构组织在一起的相关数据的集合。数据库管理系统〈DataBase Management S…...

极品笔记,阿里P7爆款《K8s+Jenkins》技术笔记,职场必备

前些日子从阿里的朋友那里取得这两份K8sJenkins的爆款技术笔记&#xff1a;《K8S(kubernetes)学习指南》《Jenkins持续集成从入门到精通》&#xff0c;非常高质量的干货&#xff0c;我立马收藏&#xff01; 而今天咱们文章的主角就是这非常之干货的技术笔记&#xff1a;K8SJenk…...

数据结构:各种排序方法的综合比较

排序方法的选用应视具体场合而定。一般情况下考虑的原则有:(1)待排序的记录个数 n;(2)记录本身的大小;(3)关键字的分布情况:(4)对排序稳定性的要求等。 1.时间性能 (1) 按平均的时间性能来分,有三类排序方法: 时间复杂度为 O(nlogn)的方法有:快速排序、堆排序和归并排序,其中…...

【设计模式】 策略模式介绍及C代码实现

【设计模式】 策略模式介绍及C代码实现 背景 在软件构建过程中&#xff0c;某些对象使用的算法可能多种多样&#xff0c;经常改变&#xff0c;如果将这些算法都编码到对象中&#xff0c;将会使对象变得异常复杂&#xff0c;而且有时候支持不使用的算法也是一个性能负担。 如何…...

【数据库】第二章 关系数据库

第二章 关系数据库 2.1关系数据结构及形式化定义 关系 域&#xff08;domain) :域是一组具有相同数据类型的值的集合&#xff0c;可以取值的个数叫基数 笛卡尔积 &#xff1a;一个记录叫做一个元组&#xff08;tuple),元组中每一个属性值&#xff0c;叫一个分量 基数&…...

oracle和mysql的分页

oracle的分页&#xff1a;rownum 注意:&#xff1a; 对 ROWNUM 只能使用 < 或 <, 用 、 >、 > 都不能返回任何数据。 rownum是对结果集的编序排列&#xff0c;始终是从1开始&#xff0c;所以rownum直接使用时不允许使用>、> 所以当查询中间部分的信息时&…...

深拷贝与浅拷贝的理解

浅拷贝的理解浅拷贝的话只会拷贝基本数据类型&#xff0c;例如像string、Number等这些&#xff0c;类似&#xff1a;Object、Array 这类的话拷贝的就是对象的一个指针(通俗来讲就是拷贝一个引用地址&#xff0c;指向的是一个内存同一份数据)&#xff0c;也就是说当拷贝的对象数…...

Shell变量

一、变量分类 根据作用域分三种 &#xff08;一&#xff09;只在函数内有效&#xff0c;叫局部变量 &#xff08;二&#xff09;只在当前shell进程中有效&#xff0c;叫做全局变量 &#xff08;三&#xff09;在当前shell进程与子进程中都有效&#xff0c;叫做环境变量 shell进…...

Android 8请求权限时弹窗BUG

弹窗BUG 应用使用requestPermissions申请权限时&#xff0c;系统会弹出一个选择窗口&#xff0c;可进行允许或拒绝&#xff0c; 此窗口中有一个”不再询问“的选择框&#xff0c; ”拒绝”及“允许”的按钮。 遇到一个Bug,单点击“不再询问”&#xff0c;“允许”这个按钮会变…...

路漫漫:网络空间的监管趋势

网络空间是“以相互依存的网络基础设施为基本架构&#xff0c;以代码、信息与数据的流动为环境&#xff0c;人类利用信息通讯技术与应用开展活动&#xff0c;并与其他空间高度融合与互动的空间”。随着信息化技术的发展&#xff0c;网络空间日益演绎成为与现实人类生存空间并存…...

洛谷 P1208 [USACO1.3]混合牛奶 Mixing Milk

最后水一篇水题题解&#xff08;实在太水了&#xff09; # [USACO1.3]混合牛奶 Mixing Milk ## 题目描述 由于乳制品产业利润很低&#xff0c;所以降低原材料&#xff08;牛奶&#xff09;价格就变得十分重要。帮助 Marry 乳业找到最优的牛奶采购方案。 Marry 乳业从一些奶农手…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#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…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...