【Linux 网络编程 】

news2024/11/30 8:32:26

Linux 网络编程

  • 背景知识:
    • 主机字节序列和网络字节序列
    • IP地址的转换
  • API
    • 网络编程接口
    • 网络节序与主机节序转换函数
    • IP地址转换函数
    • 数据读写
  • TCP编程
    • 编程步骤:
    • 客户端链接服务端成功的条件
    • 多线程实现服务端并发
    • 多进程实现服务端并发
    • 注意:
  • UDP编程
    • 编程步骤
    • 实现
  • netstat
    • netstat -natp
    • netstat -naup

背景知识:

  • ipv4地址:32位 网络号+ 主机号
  • ipv6地址:128位
  • 端口号: short类型 应用程序的一个代号

当我们进行网络编程的时候,只需要关注于对方进程的ip地址和端口号(0-1024为知名端口,用户不能随便使用,1024-4096为保留端口,用户也不能随便使用,端口号必须4096以上才可以用)就可以了。

主机字节序列和网络字节序列

主机字节序列分为大端节序和小端节序,不同的主机采用的节序不同。

大端节序:高位字节存放在地址的低地址处。
小端节序:高位字节存放在地址的高地址处。
当两个字节节序不同的主机进行通信时,这样会产生冲突。
所以就有了网络字节序列。
网络字节序列规定使用整形数据时使用大端节序来传输。

IP地址的转换

人们习惯用点分十进制字符串表示 IPV4地址,但编程中我们需要先把它们转化。

API

网络编程接口

int socket(int domain, int type, int protocol);创建套接字
返回值:成功返回套接字的文件描述符,失败返回-1
domain:设置套接字的协议簇,AF_UNIX AF_INET AF_INET6
type:设置套接字的服务类型 SOCK_STREAM SOCK_DGRAM
protocol:一般设置为0,表示使用默认协议
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);将 sockfd 与一个socket地址绑定。
返回值:成功返回0,失败返回-1
sockfd:是网络套接字描述符
addr:地址结构
addrlen: socket 地址的长度
int listen(int sockfd, int backlog);创建一个监听队列以存储待处理的客户连接。
返回值;成功返回0,失败返回-1
sockfd:被监听的socket套接字
backlog:表示处于完全连接状态的 socket的上限
int accept(int sockfd, struct sockaddr*addr, socklen_t *addrlen);accept()从 listen 监听队列中接收一个连接,成功返回一个新的连接socket,该socket 唯一地标识了被接收的这个连接,失败返回-1
sockfd:是执行过 listen系统调用的监听socket
addr:用来获取被接受连接的远端socket地址
addrlen:该socket地址的长度
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);客户端需要通过此系统调用来主动与服务器建立连接,
返回值:成功返回0,失败返回-1
sockfd:由socket返回的一个socket。
serv_addr:服务器监听的socket地址
addrlen:这个地址的长度
int close(int sockfd);关闭一个连接,实际上就是关闭该连接对应的socket。

网络节序与主机节序转换函数

#include<netinet/in.h>
uint32_t htonl(uint32_t hostlong);长整型的主机字节序转网络字节序
uint32_t ntohl(uint32_t netlong);长整型的网络字节序转主机字节序
uint16_t htons(uint16_t hostshort);短整形的主机字节序转网络字节序
uint16_t ntohs(uint16_t netshort);短整型的网络字节序转主机字节序

IP地址转换函数

#include <arpa/inct.h>
in_addr_t inet_addr(const char *cp);字符串表示的IPV4地址转化为网络字节序
char* inet_ntoa(struct in_addr in);/IPV4地址的网络字节序转化为字符串

数据读写

TCP数据读写

ssize_t recv(int sockfd, void *buff, size_t len, int flags);读取sockfd上的数据,buff和 len参数分别指定读缓冲区的位置和大小
ssize_t scnd(int sockfd, const void *buff, size_t len, int flags);往socket上写入数据,buff和len参数分别指定写缓冲区的位置和数据长度flags参数为数据收发提供了额外的控制

UDP数据读写

ssize_t recvfrom(int sockfd, void  * buff, size_t len, int flags,struct sockaddr* src_addr, socklen_t * addrlcn);读取 sockfd上的数据,buff和len参数分别指定读缓冲区的位置和大小,src_addr记录发送端的socket地址addrlen指定该地址的长度
ssize_t sendto(int sockfd, void * buff, size_t len, int flags,struct sockaddr * dest_addr, socklen_t addrlcn);往socket上写入数据,buff和 len参数分别指定写缓冲区的位置和数据长度dest_addr指定接收数据端的 socket地址addrlen指定该地址的长度

TCP编程

编程步骤:

1.服务端

 1. socket()
 2. bind()
 3. listen()backlog的最大值不能超过128,它的值加1则是全连接队列的大小。非阻塞函数
 4. accept()是不是阻塞取决于socket是阻塞还是非阻塞模式,一般默认创建的是阻塞模式。
 5. recv() 是不是阻塞取决于socket是阻塞还是非阻塞模式,一般默认创建的是阻塞模式。
 6. send()是不是阻塞取决于socket是阻塞还是非阻塞模式,一般默认创建的是阻塞模式。
 7. close()

2.客户端

 - socket() 
 - connect() 
 - send() 
 - recv() 
 - close()

我们先来看一下三次握手的状态图。首先当客户端和服务端进行连接时,它是一种过程,中间有很多状态。
Linux内核会为维护两个队列

  • 为SYN_RCVD状态的socket维护一个队列(半连接队列)

    等待收到对方的ACK,接着把该socket放到全连接队列中
    
  • 为处于ESTABLISTENsocket维护一个队列(全连接队列)

    等待服务端进行accept的时候从这个队列中取出socket。
    

在这里插入图片描述
listen函数的backlog的值加1则是这个全连接队列的大小。
对于半连接队列的大小,我们可以通过下面命令来查看,它同样可以修改。
在这里插入图片描述

因为队列在内核中维护,这个也需要消耗大量资源,所以backlog的大小不能太大,不能超过128。
所以说listen中backlog的值并不是服务端能连接客户端的大小。

客户端链接服务端成功的条件

1.端口,ip地址,服务类型都正确
2.服务器正在运行
3.网络正常
4.服务器资源充足

多线程实现服务端并发

服务端
threadser.cpp

#include<iostream>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<bits/socket.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<pthread.h>
int sock_init()
{
  int socketfd = socket(AF_INET,SOCK_STREAM,0);
  if(socketfd == -1)
  {
   std::cout<<"socket erro"<<std::endl;
   exit(1);
  }
  struct sockaddr_in saddr,caddr;
  memset(&saddr,0,sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_port=htons(6000);
  saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
  int res = bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
  if(res == -1)
  {
   std::cout<<"bind erro "<<std::endl;
   exit(1);
  }
  res = listen(socketfd,5);
  if(res == -1)
  {
   std::cout<<"listen erro"<<std::endl;
    exit(1);
  }
 return socketfd;

}
void * fun(void *);
int main()
{
 int socketfd = sock_init();
 while(1)
 {

   struct sockaddr_in caddr;
   int len = sizeof(caddr);
   int c = accept(socketfd,(struct sockaddr *)&caddr,(socklen_t *)&len);
   if(c<=0)
   {
     std::cout<<"accept erro"<<std::endl;
     break;
   
   }
   std::cout<<"c=  "<<c<<"ip=  "<<inet_ntoa(caddr.sin_addr)<<"port=  "<<ntohs(caddr.sin_port)<<std::endl;;
   pthread_t id;
   pthread_create(&id,NULL,fun,(void *)(long)c);
 
   }
}
void * fun(void * arg)
{
 int c = (int)(long)arg;
  while(1)
  {
   char buff[128]={0};
   int n = recv(c,buff,1,0);
   if(n<=0)
   {
    break;
   
   }
   std::cout<<"recive ="<<buff<<"len="<<n<<std::endl;
   send(c,"ok",2,0);
  }
  std::cout<<c<<"close"<<std::endl;
  close(c);
  return NULL;
}

客户端
cli.cpp

#include<iostream>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<bits/socket.h>
#include<arpa/inet.h>
#include<sys/socket.h>
int main()
{
 int socketfd = socket(AF_INET,SOCK_STREAM,0);
 if(socketfd == -1)
 {
    std::cout<<"socket erro"<<std::endl;
     exit(1);
 
 }

 struct sockaddr_in saddr;
 memset(&saddr,0,sizeof(saddr));
 saddr.sin_family = AF_INET;
 saddr.sin_port =htons(6000);
 saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 int res = connect(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
 if(res == -1)
 {
   std::cout<<"connect erro"<<std::endl;
   exit(1);
 
 }

  while(1)
  {

   char buff[128]={0};
  std::cout<<"please input"<<std::endl;
   fgets(buff,128,stdin); 
   if(strncmp(buff,"end",3)==0)
   {
    break;
   }
   send(socketfd,buff,strlen(buff)-1,0);
   memset(buff,0,sizeof(buff));
   recv(socketfd,buff,127,0);
   std::cout<<"buff = "<<buff<<std::endl;
  }
   close(socketfd);
   exit(0);
  
}

运行结果
客户端1
在这里插入图片描述

客户端2
在这里插入图片描述

服务端
在这里插入图片描述

多进程实现服务端并发

服务端
courseser.cpp

#include<iostream>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<bits/socket.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<pthread.h>
#include<signal.h>
void fun(int sig)
{
  wait(NULL);

}
int sock_init()
{
  int socketfd = socket(AF_INET,SOCK_STREAM,0);
  if(socketfd == -1)
  {
    std::cout<<"socket erro"<<std::endl;
   exit(1);
  }
  struct sockaddr_in saddr,caddr;
  memset(&saddr,0,sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_port=htons(6000);
  saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
  int res = bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
  if(res == -1)
  {
    std::cout<<"bind erro "<<std::endl;
   exit(1);
  }
  res = listen(socketfd,5);
  if(res == -1)
  {
     std::cout<<"listen erro"<<std::endl;
    exit(1);
  }
 return socketfd;

}
int main()
{
 signal(SIGCHLD,fun);
 int socketfd = sock_init();
 while(1)
 {

   struct sockaddr_in caddr;
   int len = sizeof(caddr);
   int c = accept(socketfd,(struct sockaddr *)&caddr,(socklen_t*)&len);
   if(c<=0)
   {
      std::cout<<"accept erro"<<std::endl;
     break;
   
   }
   std::cout<<"c="<<c<<"  ip="<<inet_ntoa(caddr.sin_addr)<<"  port="<<ntohs(caddr.sin_port)<<std::endl;;
   pid_t pid = fork();
  if(pid ==0)
  {
  
    while(1)
   {
     char buff[128]={0};
     int n = recv(c,buff,127,0);
     if(n<=0)
    {
      break;
   
    }
     std::cout<<"recive ="<<buff<<"  len="<<n<<std::endl;
     send(c,"ok",2,0);
  
   } 
  std::cout<<c<<"close"<<std::endl;
  close(c);
  }
 
 }
 exit(0);
}

客户端
cli.cpp

#include<iostream>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<bits/socket.h>
#include<arpa/inet.h>
#include<sys/socket.h>
int main()
{
 int socketfd = socket(AF_INET,SOCK_STREAM,0);
 if(socketfd == -1)
 {
    std::cout<<"socket erro"<<std::endl;
     exit(1);
 
 }

 struct sockaddr_in saddr;
 memset(&saddr,0,sizeof(saddr));
 saddr.sin_family = AF_INET;
 saddr.sin_port =htons(6000);
 saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 int res = connect(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
 if(res == -1)
 {
   std::cout<<"connect erro"<<std::endl;
   exit(1);
 
 }

  while(1)
  {

   char buff[128]={0};
  std::cout<<"please input"<<std::endl;
   fgets(buff,128,stdin); 
   if(strncmp(buff,"end",3)==0)
   {
    break;
   }
   send(socketfd,buff,strlen(buff)-1,0);
   memset(buff,0,sizeof(buff));
   recv(socketfd,buff,127,0);
   std::cout<<"buff = "<<buff<<std::endl;
  }
   close(socketfd);
   exit(0);
  
}

运行结果
服务端
在这里插入图片描述
客户端1
在这里插入图片描述
客户端2
在这里插入图片描述

注意:

关闭的时候要先关闭所以客户端,然后再关闭服务端,要不然运行服务端的时候会出现端口占用的情况,无法启动。

UDP编程

与TCP编程相比,UDP编程一定要每次都把数据读完,因为它是无连接的,如果没有读完的话,则会把剩余的数据丢掉。
udp在局域网中很难丢数据

编程步骤

服务端

1.socket()
2.bind()
3.recvfrom()
4.sendto()
5.close()

客户端

1.socket()
 //bind()可有可无
2.sendto()
3.recvfrom()
4.close()

实现

ser.cpp

#include<iostream>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
 int sockfd = socket(AF_INET,SOCK_DGRAM,0);
 if(sockfd == -1)
 {
   std::cout<<"sockfd erro"<<std::endl;
  exit(1);
 
 }
 struct sockaddr_in saddr,caddr;
 memset(&saddr,0,sizeof(saddr));
 saddr.sin_family = AF_INET;
 saddr.sin_port = htons(6000);
 saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
 int res =bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
 if(res == -1)
 {
   std::cout<<"bind erro"<<std::endl;
  exit(1);
 }

 while(1)
 {
   char buff[128]={0};
   int len = sizeof(caddr);
   int num = recvfrom(sockfd,buff,127,0,(struct sockaddr *)&caddr,(socklen_t*)&len);
   std::cout<<"datalength="<<num<<"  ip="<<inet_ntoa(caddr.sin_addr)<<"  buff="<<buff<<std::endl;
   sendto(sockfd,"ok",2,0,(struct sockaddr *)&caddr,sizeof(caddr));
 
 }
 close(sockfd);
 exit(0);
}

cli.cpp

#include<iostream>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
 int sockfd = socket(AF_INET,SOCK_DGRAM,0);
 if(sockfd == -1)
 {
  std::cout<<"sockfd erro"<<std::endl;;
  exit(1);
 
 }
 struct sockaddr_in saddr;
 memset(&saddr,0,sizeof(saddr));
 saddr.sin_family = AF_INET;
 saddr.sin_port = htons(6000);
 saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

 while(1)
 {
   std::cout<<"input"<<std::endl;
   char buff[128]={0};
   fgets(buff,128,stdin);
   if(strncmp(buff,"end",3) == 0)
   {
    break;
   }
   sendto(sockfd,buff,strlen(buff)-1,0,(struct sockaddr *)&saddr,sizeof(saddr));
   memset(buff,0,sizeof(buff));
  int len = sizeof(saddr);
  recvfrom(sockfd,buff,127,0,(struct sockaddr *)&saddr,(socklen_t*)&len);
  std::cout<<"recive ="<<buff<<std::endl;
 }
 close(sockfd);
 exit(0);
}

运行结果
服务端
在这里插入图片描述
客户端1
在这里插入图片描述
客户端2
在这里插入图片描述

对于UDP编程来说,它是无连接的,发送数据的时候只需要知道对方的IP和端口就可以了

TCP 可靠,开销大
UDP 开销小,不可靠
UDP和TCP可以同时复用一个端口

一个进程中可以创建多个套接字,只要使用的端口不同就可以了

netstat

netstat -natp

查看tcp连接的各种信息
在这里插入图片描述

netstat -naup

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/26245.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动化测试基础简介(本质)

目录 前言 1.自动化基础 2.分层的自动化测试 2.1 单元自动化测试 2.2 接口自动化测试 2.3 UI自动化测试 3.适合自动化的项目 4.自动化测试模型 4.1线性测试 4.2模块化与类库 4.3数据驱动测试 4.4关键字驱动测试 5.POM设计模式 总结 前言 随着软件系统规模的日益…

应对Redis缓存污染问题,你应该知道这些内容

前言 我们在使用Redis做为缓存时&#xff0c;能加速我们对于热点数据的查询。但是如果缓存中有大量的数据不再热门了&#xff0c;从而占据着大量的内存空间&#xff0c;那么我们的Redis性能就会收到很大影响。该如何解决这个问题呢&#xff1f;本文给你答案。 什么是缓存污染…

kafka开发环境搭建

1 kafka开发环境 1.1 安装Java环境 1.1.1 下载linux下的安装包 登陆网址https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载完成后&#xff0c;Linux默认下载位置在当前目录下的Download或下载文件夹下&#xff0c;通过命令cd ~/…

轻松学习string类常用接口(附模拟实现)

目录 String的常用接口说明(最常用的) string类对象的容量操作 string类对象的访问及遍历操作 string类对象的修改操作 string类非成员函数 深浅拷贝 简介&#xff1a;Cstring 是C中的字符串。 字符串对象是一种特殊类型的容器&#xff0c;专门设计来操作的字符序列。 不像…

MySQL 全文检索的实现

微信搜「古时的风筝」&#xff0c;还有更多技术干货 这有朋友聊到他们的系统中要接入全文检索&#xff0c;这让我想起了很久以前为一个很古老的项目添加搜索功能的事儿。 一提到全文检索&#xff0c;我们首先就会想到搜索引擎。也就是用一个词、一段文本搜索出匹配的内容。一般…

Vue3中的组合Api与响应函数

文章目录1. 组合Api介绍setup2. 响应函数2.1 ref2.2 reactive2.3 toRef和toRefs2.4 readonly2.5 customRef1. 组合Api介绍 组合Api其实时用于解决功能、数据和业务逻辑分散的问题&#xff0c;使项目更益于模块化开发以及后期维护。 vue2.x — optionsApi 配置式Api — react类…

STM32 定时器定时计算

STM32 定时器定时计算STM32 定时器频率例子公式referenceSTM32 定时器频率 定时时间 定时器频率 / 倍频 /装载周期 htim1.Init.Prescaler 72-1;htim1.Init.CounterMode TIM_COUNTERMODE_UP;htim1.Init.Period 1*1000*1000;tim 72x10^6 / (72-1)/ 110001000 1us 根据定时…

RAC/RAC One Node 修改私网/心跳网卡名

cDAS RAC/RAC One Node一般采用ib ip作为私网/心跳ip&#xff0c;有些现场可能会有修改心跳/私网网卡的需求&#xff0c;一般修改网卡也会伴随修改心跳/私网ip的需求。若心跳/私网有高可用&#xff0c;可以一个个修改。 场景1&#xff1a;心跳网卡有高可用 原心跳网卡名 原心…

Vue中 引入使用 localforage 改进本地离线存储(突破5M限制)

1. 简介 说到本地存储数据&#xff0c;首先想到的是 localStorage&#xff0c;应该很多小伙伴都用过&#xff0c;使用很简单。然而&#xff0c;localStorage 却有下面一些缺点&#xff1a; 存储容量限制&#xff0c;大部分浏览器应该最多5M。我就遇到过localStorage存储字符然…

H264码流中SPS PPS详解

1 SPS和PPS从何处而来&#xff1f; 2 SPS和PPS中的每个参数起什么作用&#xff1f; 3 如何解析SDP中包含的H.264的SPS和PPS串&#xff1f; 1 客户端抓包 在做客户端视频解码时&#xff0c;一般都会使用Wireshark抓包工具对接收的H264码流进行分析&#xff0c;如下所示&…

Transform+ASM插桩系列(3)——Transform+ASM的实战

回顾 在上一章讲到创建完buildSrc之后&#xff0c;实现了项目的plugin之后&#xff0c;就可以在plugin注册我们的Transform。这期文章将正式进入重头戏&#xff0c;今天的学习内容有【认识Transform】、【认识AMS】、【插桩实战】 前言 插桩的技巧中&#xff0c;我们要知道 …

Python实现LDA和KNN人脸识别模型(LinearDiscriminantAnalysis和KNeighborsClassifier算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 人脸识别&#xff0c;是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含…

【2021-TITS】Deep Learning in Lane Marking Detection: A Survey

概述 回顾了针对路面标线的深度学习算法&#xff0c;主要分析了他们的网络架构及优化目标&#xff1b;此外还总结了现有车道标线相关的数据集&#xff0c;评价基准及常见的数据处理技术 PaperCode 总结 创新点&#xff1a; 1.总结深度学习网络架构、优化目标2.总结了相关现有…

使用Portman、Postman和Newman自动化API测试

几个月前我换了工作和公司&#xff0c;很喜欢这样的变化&#xff0c;但多少会有些不知所措。现在事情已经安排好了一些&#xff0c;我可以回来做一些我喜欢的事情。 现在正在工作的团队是一个新团队&#xff0c;我们正在努力弄清楚我们的流程&#xff0c;并试图尽可能地实现自…

编译原理实验一:源程序的预处理及词法分析程序的设计与实现(python)

实验目的 设计并实现一个包含预处理功能的词法分析程序&#xff0c;加深对编译中词法分析过程的理解。 实验要求 1、实现预处理功能 源程序中可能包含有对程序执行无意义的符号&#xff0c;要求将其剔除。 首先编制一个源程序的输入过程&#xff0c;从键盘、文件或文本框输…

[附源码]java毕业设计校园拓展活动管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

论文阅读之Dynamic Routing Between Capsules(2017)

文章目录前言capsule network参考前言 capsule network 可以简单理解为将神经网络的标量计算&#xff0c;赋予了方向&#xff0c;变换成了向量进行计算。 标量只有大小之分。 向量不仅有大小&#xff0c;还有方向之分。 应该可以略微感受到这种思路的魔力了。 capsule 主要是…

IB历史备考,如何拿高分?

邻近期末考&#xff0c;大伙儿是不是十分焦虑不安呢&#xff1f;大家的老师经常发觉学生们本来知识要点都把握得挺不错的&#xff0c;但每到考試便是拿不上满意的考试成绩&#xff0c;让人十分郁闷&#xff01;因此应对那样的难点&#xff0c;大家的IB历史时间评委为大伙儿筹备…

行业分析| 智慧消防对讲

消防工程关系国计民生&#xff0c;关系到每个公民的人身和财产安全&#xff0c;在每个场所的建设中&#xff0c;消防工程建设是必不可少的一个板块&#xff0c;包括消防应急通道建设和应急设备建设两个方面。其中消防系统设备建设主要分为九大系统的建设&#xff1a;消防水系统…

VectorCAST测试工具环境搭建

**目的&#xff1a;**本文提供VectorCAST使用的实际操作方法&#xff0c;以便项目可以很好的使用VectorCAST工具。 适用范围&#xff1a;VectorCAST是一个专门针对嵌入式软件的C/C代码进行单元测试的工具&#xff0c;它可以自动生成测试用例、评估测试结果并生成测试报告。 **工…