C#实现本地服务器客户端私聊通信
(一)需求
在游戏中我们经常能够看到玩家与玩家之间可以进行私聊,在QQ或微信中最基本的功能就是用户与用户之间的通信。抽象成计算机网络,就是两个客户端通过服务器进行私聊通信,两个客户端可以互相看到对方发送过来的信息。这种两个客户端的私聊通信是如何实现的呢?在本篇文章我们就来探讨一下。
(二)解决思路
这个需求的重点部分在于网络通信,需要我们掌握基本的计算机网络通信知识,具体到每种编程语言又有对应的API。如果把这个需求抽象到计算机网络中,我们就可以理解成两个客户端向服务器发送信息,服务器接收信息后又把信息发送给另一个客户端。这样,一个客户端就可以接收到另一个客户端发送的信息了。
(三)设计思路
服务器基于本地服务器开发,通过一个单独的C#控制台项目模拟,编程语言使用C#,客户端通过Unity3D构建GUI并编写客户端脚本。两个客户端则通过打开两个Unity3D项目的可执行文件进行模拟,客户端的GUI需要有调试面板、客户端名称下拉菜单、连接和断开连接按钮、消息显示面板、消息输入框和消息发送按钮等。
(四)代码实现
由于代码中引用了自定义的网络通信共享库NetShare,关于NetShare请阅读这篇文章。
客户端
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.UI;
/*
自定义网络通信共享库NetShare,包括通用数据包DataPacket、私聊频道服务器数据包PSDataPacket、
服务器数据包ServerDataPacket和私聊频道客户端数据包PCDataPacket等。
*/
using NetShare;
using System.Threading;
using System.Linq;//私聊频道客户端
public class PersonalChannelClient : MonoBehaviour
{public Text BaseInfo;//显示Socket连接基本信息的文本public Text EchoContents;//Socket回显信息的文本public Text ChatContents;//聊天信息的文本public Dropdown Friends;//目标客户端名称下拉菜单public Dropdown ClientMenu;//客户端名称下拉菜单public Button Connect;//连接按钮public Button DisConnect;//断开连接按钮public InputField SendInput;//聊天消息输入框public Button Send;//聊天信息发送按钮static string ipAddressStr;//IP地址字符串static int port;//端口static IPAddress iPAddress;//IP地址对象static IPEndPoint iPEndPoint;//IP端点对象string clientName, sendStr;//客户端名称和发送信息字符串Socket currentClientSocket;//当前客户端Socketbool isLockSend;//是否锁定聊天信息发送按钮byte[] buffer;//消息接收缓冲区Queue<string> echoContentQueue, chatContentQueue;//回显信息队列和聊天信息队列PCDataPacket dataPacket;//通用数据包List<string> desClientNames;//目标客户端名称合集//反映Socket是否与服务器有效连接的属性bool isConnected{get{if (currentClientSocket == null) return false;return !currentClientSocket.Poll(10, SelectMode.SelectRead) && currentClientSocket.Connected;}}void Start(){//初始化ipAddressStr = "127.0.0.1";clientName = ClientMenu.options.Count > 0 ? ClientMenu.options[0].text : "";port = 5500;iPAddress = IPAddress.Parse(ipAddressStr);iPEndPoint = new IPEndPoint(iPAddress, port);buffer = new byte[1024];echoContentQueue = new Queue<string>();chatContentQueue = new Queue<string>();desClientNames = new List<string>() { "None" };//为UI控件添加监听事件ClientMenu.onValueChanged.AddListener((index) =>{clientName = ClientMenu.options[index].text;});Connect.onClick.AddListener(() =>{Thread thread = new Thread(new ThreadStart(ConnectDeal));thread.Start();});DisConnect.onClick.AddListener(() =>{Thread thread = new Thread(new ThreadStart(DisConnectDeal));thread.Start();});Send.onClick.AddListener(() =>{sendStr = SendInput.text;Thread thread = new Thread(new ThreadStart(SendDeal));thread.Start();SendInput.text = string.Empty;});}void Update(){//不断更新Socket基本信息BaseInfo.text = $"ClientName:{clientName}" +string.Format("\nSocketHashCode:{0}", currentClientSocket == null ? "None" : currentClientSocket.GetHashCode().ToString()) +$"\nisLock:{isLockSend}" +string.Format("\nPoll:{0}", currentClientSocket == null ? "None" : (!currentClientSocket.Poll(10, SelectMode.SelectRead)).ToString()) +string.Format("\nIsConnected:{0}", currentClientSocket == null ? "False" : currentClientSocket.Connected.ToString());//更新回显信息if (echoContentQueue.Count > 0){while (echoContentQueue.Count > 0){SetEchoContents(echoContentQueue.Dequeue());}}//更新聊天信息if (chatContentQueue.Count > 0){while (chatContentQueue.Count > 0){SetChatContents(chatContentQueue.Dequeue());}}//更新目标客户端名称下拉菜单if (desClientNames?.Count > 0){Friends.AddOptions(desClientNames);desClientNames.Clear();}}//设置回显信息相关UI的内容void SetEchoContents(string text){EchoContents.text += text;}//设置聊天信息相关UI的内容void SetChatContents(string text){ChatContents.text += text;}//执行逻辑:Socket异步连接处理void ConnectDeal(){echoContentQueue.Enqueue($"\n客户端{clientName}正在请求服务器连接...");Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);clientSocket.BeginConnect(iPEndPoint, ConnectCallback, clientSocket);}//执行逻辑:Socket异步断开连接处理void DisConnectDeal(){echoContentQueue.Enqueue($"\n客户端{clientName}正在断开与服务器的连接...");if (isConnected){currentClientSocket.Shutdown(SocketShutdown.Both);currentClientSocket.BeginDisconnect(false, DisConnectCallback, currentClientSocket);}else echoContentQueue.Enqueue($"\n客户端{clientName}未与服务器建立连接,无法进行断开连接的操作...");}//执行逻辑:Socket异步接收信息处理void ReceiveDeal(){echoContentQueue.Enqueue($"\n客户端{clientName}开始监听服务器响应...");currentClientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, currentClientSocket);}//执行逻辑:Socket异步发送信息处理void SendDeal(){if (!isLockSend && !string.IsNullOrEmpty(sendStr)){dataPacket.mContent = sendStr;string v_desClientName = Friends.options[Friends.value].text;if (!v_desClientName.Equals("None")) dataPacket.mDestinationClientName = v_desClientName;byte[] bytes = dataPacket.ToBytes();currentClientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallback, currentClientSocket);}}//执行逻辑:Socket异步连接处理回调void ConnectCallback(IAsyncResult ar){try{Socket socket = ar.AsyncState as Socket;socket.EndConnect(ar);currentClientSocket = socket;if (isConnected){dataPacket = new PCDataPacket(){mLocalEndPointStr = socket.LocalEndPoint.ToString(),mClientName = clientName,mDestinationClientName = string.Empty,mContent = $"成功与服务器建立连接!"};byte[] bytes = dataPacket.ToBytes();socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallback, currentClientSocket);isLockSend = false;echoContentQueue.Enqueue($"\n<color=orange>客户端{clientName}与服务器连接成功!</color>");ReceiveDeal();}else echoContentQueue.Enqueue($"\n<color=red>客户端{clientName}与服务器连接失败!</color>");}catch (SocketException se){echoContentQueue.Enqueue($"\n<color=red>客户端{clientName}与服务器连接失败!</color>\n错误信息:{se.Message}");}}//执行逻辑:Socket异步断开连接处理回调void DisConnectCallback(IAsyncResult ar){try{isLockSend = true;Socket socket = ar.AsyncState as Socket;socket.EndDisconnect(ar);dataPacket = null;echoContentQueue.Enqueue($"\n<color=orange>客户端{clientName}与服务器断开连接操作成功!</color>");}catch (SocketException se){echoContentQueue.Enqueue($"\n客户端{clientName}与服务器断开连接操作失败!\n错误信息:{se.Message}");}}//执行逻辑:Socket异步发送信息处理回调void SendCallback(IAsyncResult ar){try{Socket socket = ar.AsyncState as Socket;socket.EndSend(ar);echoContentQueue.Enqueue($"\n客户端{clientName}向服务器发送了一条消息!");}catch (SocketException se){echoContentQueue.Enqueue($"\n客户端{clientName}向服务器发送信息操作失败!\n错误信息:{se.Message}");}}//执行逻辑:Socket异步接收信息处理回调void ReceiveCallback(IAsyncResult ar){try{Socket socket = ar.AsyncState as Socket;int count = socket.EndReceive(ar);DataPacket dataPacket = DataPacket.ToObject<DataPacket>(buffer.Take(count).ToArray());if (dataPacket is PSDataPacket psdp && psdp.mClientNames?.Length > 0){desClientNames.Clear();foreach (string name in psdp.mClientNames){if (Friends.options.FindIndex((od) => od.text.Equals(name)) == -1) desClientNames.Add(name);}}else if (dataPacket is ServerDataPacket sdp){string v_res = sdp.mContent;if (!string.IsNullOrEmpty(v_res)) chatContentQueue.Enqueue("\n" + v_res);}//若Socket连接有效则继续接收消息if (isConnected)socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, socket);}catch (SocketException se){echoContentQueue.Enqueue($"\n客户端{clientName}接收服务器消息失败!\n错误信息:{se.Message}");}}
}
服务器
using System.Net.Sockets;
using System.Net;/*
自定义网络通信共享库NetShare,其中包括了私聊频道服务器数据包PSDataPacket、通用数据包DataPacket、
服务器数据包ServerDataPacket和私聊频道客户端数据包PCDataPacket等。*/using NetShare;namespace UnityServer
{//私聊频道服务器internal class PersonalChannelServer{private static string ipAddress = "127.0.0.1";//IP地址字符串private static int port = 5500;//端口private static int maxConnect = 20;//最大连接数private static byte[] buffer = new byte[1024];//消息缓冲区//客户端Socket合集,key为IPEndPoint字符串,value为服务器为客户端分配的Socketprivate static Dictionary<string, Socket> clients = new Dictionary<string, Socket>();private static Socket? serverSocket;//服务器Socket//客户端键值对,key为客户端名称,value为IPEndPoint字符串private static Dictionary<string, string> clientKVs = new Dictionary<string, string>();private static void Main(string[] args){Thread thread = new Thread(new ThreadStart(ServerDeal));thread.Start();Console.ReadLine();}//判断Socket是否进行有效连接private static bool IsConnected(Socket socket){if (socket == null) return false;return !socket.Poll(10, SelectMode.SelectRead) && socket.Connected;}//执行逻辑:服务器处理private static void ServerDeal(){serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress v_ipAddress = IPAddress.Parse(ipAddress);serverSocket.Bind(new IPEndPoint(v_ipAddress, port));serverSocket.Listen(maxConnect);Console.WriteLine($"开启服务器[{serverSocket.LocalEndPoint}]...");serverSocket.BeginAccept(AcceptCallback, null);}//执行逻辑:Socket异步接收消息private static void ReceiveDeal(object? clientSocket){Console.WriteLine("********************");if (clientSocket == null) return;Socket? v_clientSocket = clientSocket as Socket;if (v_clientSocket == null) return;Console.WriteLine("接收到客户端的连接请求!");if (IsConnected(v_clientSocket))v_clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, v_clientSocket);}//添加客户端Socket到客户端Socket合集private static void AddClient(Socket clientSocket){if (clientSocket == null) return;EndPoint? endPoint = clientSocket.RemoteEndPoint;if (endPoint != null){string? v_endPointStr = endPoint.ToString();if (v_endPointStr != null) clients[v_endPointStr] = clientSocket;}}//向所有客户端发送指定信息private static void SendToAll(PSDataPacket dataPacket){if (dataPacket == null) return;byte[] bytes = dataPacket.ToBytes();foreach (Socket clientSocket in clients.Values){if (IsConnected(clientSocket)){Thread thread = new Thread(() =>{clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallback, clientSocket);});thread.Start();}}}//向指定的客户端发送服务器数据包private static void SendTo(ServerDataPacket dataPacket, Socket? destinationSocket){if (dataPacket == null || destinationSocket == null) return;byte[] bytes = dataPacket.ToBytes();if (IsConnected(destinationSocket)){Thread thread = new Thread(() =>{destinationSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallback, destinationSocket);});thread.Start();}}//Socket监听请求回调private static void AcceptCallback(IAsyncResult ar){try{if (serverSocket != null){Socket clientSocket = serverSocket.EndAccept(ar);AddClient(clientSocket);Thread thread = new Thread(new ParameterizedThreadStart(ReceiveDeal));thread.Start(clientSocket);serverSocket.BeginAccept(AcceptCallback, null);}}catch (SocketException se){Console.WriteLine("AcceptException:" + se.Message);}}//Socket发送信息回调private static void SendCallback(IAsyncResult ar){try{Socket? clientSocket = ar.AsyncState as Socket;if (clientSocket != null) clientSocket.EndSend(ar);}catch (SocketException se){Console.WriteLine("SendException:" + se.Message);}}//Socket接收信息回调private static void ReceiveCallback(IAsyncResult ar){try{Socket? clientSocket = ar.AsyncState as Socket;if (clientSocket != null){int bytesCount = clientSocket.EndReceive(ar);PCDataPacket? dataPacket = DataPacket.ToObject<PCDataPacket>(buffer.Take(bytesCount).ToArray());if (dataPacket != null){if (clientSocket.RemoteEndPoint != null){string? v_endPointStr = clientSocket.RemoteEndPoint.ToString();if (!string.IsNullOrEmpty(v_endPointStr)){clientKVs[dataPacket.mClientName] = v_endPointStr;SendToAll(new PSDataPacket(){mClientNames = clientKVs.Keys.ToArray()});}}string v_content = $"客户端{dataPacket.mClientName}:{dataPacket.mContent}";Socket? destinationSocket;string? endPointStr;clientKVs.TryGetValue(dataPacket.mDestinationClientName, out endPointStr);if (!string.IsNullOrEmpty(endPointStr) && clients.TryGetValue(endPointStr, out destinationSocket))SendTo(new ServerDataPacket() { mContent = v_content }, destinationSocket);}if (IsConnected(clientSocket))clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, clientSocket);}}catch (SocketException se){Console.WriteLine("ReceiveException:" + se.Message);}}}
}
(五)测试
测试流程大概是先启动服务器,然后启动三个客户端,三个客户端分别以A、B、C的名称作为客户端名称与服务器建立连接,连接后再由客户端A、B、C分别向服务器发送信息,通过观察三个客户端的消息面板来确定测试结果,这里之所以启动三个客户端是为了进行对比测试,以区分多客户端的同频道通信,具体测试流程请观看下列视频:
本地服务器客户端单独通信
(六)总结
在服务器端,我们通过一个C#控制台项目来模拟服务器后台,服务器与客户端具有类似的功能,同样具有发送、接收消息的功能,不同的是服务器具有监听客户端连接的功能,而客户端具有向服务器发送连接请求的功能,本质上这些都是通过Socket实现的功能,人为划分成服务器端和客户端。在客户端我们通过GUI将用户的操作进行可视化构建,实现了回显、客户端名称选择、连接、断开连接、发送和显示消息等基本交互。
为了模拟多客户端并发操作,所有功能我们都采用了异步的方式启动,对于真正的网络通信而言,这对我们来说才刚刚开始,不过通过这个案例也让我们了解了基本的网络通信流程。
如果这篇文章对你有帮助,请给作者点个赞吧!
相关文章:
C#实现本地服务器客户端私聊通信
(一)需求 在游戏中我们经常能够看到玩家与玩家之间可以进行私聊,在QQ或微信中最基本的功能就是用户与用户之间的通信。抽象成计算机网络,就是两个客户端通过服务器进行私聊通信,两个客户端可以互相看到对方发送过来的信…...
PyTorch 之 Dataset 类入门学习
PyTorch 之 Dataset 类入门学习 Dataset 类简介 PyTorch 中的 Dataset 类是一个抽象类,用来表示数据集。通过继承 Dataset 类可以进行自定义数据集的格式、大小和其它属性,供后续使用; 可以看到官方封装好的数据集也是直接或间接的继承自 …...
Java update scheduler
引言 Java 更新调度器是 Java 中的一个特性,可以自动化 Java 应用程序的更新过程。它提供了一种方便的方式来安排 Java 应用程序的更新,确保其与最新的功能、错误修复和安全补丁保持同步。本文将深入介绍如何使用 Java 更新调度器,并解释它对…...
常见树种(贵州省):006栎类
摘要:本专栏树种介绍图片来源于PPBC中国植物图像库(下附网址),本文整理仅做交流学习使用,同时便于查找,如有侵权请联系删除。 图片网址:PPBC中国植物图像库——最大的植物分类图片库 一、麻栎 …...
拓扑排序-
有向无环图是拓扑排序 拓扑排序将图中所有的顶点排成一个线性序列,使得所有的有向边均从序列的前面指向后面。 拓扑排序使用深度优先搜索来实现,图中有环则无法进行拓扑排序 一个有向图,如果图中有入度为0的点,就把这个点删掉…...
Oracle数据库如何定位trace file位置
用一个示例来说明吧。 在导入master key时,出现错误: ADMINISTER KEY MANAGEMENTIMPORT KEYS WITH SECRET "my_secret"FROM /tmp/export.expIDENTIFIED BY keypwd5 WITH BACKUP; ADMINISTER KEY MANAGEMENT * ERROR at line 1: ORA-46655…...
电脑盘符错乱,C盘变成D盘怎么办?
在一些特殊情况下,磁盘盘符会出现错乱,C盘可能会变成D盘。那么,这该怎么办呢?下面我们就来了解一下。 通过磁盘管理更改盘符 磁盘管理是Windows自带的工具,它位于“计算机管理”的控制台中。管理硬盘及其所包含的卷或…...
Android WMS——客户端输入事件处理(十九)
前面的文章我们介绍了 WMS 中的输入服务的启动及事件处理,这一篇我们来看一下客户端对输入事件的处理。 一、事件初始化 事件的初始化就是在添加窗口的过程。 1、ViewRootImpl 源码位置:/frameworks/base/core/java/android/view/ViewRootImpl.java public void setView(…...
Python基础学习__测试报告
# 使用pycharm生成报告:只有在单独执行一个TestCase文件时可以生成,使用TestSuite等就不能用了 # 使用第三方的测试报告:例如:HTMLTestRunner第三方类库 #使用HTMLTestRunner这个执行对象# 1.获取第三方的测试运行类Runner模块(一个py文件),将其放在代码目录下 # 2.导包:unitte…...
bclinux aarch64 ceph 14.2.10 云主机 4节点 fio
ceph -s 由于是基于底层分布式存储的云主机,数据仅供参考 本地云盘性能 direct1 1M读取 IOPS134, BW134MiB/s [rootceph-client rbd]# cd / [rootceph-client /]# fio -filenamefio.bin -direct1 -iodepth 128 -thread -rwread -ioenginelibaio -bs1M -size10G -n…...
智能座舱架构与芯片- (14) 测试篇 上
一、 验证平台概要 1.1 测试软件方法论 “软件定义汽车” 的时代,软件在整车制造中的重要性日渐凸显。但不同于其他行业的软件开发,汽车行业有自己独特的软件开发要求。首先是需求严谨、需求层次复杂、需要通过专业的工具进行管理;其次开发…...
【Django-DRF用法】多年积累md笔记,第3篇:Django-DRF的序列化和反序列化详解
本文从分析现在流行的前后端分离Web应用模式说起,然后介绍如何设计REST API,通过使用Django来实现一个REST API为例,明确后端开发REST API要做的最核心工作,然后介绍Django REST framework能帮助我们简化开发REST API的工作。 全…...
Redis主从复制,哨兵和Cluster集群
主从复制: 主从复制是高可用Redis的基础,哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份(和同步),以及对于读操作的负载均衡和简单的故障恢复。 缺陷:故障恢复无法自动化…...
Linux嵌入式I2C协议笔记
硬件: 1.I2C结构 在一个SOC中有一个或者多个I2C控制器,一个I2C控制器可以连接一个或多个I2C设备。 I2C总线需要两条线,时钟线SCL和数据线SDA 2.I2C传输数据格式 开始信号(S):SCL为高电平时,S…...
科技的成就(五十三)
503、任天堂首次公开 Switch 2016 年 10 月 20 日,任天堂首次公开 Switch 正式名称及造型。Switch 是任天堂推出的混合型游戏机,可作为家用游戏机,也可作为便携式掌机。Switch 在开发过程中就以代号 NX 而闻名,成为当年的现象级产…...
Ubuntu22.04 编译 AOSP
在 Ubuntu 22.04 系统上搭建环境编译 AOSP(Android Open Source Project)需要进行以下步骤: 1, 更新系统:首先,确保您的 Ubuntu 22.04 系统已经更新到最新版本。可以使用以下命令进行系统更新: sudo apt update sudo apt upgrade2,安装必要的软件包:AOSP 编译需要一些…...
【计算机网络】多路复用的三种方案
文章目录 1. selectselect函数select的工作特性select的缺点 2. pollpoll函数poll与select的对比 3. epollepoll的三个接口epoll的工作原理epoll的优点LT和ET模式epoll的应用场景 🔎Linux提供三种不同的多路转接(又称多路复用)的方案…...
供应链和物流的自动化新时代
今天,当大多数人想到物流自动化时,他们会想到设备。机器人、无人机和自主卡车运输在大家的谈话中占主导地位。全自动化仓库的视频在网上流传,新闻主播们为就业问题绞尽脑汁。这种炒作是不完整的,它错过了供应链和物流公司的机会。…...
Python与ArcGIS系列(九)自定义python地理处理工具
目录 0 简述1 创建自定义地理处理工具2 创建python工具箱0 简述 在arcgis中可以进行自定义工具箱,将脚本嵌入到自定义的可交互窗口工具中。本篇将介绍如何利用arcpy实现创建自定义地理处理工具以及创建python工具箱。 1 创建自定义地理处理工具 在arctoolbox中的自定义工具箱…...
Nginx部署前端项目
Nginx部署前端项目 1.在nginx官网http://nginx.org/en/download.html ,下载稳定版本: 2.解压后,点击根目录中的nginx.exe即可启动Nginx,或是在nginx安装目录中启动cmd并输入以下命令启动: nginx.exe 或 start nginx3…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
