Linux网络-Socket套接字_Windows与Linux端双平台基于Udp传输协议进行多线程跨平台的服务器与客户端网络通信的简易聊天室实现

文章目录

  • 一、Socket套接字
  • 二、Udp 常见API
    • 1. int socket(int domain, int type, int protocol);
    • 2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);
      • struct sockaddr
    • 3. ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address,socklen_t *restrict address_len);
    • 4. ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr,socklen_t dest_len);
    • 5. uint16_t htons(uint16_t hostshort);
      • 网络字节序
    • 6. in_addr_t inet_addr(const char *cp);
  • 三、Server端
    • 1.初始化Socket套接字
    • 2.发送和接收
    • Server端全代码
      • const std::string default_ip = "0.0.0.0";
  • Client端
  • Windows Client端
  • 简易聊天室运行图


一、Socket套接字

在互联网中,我们在网络层采用 IP + 端口号 的方式即可在全网中找到唯一进程,IP标识唯一主机,端口号标识该主机端口号绑定的进程。

这就是Socket套接字。


二、Udp 常见API

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

创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)

参数 int domin :指定要在其中创建套接字的通信域。这里我们使用AF_INET采用IPV4通信域。

参数 int type : 指定要创建的套接字类型。 Udp协议是采用面向数据流,所以是用SOCK_DGRAM。

参数 int protocol :指定与套接字一起使用的特定协议。

返回值: 如果成功则返回创建的socket 文件描述符, 失败则返回-1.

代码示例

		int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            exit(1);
        }

2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);

绑定端口号 (TCP/UDP, 服务器)
参数 int socket 就是使用socket接口函数所创建的那个socket文件描述符。

struct sockaddr

在这里插入图片描述
由于底层有不同的网络协议,所以它们的地址格式并不相同,所以通常使用struct sockaddr* 作为参数,然后根据前16位地址类型来确定协议内容。

参数 socklen_t address_len, 结构体sockaddr的长度。
typedef unsigned int socklen_t

返回值: 如果绑定成功则返回0, 失败则返回-1.

代码示例

		int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            exit(1);
        }
        struct sockaddr_in Server;
        bzero(&Server, 0);
        Server.sin_family = AF_INET;
        Server.sin_addr.s_addr = inet_addr(_ip.c_str()); //?
        Server.sin_port = htons(_port); //?

        int n = bind( socket_fd, (const struct sockaddr *)&Server, sizeof(Server));
        if (n != 0)
        {
            exit(2);
        }

3. ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address,socklen_t *restrict address_len);

参数 int socket 就是使用socket接口函数所创建的那个socket文件描述符。
参数 void *restrict buffer为缓冲区,可以用来存放接收的字节数据。
参数 size_t length 为读取的字节长度。
参数 struct sockaddr *restrict address,输出型参数,用于获取发送方的socketaddr。
参数 socklen_t *restrict address_len,输出型参数,用于获取发送方的sockaddr的长度。

示例代码

		char inbuffer[1024];
        while (true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client); // unsigned int
            bzero(&client, 0);
            // 服务器接受数据
            ssize_t n = recvfrom(_socket_fd, inbuffer, sizeof inbuffer - 1, 0, (sockaddr *)&client, &len);
            if (n > 0)
            {
                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);
                userCheck(client_ip, client);
                inbuffer[n] = '\0';
                if(strcmp(inbuffer,"login\0") == 0) continue;

                std::string info = func(inbuffer, client_ip, client_port);
                senMessage(info);

            }
        }

4. ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr,socklen_t dest_len);

参数与 recvfrom类似。不同的是,这里的const struct sockaddr *dest_addr和socklen_t dest_len 变成了输入型参数。 因为你要发送消息,就需要知道对方的socket信息。

5. uint16_t htons(uint16_t hostshort);

这个函数的作用是什么呢? 这就涉及到网络字节序

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

规定网络数据在内存中必须采用大端存储方式!!

如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

所以这个函数的作用就是将hostshort转换为网络字节序。

6. in_addr_t inet_addr(const char *cp);

由于我们习惯将IP地址 写成这样的格式xxx.xxx.xxx.xxx(例如192.168.0.1),而在网络层面,我们肯定是采用四字节的int来存储IP,所以有这么一个函数将一个xxx.xxx.xxx.xxx的格式的IP转换为四字节int。

三、Server端

1.初始化Socket套接字

代码如下(示例):

		// 申请套接字
        int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            logMessage(FATAL, "Socket Applied Fault");
            exit(SOCKET_DENIED);
        }
        _socket_fd = socket_fd;
        logMessage(DEBUG, "Socket Applied Success");
        // bind套接字
        struct sockaddr_in Server;
        bzero(&Server, 0);
        Server.sin_family = AF_INET;
        Server.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr可以将ip形式x.x.x.x的字符串转化为大段存储的in_addr_t类型(typedef uint32_t in_addr_t;)
        Server.sin_port = htons(_port);

        int n = bind(_socket_fd, (const struct sockaddr *)&Server, sizeof(Server));
        if (n != 0)
        {
            logMessage(FATAL, "Bind Fault");
            exit(BIND_FAULT);
        }

2.发送和接收

代码如下(示例):


    void userCheck(std::string &client_ip, struct sockaddr_in &user)
    {
        auto it = _user_map.find(client_ip);
        // std::cout << "开始检测" << std::endl; 
        if (it == _user_map.end())
        {
            //没找到
            std::cout << "新用户[" << client_ip << "]上线..." << std::endl;
        }
        _user_map[client_ip] = user;
    }

    void senMessage(const std::string& info)
    {
        for(auto& user:_user_map )
        {
            sendto(_socket_fd, info.c_str(), info.size(), 0, (const struct sockaddr *)&(user.second), sizeof(user.second));
        }
    }
    
	std::string func(const char *res, const std::string& client_ip, const uint16_t client_port)
	{

    	char buffer [1024];
    	snprintf(buffer, sizeof buffer,"[%s:%d]# %s",client_ip.c_str(),client_port,res);
    	std::string mes = buffer;
    	std::cout << mes << std::endl; 
   	 	return mes;
	}
    
    void run(func_t func)
    {
        char inbuffer[1024];
        while (true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client); // unsigned int
            bzero(&client, 0);
            // 服务器接受数据
            ssize_t n = recvfrom(_socket_fd, inbuffer, sizeof inbuffer - 1, 0, (sockaddr *)&client, &len);
            if (n > 0)
            {
                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);

                userCheck(client_ip, client);
                inbuffer[n] = '\0';
                if(strcmp(inbuffer,"login\0") == 0) continue;

                std::string info = func(inbuffer, client_ip, client_port);
                senMessage(info);

            }
        }
    }

Server端全代码

                  //UdpServer.hpp  
#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <cstdlib>
#include <unistd.h>
#include "log.hpp"
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
const std::string default_ip = "0.0.0.0"; //?

typedef std::function<std::string(const char*, const std::string &, const uint16_t)> func_t;

enum
{
    SOCKET_DENIED = 1,
    BIND_FAULT
};

class UdpServer
{
public:
    UdpServer(const std::string &ip = default_ip, const uint16_t port = 8080)
        : _socket_fd(0), _ip(ip), _port(port)
    {
    }

    void init()
    {
        // 申请套接字
        int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            logMessage(FATAL, "Socket Applied Fault");
            exit(SOCKET_DENIED);
        }
        _socket_fd = socket_fd;
        logMessage(DEBUG, "Socket Applied Success");
        // bind套接字
        struct sockaddr_in Server;
        bzero(&Server, 0);
        Server.sin_family = AF_INET;
        Server.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr可以将ip形式x.x.x.x的字符串转化为大段存储的in_addr_t类型(typedef uint32_t in_addr_t;)
        Server.sin_port = htons(_port);

        int n = bind(_socket_fd, (const struct sockaddr *)&Server, sizeof(Server));
        if (n != 0)
        {
            logMessage(FATAL, "Bind Fault");
            exit(BIND_FAULT);
        }
    }

    void userCheck(std::string &client_ip, struct sockaddr_in &user)
    {
        auto it = _user_map.find(client_ip);
        // std::cout << "开始检测" << std::endl; 
        // std::cout << (*it).first << std::endl; 
        if (it == _user_map.end())
        {
            //没找到
            std::cout << "新用户[" << client_ip << "]上线..." << std::endl;
        }
        _user_map[client_ip] = user;
    }

    void senMessage(const std::string& info)
    {
        for(auto& user:_user_map )
        {
            sendto(_socket_fd, info.c_str(), info.size(), 0, (const struct sockaddr *)&(user.second), sizeof(user.second));
            // std::cout << "已发送数据给" << user.first << std::endl; 
        }
    }

    void run(func_t func)
    {
        char inbuffer[1024];
        while (true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client); // unsigned int
            bzero(&client, 0);
            // 服务器接受数据
            ssize_t n = recvfrom(_socket_fd, inbuffer, sizeof inbuffer - 1, 0, (sockaddr *)&client, &len);
            if (n > 0)
            {
                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);
                // std::cout <<  client_ip << " : " << client_port <<std::endl;
                userCheck(client_ip, client);
                inbuffer[n] = '\0';
                if(strcmp(inbuffer,"login\0") == 0) continue;
                // std::cout << "Server get message# " << inbuffer << std::endl;
                std::string info = func(inbuffer, client_ip, client_port);
                senMessage(info);
                // sendto(_socket_fd, info.c_str(), info.size(), 0, (const struct sockaddr *)&client, len);
                // sendto(_socket_fd, inbuffer, sizeof inbuffer, 0, (const struct sockaddr *)&client, len);
            }
        }
    }

    ~UdpServer()
    {
        if (_socket_fd == 0)
        {
            close(_socket_fd);
        }
    }

private:
    int _socket_fd;
    std::string _ip;
    uint16_t _port;
    std::unordered_map<std::string, struct sockaddr_in> _user_map;
};
                            //main.cc
#include "Udpserver.hpp"

std::string func(const char *res, const std::string& client_ip, const uint16_t client_port)
{
    //std::cout << "[" << client_ip << " : " << client_port << "] # " << res << std::endl;
    char buffer [1024];
    snprintf(buffer, sizeof buffer,"[%s:%d]# %s",client_ip.c_str(),client_port,res);
    std::string mes = buffer;
    std::cout << mes << std::endl; 
    return mes;
}

int main()
{
    UdpServer us;
    us.init();
    us.run(func);
    return 0;
}

const std::string default_ip = “0.0.0.0”;

服务器将ip绑定为0.0.0.0是什么意思呢?
指定ip为本机的任意ip. 尤其适用于本机有多个网卡的情况下,可根据目的地址的默认路由选择合适的网卡建立链路,收发数据。

在这里插入图片描述

Client端

#define terminal "/dev/pts" 
void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

enum
{
    SOCKET_DENIED = 1,
};

struct Thread_Data
{
    int socket_fd;
    struct sockaddr_in server;
};

void *recv_mes(void *args)
{
    Thread_Data* data = (Thread_Data*)args;
    char buffer[1024];
    bzero(buffer,0);
    while (true)
    {
        struct sockaddr_in tmp;
        socklen_t tmp_len;
        int n = recvfrom(data->socket_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&tmp, &tmp_len);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cerr <<  buffer << std::endl;
        }
        bzero(buffer,0);

    }
}

void *send_mes(void *args)
{
    Thread_Data* data = (Thread_Data*)args;
    std::string message = "login";
    sendto(data->socket_fd, message.c_str(), message.size(), 0, (const struct sockaddr *)&data->server, sizeof(data->server));
    while (true)
    {
        std::cout << "Please Enter Message@ ";
        std::getline(std::cin, message);
        sendto(data->socket_fd, message.c_str(), message.size(), 0, (const struct sockaddr *)&data->server, sizeof(data->server));
    }
}

int main(int args, char *argv[])
{
    if (args != 3)
    {
        Usage("./UdpClient");
    }
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd == -1)
    {
        logMessage(FATAL, "Socket Applied Fault");
        exit(SOCKET_DENIED);
    }
    struct sockaddr_in server;
    memset(&server, 0, sizeof server);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(argv[1]);
    server.sin_port = htons(std::atoi(argv[2]));
    pthread_t recv_thread, send_thread;
    Thread_Data data;
    data.server = server;
    data.socket_fd = socket_fd;
    pthread_create(&send_thread, nullptr, send_mes, (void *)&data);
    pthread_create(&recv_thread, nullptr, recv_mes, (void *)&data);

    pthread_join(recv_thread, nullptr);
    pthread_join(send_thread, nullptr);

    return 0;
}

采用了多线程的方式, 这里需要知道,因为socket本就支持同时可读可写,我们可以理解为它就是线程安全的。

Windows Client端

#include<iostream>
#include<string>
#include<WinSock2.h>
#include<Windows.h>
#include<thread>
#include<functional>
#pragma comment(lib, "ws2_32.lib") // 链接库文件
#pragma warning(disable:4996)      //防止VS发出4996号警告

const int server_port = 8080;
const std::string server_ip = ""; //提前写好服务器IP
                                  //warning : 多线程版本仍有一些小问题  
struct Thread_Data
{
    SOCKET socket_fd;
    struct sockaddr_in server;
};

void recv_mes(const Thread_Data& data)
{

    char buffer[1024];
    while (true)
    {
        memset(buffer, 0, sizeof(buffer));
        struct sockaddr_in tmp;
        int tmp_len = sizeof(tmp);
        int n = recvfrom(data.socket_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&tmp, &tmp_len);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
        }
        //std::cout << n << " errno:" << errno << std::endl;

    }
}

void send_mes(const Thread_Data& data)
{

    std::string message;
    std::cout << "Send A Message To Start A Chat ";
    while (true)
    {
        std::getline(std::cin, message);
        sendto(data.socket_fd, message.c_str(), (int)message.size(), 0, (const struct sockaddr*)&data.server, sizeof(data.server));
    }
}
int main()
{
    //初始化网络环境
	WSADATA wsd;    
	WSAStartup(MAKEWORD(2, 2), &wsd);
    system("chcp 65001");


    SOCKET socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd == SOCKET_ERROR)
    {
        perror("Socket Error");
        exit(1);
    }
    struct sockaddr_in server;
    memset(&server, 0, sizeof server);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(server_port);
    std::thread threads[2];
    Thread_Data data;
    data.server = server;
    data.socket_fd = socket_fd;
    sendto(socket_fd, "login", 5, 0, (const struct sockaddr*)&server, sizeof(server));
    threads[0] = std::thread(recv_mes, std::ref(data));
    threads[1] = std::thread(send_mes, std::ref(data));



    threads[0].join();
    threads[1].join();


    closesocket(socket_fd);
    WSACleanup();   //清理网络环境
	return 0;

}

这里的Windows端也是实现了一个多线程的版本,因为是不同的操作系统,在接口上是有一些差别的,不过大部分都差不多。

简易聊天室运行图

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构与算法】算法优化、时间复杂度、空间复杂度

文章目录 一、什么是复杂度&#xff1f;二、大O表示法三、时间复杂度计算四、常见复杂度的比较五、算法优化的核心方法论六、常见算法复杂度五、总结 一、什么是复杂度&#xff1f; 复杂度是衡量代码运行效率的重要的度量因素。 而复杂度主要就是指时间复杂度和空间复杂度。 …

三菱MR-J4系列伺服驱动器E7.1和32.3故障报警处理总结

三菱MR-J4系列伺服驱动器E7.1和32.3故障报警处理总结 三菱MR-J4系列伺服驱动器出现报警,故障代码为:E7.1和32.3,查阅手册可以看到E7.1和32.3的报警解释信息, 如下图所示,此时简单运动控制模块上的ERROR灯亮, 如下图所示,用GX WORKS3打开备份程序,找到FX5-80SSC-…

一维时间序列信号的广义傅里叶族变换(Matlab)

广义傅里叶族变换是一种时频变换方法&#xff0c;傅里叶变换、短时傅里叶变换、S变换和许多小波变换都是其特殊情况&#xff0c;完整代码及子函数如下&#xff0c;很容易读懂&#xff1a; % Run a demo by creating a signal, transforming it, and plotting the results% Cre…

大数据开发面试题【Mysql篇】

181、mysql数据库中的引擎 用于数据存储、处理和保护数据的核心服务&#xff0c;不同的数据库引擎有其各自的特点&#xff0c;常见的引擎&#xff1a;InnoDB&#xff0c;Mylsam、Memory、Mrg_Mylsam、Blackhole innodb&#xff1a;是一个事务性存储引擎&#xff0c;提供了对事…

Transformer 动画讲解:数据处理的四大关键步骤

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 汇总合集…

MFC工控项目实例之二添加iPlotx控件

承接专栏《MFC工控项目实例之一主菜单制作》 在WIN10下使用Visual C 6.0 &#xff08;完整绿色版&#xff09;添加iPlotx控件的方法。 1、在资源主对话框界面点击鼠标右键如图选择插入Active控件点击进入。 2、选择iPlotx Contrlolh点击确定。 3、在对话框界面插入iPlotx控件。…

NATS-研究学习

NATS-研究学习 文章目录 NATS-研究学习[toc]介绍说明提供的服务内容各模式介绍测试使用发布订阅&#xff08;Publish Subscribe&#xff09;请求响应&#xff08;Request Reply&#xff09;队列订阅&分享工作&#xff08;Queue Subscribers & Sharing Work&#xff09;…

编程入门(七)【虚拟机VMware安装Linux系统Ubuntu】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 &#x1f525;前言&#x1f680;Ubuntu知多少&#x1f680;安装的前期准备&am…

放开了去的 ulimit

放开了去的 ulimit 放开了去的 ulimitulimit简介临时修改打开文件数目永久修改系统总打开句柄限制更多信息 放开了去的 ulimit ulimit简介 对于高并发或者频繁读写文件的应用程序而言&#xff0c;有时可能需要修改系统能够打开的最多文件句柄数&#xff0c;否则就可能会出现t…

3389,为了保障3389端口的安全,我们可以采取的措施

3389端口&#xff0c;作为远程桌面协议&#xff08;RDP&#xff09;的默认端口&#xff0c;广泛应用于Windows操作系统中&#xff0c;以实现远程管理和控制功能。然而&#xff0c;正因为其广泛使用&#xff0c;3389端口也成为许多潜在安全威胁的入口。因此&#xff0c;确保3389…

使用C#实现VS窗体应用——画图板

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。&#x1f34e;个人主页&#xff1a;Meteors.的博客&#x1f49e;当前专栏&#xff1a;小项目✨特色专栏&#xff1a; 知识分享&#x1f96d…

ARM32开发——总线与时钟

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 APB总线时钟树时钟树 外部晶振内部晶振 在这个例子中&#xff0c;这条大街和巴士构成了一套系统&#xff0c;我们称之为AHB总线。 …

【数据结构】详解二叉树

文章目录 1.树的结构及概念1.1树的概念1.2树的相关结构概念1.3树的表示1.4树在实际中的应用 2.二叉树的结构及概念2.1二叉树的概念2.2特殊的二叉树2.2.1满二叉树2.2.2完全二叉树 2.3 二叉树的性质2.4二叉树的存储结构2.4.1顺序结构2.4.2链表结构 1.树的结构及概念 1.1树的概念…

乐观锁 or 悲观锁 你怎么选?

你有没有听过这样一句话&#xff1a;悲观者正确&#xff0c;乐观者成功​。那么今天我来分享下什么是乐观锁​和悲观锁。 乐观锁和悲观锁有什么区别&#xff0c;它们什么场景会用 乐观锁 乐观锁基于这样的假设&#xff1a;多个事务在同一时间对同一数据对象进行操作的可能性很…

三体中的冯诺依曼

你叫冯诺依曼&#xff0c;是一位科学家。你无法形容眼前的现态&#xff0c;你不知道下一次自己葬身火海会是多久&#xff0c;你也不知道会不会下一秒就会被冰封&#xff0c;你唯一知道的&#xff0c;就是自己那寥寥无几的科学知识&#xff0c;你可能会抱着他们终身&#xff0c;…

基于Android Studio记事本系统

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 具有登录&#xff0c;注册&#xff0c;记住密码&#xff0c;自动登录的功能&#xff1b; 可以新增记事本&#xff0c;编辑&#xff0c;删除记事本信息&#xff0c;同时可以设置主标题&#xff0c;内容&#xff0c;以及…

SpringBoot【1】集成 Druid

SpringBoot 集成 Druid 前言创建项目修改 pom.xml 文件添加配置文件开发 java 代码启动类 - DruidApplication配置文件-propertiesDruidConfigPropertyDruidMonitorProperty 配置文件-configDruidConfig 控制层DruidController 运行验证Druid 的监控应用程序 前言 JDK版本&…

【HarmonyOS - ArkTS - 状态管理】

概述 本文主要是从页面和应用级两个方面介绍了ArkTS中的状态管理的一些概念以及如何使用。可能本文比较粗略&#xff0c;细节化请前往官网(【状态管理】)学习&#xff0c;若有理解错误的地方&#xff0c;欢迎评论指正。 装饰器总览 由于下面会频繁提及到装饰器&#xff0c;所…

【CH32V305FBP6】调试入坑指南

1. 无法烧录程序 现象 MounRiver Studio WXH-LinkUtility 解决方法 前提&#xff1a;连接复位引脚 或者 2. 无法调试 main.c 与调试口冲突&#xff0c;注释后调试 // USART_Printf_Init(115200);

orin部署tensorrt、cuda、cudnn、pytorch、onnx

绝大部分参考https://blog.csdn.net/qq_41336087/article/details/129661850 非orin可以参考https://blog.csdn.net/JineD/article/details/131201121 报错显卡驱动安装535没法安装、原始是和l4t-cuda的部分文件冲突 Options marked [*] produce a lot of output - pipe it t…