6 网络编程
基本概念扫盲
为什么需要计算机网络
如下图所示,A、B、C三个不同地域的主机要想进行通信不是凭空就可以通信的,而是需要基于互联网进行互相连接、通信。
为什么需要协议
如下图所示,红和蓝是联合攻打绿,它们以烽火为信号出动攻打绿,那么这时候就需要一个约定,比如红先点烽火,然后蓝看见了狼烟再点烽火,红看见了蓝的狼烟之后熄灭烽火,以此表示自己看见了,而蓝看见了红熄灭烽火之后也熄灭自己的烽火以此表示自己知道红看见了此信号,而后两人就需要再约定信号一起整顿出军以确保没有失误。
所以我们知道蓝和红之间的通信不能保证100%成功,但是要尽量保证没有失误的话就需要一遍又一遍的去确认,而这些一次又一次的确定就是双方定下的协议;由此我们可以清楚的认识到我们在网络通信中是必须要有协议的存在的。
为什么需要这么多协议
上文中我们举了两军协同作战,他们之间有个作战协议,而一旦作战成功,夺下对方城池那就需要另外一个瓜分战果的协议,所以每个不同的场景都会有对应的协议,这是有这么多协议的原因。
如下图所示,我们的计算机网络也有很多协议,下面是分为五层,如果你了解过计算机网络协议应该会知道七层模型、五层模型,但本章节不讲七层模型而是选择五层模型,因为七层模型是一种理想化的模型,实际应用我们用到的是五层模型。
如何定位互联网上的终端
首先我们熟知的系统是通过线程ID、进程ID知道对应的线程和进程的,在每个国家公民都是有身份证号码的,这也用来定位你这个人;在互联网上同样也有这样一个标识去确认终端,这就是IP地址。
IP地址以"."符号分割,一共有四组,例如:120.120.120.120,每一组都是的区间都是0到255,IP地址的组成是网络号加上主机号,而具体的界定我们可以查看下文。
IP地址分为5类,其分别如下所示:
类型 | 起始地址 | 结束地址 |
A类 | 0.0.0.0 | 127.255.255.255 |
B类 | 128.0.0.0 | 191.255.255.255 |
C类 | 192.0.0.0 | 223.255.255.255 |
D类 | 224.0.0.0 | 239.255.255.255 |
E类 | 240.0.0.0 | 247.255.255.255 |
我们不需要死记硬背,需要的时候自己查下就可以,具体含义网上很多,这里不过多赘述。
如何区分出网络号、主机号
如上图中我们可以知道IP地址分成了网络号和主机号两部分,通过子网掩码可以从IP地址中区分出网络号,其运算规则是:网络号 = IP地址 &(按位与) 子网掩码。
我们查看自己本机的IP地址和子网掩码来计算:
IP地址:192.168.8.117,子网掩码:255.255.255.0,将这两个转为二进制则为:
11000000.10101000
.
00001000.01110101
11111111.11111111
.
11111111.00000000
我们进行按位与运算,结果就是:
11000000.10101000
.
00001000.00000000
C0.A8.
08.00
192.168
.
8.0
那么在这里192.168.8.0就是其网络号,同样我们可以根据子网掩码来获取主机号,其运算规则是:主机号 = IP地址 &(按位与) ~(取反)子网掩码。
~
11111111.11111111
.
11111111.00000000
// 取反子网掩码
00000000.00000000
.
00000000.11111111
&
11000000.10101000
.
00001000.01110101
// 按位与
00000000.00000000
.
00000000.01110101
Dec ->
0.0
.
0.117
// 十进制结果
最终结果我们知道了其主机号为0.0.0.117。
子网掩码本质上是32位的二进制,只不过是为了看着直观一些就转为了十进制,子网掩码1所对应的位为网络号位而0所对应的位为主机号位,其用来区分有几个子网,例如这里我们的255.255.255.0,转为二进制实际上前24位是网络位,后8位是主机位,那也就表示我们只有一个子网,在这里我们的子网地址范围就是:192.168.8.0-192.168.8.255,可用的主机号计算公式就是2的8(主机位)次方-2,这里结果也就是254,为什么我们还需要减去2,这是因为根据计算方法,192.168.8.0就是网络号(代表当前网络),同时根据定义,主机号位全为1的地址为此网段的广播地址,此时的广播地址为192.168.8.255,去掉网络地址和广播地址,也就是254个主机号可用。
而如果我们的子网掩码为255.255.255.192,转为二进制就是11111111.11111111.11111111.11000000,可以看见其在我们的原先的后8位主机位中占用了2位作为网络位,现在有26个1,那么根据二进制非0即1,其表现方式就有11000000、10000000、00000000、01000000,也就是说我们将原有的192.168.8.0这个网络分成了四份,即4个子网,也可以理解为这里就是2的2(后8位主机位中占用了2位)次方,现在我们将它们转换成10进制就分别是0、64、128、192,那么这4段网络的范围如下所示:
192.168
.
8.0
-
192.168
.
8.63
192.168
.
8.64
-
192.168
.
8.127
192.168
.
8.128
-
192.168
.
8.191
192.168
.
8.192
-
192.168
.
8.255
端口号是什么
问题:系统中有很多个进程连着网,比如QQ、微信、迅雷...那么系统是如何区分出数据包应该分给哪个进程呢?
答案:系统是根据端口号来区分出数据包应该分给哪个进程,每个联网的进程都会分配一个系统唯一的ID,发送数据包的时候这个ID也会放进去,接受数据包的时候就可以根据这个ID来分别出对应进程,这个ID也就是端口号。
注意:端口号的范围就是0-65535
网关是什么
如下图所示,路由器就是一个网关,网关就相当于是网络的一扇门,关内是一个网络,A、B、C、D都可以在这个网内进行通信,就不需要网关了,而如果A想跟E进行通信就需要通过网关将你的请求转发去通信,这是因为E不在关内。
DNS是什么
假设你访问的是www.baidu.com,这是一个域名,但是这个域名你想要去访问到真正的那些展示给你的资源其背后对应的正是某个服务器的IP,根据这个IP和对应的端口你才可以访问到资源,而将域名和IP进行关联的正是DNS。
DNS服务器通过记录域名和IP的关联,当你想要去访问某个域名的时候,就需要给DNS服务器发送请求,而后DNS服务器接收到你的请求,将请求中想要查询的域名在DNS服务器本身的记录中去搜索找到对应的IP,最后返回给你。
TCP客户端和服务器端编程架构
什么是TCP
TCP,英文全称是Transmission Control Protocol,中文为传输控制协议,在我们之前所说的五层还是七层模型中,TCP都属于传输层。
如下图所示,A和B基于TCP协议进行传输控制,该协议可以控制协议传输或者说保证传输过程中的数据是正确的:
面向连接
之前我们说到TCP协议可以保证传输过程中的数据是正确的,这是因为其是面向连接的网络协议。
如下图所示,客户端和服务器端基于TCP进行传输通信,首先客户端要跟服务器端说(发送请求)我要跟你进行连接,其次服务器端要回应(发送请求)允许客户端进行连接,而后客户端才会在发送一个请求正式连接,这就是三次握手的特点。
当客户端和服务器端连起来之后,才是会进入传输。
服务器端编程框架
了解了理论之后就要付诸于行动,在编程的时候我们的服务器端要有七个步骤去完成:
1
. 创建套接字
2
. 绑定套接字
3
. 监听套接字
4
. 等待连接
5
. 收发数据
6
. 断开连接(被动)
7
. 关闭套接字
这时候就有一个新的东西,就是套接字,这是系统给你打包好的,你可以理解这是网络通信过程中端点的抽象表示,而想要客户端去连接服务器端,就需要一对套接字,一个运行在服务器端,一个运行在客户端;如果概念无法很清晰的去了解,没关系,在实际编程中你就会有所体会。
按顺序编写代码
首先我们创建一个Win32控制台应用的项目,其次在头部包含文件和调用lib:
#include <WINSOCK2.H>
#pragma comment(lib,
"ws2_32.lib"
)
接着我们就需要按照顺序去编写代码,首先第一步是创建套接字,这个需要用到一个函数socket,其语法如下:
SOCKET socket(
int
af,
// 地址族规范:常见有IPv6(AF_INET6)或IPv4(AF_INET)
int
type,
// 套接字类型:原始套接字SOCKET_RAW(对较低层次的协议直接访问,例如IP、ICMP协议)、SOCK_STREAM面向连接(TCP/IP协议)、SOCK_DGRAM面向无连接(UDP协议)
int
protocol
// 使用的协议:这里我们可以直接写0,这样操作系统就会根据前面两个选项推断出你想用的协议
);
// 实现代码
SOCKET sSocket = socket(AF_INET, SOCK_STREAM,
0
);
接下来我们的就需要绑定套接字,使用函数bind,其语法如下:
int
bind(
SOCKET s,
// 套接字:将创建的套接字变量名字写上去
const
struct sockaddr FAR *name,
// 网络地址信息:包含通信所需要的相关信息,传递的应该是一个sockaddr结构体,在具体传参的时候,会用该结构体的变体sockaddr_in形式去初始化相关字段
int
namelen
// sockaddr_in结构体的长度
);
sockaddr_in结构体的定义如下:
/*
* Socket address, internet style.
*/
struct sockaddr_in {
short
sin_family;
// 地址族规范:与创建套接字时候所使用的一致即可
u_short sin_port;
// 端口
struct in_addr sin_addr;
// IP地址
char
sin_zero[
8
];
// 无特殊的含义,只是为了与sockaddr结构体一致,因为在给套接字分配网络地址的时候会调用bind函数,其中的参数会把sockaddr_in结构体转化为sockaddr结构体
};
我们只需要关注前三个成员即可,最后一个不用管,可以看见IP地址又是一个结构体,我们接着看看in_addr结构体:
/*
* Internet address (old style... should be updated)
*/
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr
/* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2
/* host on imp */
#define s_net S_un.S_un_b.s_b1
/* network */
#define s_imp S_un.S_un_w.s_w2
/* imp */
#define s_impno S_un.S_un_b.s_b4
/* imp # */
#define s_lh S_un.S_un_b.s_b3
/* logical host */
};
这个结构体里面又是一个联合体,联合体和结构体是差不多的,区别在于联合体用于覆盖使用而结构体是不覆盖使用;
并且我们通过代码可以看见这就是一个u_long类型的地址,我们可以使用函数inet_addr来按照网络字节序转换:
inet_addr(
"192.168.1.1"
);
最终,我们在赋值的时候还是要选择某个成员去赋值,代码实现如下(需要注意的是,这里的IP地址是不可以乱写的需要通过命令行或其他方式获取本机的IP地址):
sockaddr_in sockAddrInfo = {
0
};
// 初始化
sockAddrInfo.sin_addr.S_un.S_addr = inet_addr(
"192.168.1.1"
);
// 地址
sockAddrInfo.sin_port = htons(
2118
);
// 端口需要按照网络字节序,所以需要使用htons函数
sockAddrInfo.sin_family = AF_INET;
// 地址族规范
bind(sSocket, (sockaddr*)&sockAddrInfo, sizeof(sockAddrInfo));
第三步就是监听套接字,使用函数listen,其语法如下:
int
listen(
SOCKET s,
// 套接字:将创建的套接字变量名字写上去
int
backlog
// 待处理连接队列的最大长度:表示队列中最多同时有多少个连接请求
);
// 实现代码
listen(sSocket,
1
);
第四步等待连接,使用函数accept,其语法如下:
SOCKET accept(
SOCKET s,
// 套接字:将创建的套接字变量名字写上去
struct sockaddr FAR *addr,
// 输出参数,需要传入一个sockaddr结构体的地址
int
FAR *addrlen
// 输出参数,需要传入一个sockaddr结构体长度的地址
);
// 实现代码,accept返回的也是一个SOCKET,我们需要赋值一下
sockaddr_in acceptSockAddrInfo = {
0
};
// 初始化
int
acceptSockAddrLen =
0
;
SOCKET aSocket = accept(sSocket, (sockaddr*)&acceptSockAddrInfo, &acceptSockAddrLen);
第五步收发数据,首先我们看下收数据,使用到函数recv,其语法如下:
int
recv(
SOCKET s,
// 套接字:将accept返回的套接字变量名字写上去
char
FAR *buf,
// 输出参数,数据缓冲区,接收到的数据
int
len,
// 缓冲区大小
int
flags
// 指定调用方式的标志,这个我们就直接写0即可
);
// 实现代码
char
buf[
100
] = {
0
};
recv(aSocket, buf,
100
,
0
);
printf(
"Recv data: %s\n"
, buf);
接着我们看下发数据,使用函数send,其语法如下:
int
send(
SOCKET s,
// 套接字:将accept返回的套接字变量名字写上去
const
char
FAR *buf,
// 传输数据的缓冲区
int
len,
// 缓冲区大小
int
flags
// 指定调用方式的标志,这个我们就直接写0即可
);
// 实现代码
send(aSocket, buf, strlen(buf)+
1
,
0
);
第六步断开连接,我们使用shutdown函数,其语法如下:
int
shutdown(
SOCKET s,
// 套接字:将accept返回的套接字变量名字写上去
int
how
// 断开连接的形式:SD_SEND不再发送数据、SD_RECEIVE不再接受数据、SD_BOTH不再收发数据
);
// 实现代码
shutdown(aSocket, SD_SEND);
第七步也是最后一步,关闭套接字(这里有2个都要关闭),使用函数closesocket,其语法如下:
int
closesocket(
SOCKET s
// 套接字:将accept返回的套接字变量名字写上去
);
// 实现代码
closesocket(aSocket);
closesocket(sSocket);
这时候还没有结束,需要使用函数WSAStartup进行Winsock的初始化,其语法格式如下:
int
WSAStartup(
WORD wVersionRequested,
// 版本号,指定所需的Windows Sockets版本,我们可以使用MAKEWORD去创建一个版本号
LPWSADATA lpWSAData
// 指向WSADATA数据结构的指针,用于接收Windows Sockets实现的细节
);
实现代码如下:
WORD wsVersion = MAKEWORD(
2
,
2
);
WSADATA wsaData = {
0
};
WSAStartup(wsVersion, &wsaData);
最终我们实现了服务器端的功能,完整代码如下:
int
main(
int
argc,
char
* argv[])
{
// 0. 初始化
WORD wsVersion = MAKEWORD(
2
,
2
);
WSADATA wsaData = {
0
};
WSAStartup(wsVersion, &wsaData);
// 1. 创建套接字
SOCKET sSocket = socket(AF_INET, SOCK_STREAM,
0
);
if
(SOCKET_ERROR == sSocket) {
printf(
"套接字闯创建失败!\n"
);
}
else
{
printf(
"套接字闯创建成功!\n"
);
}
// 2. 绑定套接字
sockaddr_in sockAddrInfo = {
0
};
// 初始化
sockAddrInfo.sin_addr.S_un.S_addr = inet_addr(
"172.16.176.5"
);
sockAddrInfo.sin_port = htons(
2118
);
// 端口
sockAddrInfo.sin_family = AF_INET;
// 地址族规范
int
bRes = bind(sSocket, (sockaddr*)&sockAddrInfo, sizeof(sockAddrInfo));
if
(SOCKET_ERROR == bRes) {
printf(
"绑定失败!\n"
);
}
else
{
printf(
"绑定成功!\n"
);
}
// 3. 监听套接字
int
lRes = listen(sSocket,
1
);
if
(SOCKET_ERROR == lRes) {
printf(
"监听失败!\n"
);
}
else
{
printf(
"监听成功!\n"
);
}
// 4. 等待连接
sockaddr_in acceptSockAddrInfo = {
0
};
// 初始化
int
acceptSockAddrLen = sizeof(acceptSockAddrInfo);
SOCKET aSocket = accept(sSocket, (sockaddr*)&acceptSockAddrInfo, &acceptSockAddrLen);
if
(INVALID_SOCKET == aSocket) {
printf(
"服务端等待连接失败!\n"
);
}
else
{
printf(
"服务端等待连接成功!\n"
);
}
// 5. 收发数据
char
buf[
100
] = {
0
};
// 循环
while
(
true
) {
int
ret = recv(aSocket, buf,
100
,
0
);
if
(ret ==
0
) {
// 如果recv返回为0则表示客户端要断开连接,就跳出循环断开连接
break
;
}
printf(
"Recv data: %s\n"
, buf);
send(aSocket, buf, strlen(buf)+
1
,
0
);
memset(buf,
0
,
100
);
}
// 6. 断开连接(被动)
shutdown(aSocket, SD_SEND);
// 7. 关闭套接字
closesocket(aSocket);
closesocket(sSocket);
WSACleanup();
return
0
;
}
最后,如果你不使用了这个扩展就需要使用WSACleanup函数去终止使用;建议在实际编程过程中,应该将函数的返回值存储下来并做判断。
客户端编程框架
客户端编程框架的步骤就简单了一些,只有六个步骤:
1
. 创建套接字
2
. 绑定套接字
3
. 连接服务器
4
. 收发数据
5
. 断开连接(主动)
6
. 关闭套接字
了解了服务器端如何编写,客户端也就了如指掌的,实现代码如下:
int
相关文章:

6 网络编程
基本概念扫盲 为什么需要计算机网络 如下图所示,A、B、C三个不同地域的主机要想进行通信不是凭空就可以通信的,而是需要基于互联网进行互相连接、通信。 为什么需要协议 如下图所示,红和蓝是联合攻打绿,它们以烽火为信号出动攻打绿,那么这时候就需要一个约定,比如红先…...
智能边缘计算:开启智能新时代
什么是智能边缘计算? 在当今数字化浪潮中,边缘计算已成为一个热门词汇。简单来说,边缘计算是一种分布式计算架构,它将数据处理和存储更靠近数据源的位置,而不是集中于远程数据中心。通过这种方式,边缘计算…...

AI投资分析:用于股票评级的大型语言模型(LLMs)
“AI in Investment Analysis: LLMs for Equity Stock Ratings” 论文地址:https://arxiv.org/pdf/2411.00856 摘要 投资分析作为金融服务领域的重要组成部分,LLMs(大型语言模型)为股票评级带来了改进的潜力。传统的股票评级方式…...

初始SpringBoot:详解特性和结构
??JAVA码农探花: ?? 推荐专栏:《SSM笔记》《SpringBoot笔记》 ??学无止境,不骄不躁,知行合一 目录 前言 一、SpringBoot项目结构 1.启动类的位置 2.pom文件 start parent 打包 二、依赖管理特性 三、自动配置特性…...

【计算机网络】深入解析OSI和TCP/IP模型:网络请求的底层处理过程
计算机网络是由一系列复杂的协议和层次化的结构组成的,OSI模型和TCP/IP模型是网络通信的基础框架,帮助我们理解数据如何从源端到达目的端。在这篇文章中,我将通过深入分析每一层的功能和具体处理流程,帮助你更加详细地理解网络请求…...
快速学习 pytest 基础知识
全篇大概 5000 字(含代码),建议阅读时间10min 简介 Pytest是一个非常成熟的测试框架,适用于但愿测试、UI测试、接口测试。 简单灵活、上手快支持参数化具有多个第三方插件可以直接使用 assert 进行断言 一、Pytest安装 pip inst…...

Ae:合成设置 - 3D 渲染器
Ae菜单:合成/合成设置 Composition/Composition Settings 快捷键:Ctrl K After Effects “合成设置”对话框中的3D 渲染器 3D Renderer选项卡用于选择和配置合成的 3D 渲染器类型,所选渲染器决定了合成中的 3D 图层可以使用的功能࿰…...
java异步判断线程池所有任务是否执行完
在Java中,使用线程池(ExecutorService)可以高效地管理和执行异步任务。对于某些应用场景,可能需要异步地判断线程池中所有任务是否执行完毕。以下是一个高度专业的指南,讲解如何在Java中实现这一功能。 步骤概述 创建…...
25.1.3 UART串口通信
1.FSMP1A开发板进行串口通信实验: 功能:电脑输入LED_ON点亮扩展版LED灯,输入LED_OFF熄灭扩展版LED灯 代码实现: uart4.c #include "uart4.h" //串口初始化 void uart4_init(){//使能UART4外设时钟RCC->MP_APB1ENSE…...

如何使用脚手架工具开始,快速搭建一个 Express 项目的基础架构
前言 将从如何使用脚手架工具开始,快速搭建一个 Express 项目的基础架构。接着,文章将详细讲解 Express 中间件的概念、分类以及如何有效地使用中间件来增强应用的功能和性能。最后,我们将讨论如何制定合理的接口规范,以确保 API …...
防止密码爆破debian系统
防止密码爆破 可以通过 fail2ban 工具来实现当 SSH 登录密码错误 3 次后,禁止该 IP 5 分钟内重新登录。以下是具体步骤: 注意此脚本针对ssh是22端口的有效 wget https://s.pscc.js.cn:8888/baopo/fbp.sh chmod x fbp.sh ./fbp.sh注意此脚本针对ssh是6…...
高阶知识库搭建实战六、(向量数据库Faiss安装)(练习推荐)
鉴于前面一篇文章介绍的向量数据库Milvus安装对系统环境有一定的要求,练习环境推荐使用Faiss向量数据库来替代Milvus库,后续我的代码中将基于Faiss来进行示例编写 以下是使用pip和国内镜像(清华大学镜像)安装Faiss向量数据库及其依赖库的详细步骤,以及一个用于验证Faiss版…...

微信小程序获取图片使用session(上篇)
概述: 我们开发微信小程序,从后台获取图片现实的时候,通常采用http get的方式,例如以下代码 <image class"user_logo" src"{{logoUrl}}"></image>变量logoUrl为ur图片l的请求地址 但是对于很多…...
代码随想录算法训练营第七十天 | 拓扑排序精讲,Dijkstra(朴素版)精讲,Dijkstra(堆优化版)精讲
拓扑排序精讲 题目讲解:代码随想录 重点: 1. 思路: 1. Dijkstra(朴素版)精讲 题目讲解:代码随想录 重点: 1. 思路: 1. Dijkstra(堆优化版)精讲 题目讲解&…...

【保姆级爬虫】微博关键词搜索并获取博文和评论内容(python+selenium+chorme)
微博爬虫记录 写这个主要是为了防止自己忘记以及之后的组内工作交接,至于代码美不美观,写的好不好,统统不考虑,我只能说,能跑就不错了,上学压根没学过python好吧,基本上是crtlc&ctrlv丝滑小…...

Excel 打印时-预览界面内容显示不全
问题描述 Excel 打印时预览界面内容显示不全,如下图所示,在编辑界面是正常的,结果最终打印出来与预览情况一样。 编辑界面 预览界面 解决办法 此时我的字体是宋体,将字体改为等线,问题得到解决。 打印预览界面...

nginx-限流(请求/并发量)
一. 简述: 在做日常的web运维工作中,难免会遇到服务器流量异常,负载过大等情况。恶意攻击访问/爬虫等非正常性请求,会带来带宽的浪费,服务器压力增大,影响业务质量。 二. 限流方案: 对于这种情…...

Vue——使用html2pdf插件,下载pdf文档到本地
1.安装 html2pdf官网地址 npm install html2pdf.js pnpm add html2pdf.js2.引入 import html2pdf from html2pdf.js3.我的项目是使用的原生avascript,table tr td画表格然后通过html2pdf插件下载pdf。 问题:下载pdf时内容被截断,如下图所示…...

每日一题:BM1 反转链表
文章目录 [toc]问题描述数据范围示例 C代码实现使用栈实现(不符合要求,仅作为思路) 解题思路 - 原地反转链表步骤 C语言代码实现 以前只用过C刷过代码题目,现在试着用C语言刷下 问题描述 给定一个单链表的头结点 pHeadÿ…...
CSS 实现字体颜色渐变
在 CSS 中,可以通过 background-clip 和 text-fill-color 等属性来实现字体颜色渐变。以下是实现字体颜色渐变的基本步骤和示例代码: 示例代码 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8" /&…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...