【linux网络编程】| socket套接字 | 实现UDP协议聊天室

        前言:本节内容将带友友们实现一个UDP协议的聊天室。 主要原理是客户端发送数据给服务端。 服务端将数据再转发给所有链接服务端的客户端。 所以, 我们主要就是要实现客户端以及服务端的逻辑代码。 那么, 接下来开始我们的学习吧。

        ps:本节内容建议了解socket套接字的接口的友友们进行观看哦,本节内容中涉及到的接口都不会讲解, 直接就用了。

目录

 整体代码

Udpclient

UdpServer

main(配合UdpServer, UdpServer的入口) 

准备文件

实现步骤

实现服务端客户端的收发消息

Udpserver

Init函数

run函数 

 UdpServer析构

Udpclient

实现客户端之间的聊天功能

Udpserver

Udpclient

运行结果


 整体代码

        先上整体代码:

Udpclient


#include<iostream>
using namespace std;
#include<string>
#include<sys/types.h>
#include"Log.hpp"
#include<sys/socket.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<string.h>
#include<netinet/in.h>
Log lg;class ThreadData
{
public:sockaddr_in server;int sockfd;
};void* recv_message(void* args)
{char buffer[1024];ThreadData* td = static_cast<ThreadData*>(args);    while (true){//接收数据sockaddr_in temp;socklen_t len;string info;ssize_t s = recvfrom(td->sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr*)&temp, &len);if (s < 0){lg(Error, "recv error, error: %d, strerror: %s", errno, strerror(errno));continue;}buffer[s] = 0;info = buffer;cout << info << endl;}}void* send_message(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);string message;while (true){ getline(cin, message);  //获取数据//发送数据sendto(td->sockfd, message.c_str(), message.size(), 0, (sockaddr*)&td->server, sizeof(td->server));}
}int main(int argc, char* argv[])
{if (argc != 3){cout << "Client server" << endl;}//先拿到套接字的参数string serverip = argv[1];uint16_t serverport = stoi(argv[2]);ThreadData td;//创建套接字与打开网卡memset(&td.server, 0, sizeof(td.server));td.server.sin_family = AF_INET;td.server.sin_port = htons(serverport);td.server.sin_addr.s_addr = inet_addr(serverip.c_str());td.sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建文件描述符, 网卡的文件描述符, 网络传输就是使用网络文件描述符找到对应的文件内的数据if (td.sockfd < 0){lg(Error, "client create sockfd error, errno: %d, strerror: %s", errno, strerror(errno));exit(1);}//创建线程, 然后运行线程, 等待线程pthread_t recv, send;pthread_create(&recv, nullptr, recv_message, &td);pthread_create(&send, nullptr, send_message, &td);pthread_join(recv, nullptr);pthread_join(send, nullptr);close(td.sockfd);return 0;
}

UdpServer

#include<iostream>
using namespace std;
#include<sys/types.h>
#include<string>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<strings.h>
#include"Log.hpp"
#include<functional>
#include<netinet/in.h>
#include<unordered_map>int defaultport = 8080;
string defaultip = "0.0.0.0";using func_t = function<string(string, sockaddr_in&, unordered_map<string, sockaddr_in>&)>; 
Log lg;enum
{SockError = 2,BindError = 3,RecvError = 4,
};class UdpServer
{
public:UdpServer(uint16_t port = defaultport) : port_(port), ip_(defaultip), isrunning_(false){}void Init(){//先创建套接字变量并且完成初始化。 然后就创建网卡文件sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);local.sin_addr.s_addr = inet_addr(ip_.c_str());sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){lg(Fatal, "create sock error, errno: %d, strerror: %s", errno, strerror(errno));exit(SockError);}lg(Info, "create sock success");//绑定if (bind(sockfd_, (sockaddr*)&local, sizeof(local)) < 0) {lg(Fatal, "bind error, errno: %d, strerror: %s", errno, strerror(errno));exit(BindError);}lg(Info, "bind success");}void BroadCast(string message, unordered_map<string, sockaddr_in>& clients){cout << "1 "<< endl;for (auto& e : clients){sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)&e.second, sizeof(e.second));}cout << "2 " << endl;}void run(func_t func){isrunning_ = true;char inbuffer[1024];while (isrunning_){memset(inbuffer, 0, sizeof(inbuffer));sockaddr_in client;socklen_t client_len;memset(&client, 0, sizeof(client));//接收数据的同时监听到客户端的来源ssize_t s = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)&client, &client_len);if (s < 0) {lg(Waring, "recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));continue;}inbuffer[s] = 0;//处理数据//创建套接字, 用来监听是哪一个客户端string message = inbuffer;message = func(message, client, clients);//处理完成后, 返回发送给客户端BroadCast(message, clients);// sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)&client, sizeof(client));}}~UdpServer(){if (sockfd_ > 0) close(sockfd_);    }
private:int sockfd_;uint16_t port_;string ip_;bool isrunning_;unordered_map<string, sockaddr_in> clients;};

main(配合UdpServer, UdpServer的入口) 


#include"UdpServer.hpp"
#include<memory>string Handler(string message, sockaddr_in& client, unordered_map<string, sockaddr_in>& clients)
{string tmp = inet_ntoa(client.sin_addr) + to_string(client.sin_port);if (!clients.count(tmp)){clients[tmp] = client; cout << "ip " << inet_ntoa(client.sin_addr) << " : port " << client.sin_port << " has add in talk room" << endl;}message = "[" + string(inet_ntoa(client.sin_addr)) + ":" + to_string(client.sin_port) + "]#: " + message; return message;
}int main(int argc, char* argv[])
{if (argc != 2){cout << "has return" << endl;return 1;}//uint16_t serverport = stoi(argv[1]);  unique_ptr<UdpServer> svr(new UdpServer(serverport));//svr->Init();svr->run(Handler);return 0;
}

准备文件

        我们要准备三个文件

  •         Udpclient.cc——用来运行起来客户端
  •         UdpServer.hpp——用来实现服务端的各种接口
  •         main.cc——用来运行起来服务端

        除了这三个主要的文件。 其实博主还准备了两个可以忽略的文件(为了方便)。 一个是博主自己写的日志程序, 用来打印日志。 一个是makefile, 方便编译。 

        如果没有日志程序的话,打印错误信息时直接cout, printf打印即可。 makefile建议带上, 方便编译养成好习惯。

实现步骤

注意, 一步到位是很难的。 所以我们先实现简单的功能, 再实现困难的功能。

        这里简单的功能就是,先让客户端能够将数据发给服务端了, 然后服务端接收到消息后再将数据返回给客户端。

        这里困难的功能就是当多个客户端如何看到互相的信息。然后如何能够一遍发信息,一边收信息。 

实现服务端客户端的收发消息

Udpserver

        实现逻辑:Udpserver.hpp中封装一个类。这个类里面封装一些接口, 然后我们在main函数中创建类对象, 在执行接口操作。

        所以, 先封装一个类, 将要实现的接口以及要用到的变量写上, 实现一个框架:

#include<iostream>
using namespace std;
#include<string>int defaultport = 8080;       //默认的端口号,我们要创建一个默认的端口号
string defaultip = "0.0.0.0"; //在服务器中使用套接字的时候, bind函数不能绑定公网IP, 因为
//服务器的公网IP可能是虚拟的, 注意,IP地址是和网卡挂钩的, 一个网卡只能有一个IP地址。 
//绑定ip地址就是说在绑定网卡,也就是说绑定某个IP地址后就只能监听这一个网卡的消息了。 但是
//有些机器是有很多张网卡的, 所以就有一个默认IP:0.0.0.0, 绑定这个IP就能监听在本机器下面
//所有的网卡的信息。 Log lg;class UdpServer
{
public:UdpServer(uint16_t port = defaultport) : port_(port), ip_(defaultip), isrunning_(false){}void Init(){}void run(func_t func){}~UdpServer(){}
private:int sockfd_;     //服务端的网卡文件的编号uint16_t port_;  //服务器起来后的端口号string ip_;      //服务器起来的时候所在的ip地址bool isrunning_; //服务器是否正在运行};

        下面是main.cpp里面的内容, 直接启动服务端。 

#include"UdpServer.hpp"
#include<memory>int main(int argc, char* argv[])
{if (argc != 2){cout << "has return" << endl;return 1;}//uint16_t serverport = stoi(argv[1]);  unique_ptr<UdpServer> svr(new UdpServer(serverport));//svr->Init();svr->run();return 0;
}

Init函数

        Udpserver里面的Init函数, 这个函数用来绑定服务端的套接字的。 什么是绑定? 博主目前的理解就是将我们运行的服务端这个程序能够和网卡建立起关系。         

        这个关系中, 关系的两端是我们运行的服务端程序和socket网卡文件(网卡文件就代表了网卡)。关系的纽带是ip地址和端口号。 利用ip地址和端口号来将我们的服务端程序绑定给网卡, 这个时候因为网卡的工作性质, 其他的进程都不能再绑定网卡了, 直到我们的服务端退出。 

    void Init(){//先创建套接字变量并且完成初始化。 然后就创建网卡文件sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);local.sin_addr.s_addr = inet_addr(ip_.c_str());sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){lg(Fatal, "create sock error, errno: %d, strerror: %s", errno, strerror(errno));exit(SockError);}lg(Info, "create sock success");//绑定if (bind(sockfd_, (sockaddr*)&local, sizeof(local)) < 0) {lg(Fatal, "bind error, errno: %d, strerror: %s", errno, strerror(errno));exit(BindError);}lg(Info, "bind success");}

run函数 

        我们这里思考一个问题, 我们要实现的其实是服务端与客户端之间收发消息。 所以, 我们就要客户端先发, 然后服务端收消息。 

        然后! 服务端收到消息将消息 处理一下 再将消息发回客户端。

        所以,这个过程中服务端有三个主要的动作, 一个是收,一个是处理, 一个是发。         

        然后我们的处理怎么处理, 我们可以将处理动作暴露出去,  交给main.cpp来决定。 ——利用回调函数, main.cpp中将要执行的动作作为函数传给run函数。 

如下为接口:

    //这里的func_t是一个回调函数的类型。 什么类型, 使用包装器包装的!注意//包含头文件functionalusing func_t = function<string(string)>; void run(func_t func){isrunning_ = true;char inbuffer[1024];while (isrunning_){memset(inbuffer, 0, sizeof(inbuffer));sockaddr_in client;socklen_t client_len;memset(&client, 0, sizeof(client));//接收数据的同时监听到客户端的来源ssize_t s = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)&client, &client_len);if (s < 0) {lg(Waring, "recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));continue;}inbuffer[s] = 0;//处理数据//创建套接字, 用来监听是哪一个客户端string message = inbuffer;message = func(message);//这里的处理使用一个外部的接口//处理完成后, 返回发送给客户端sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)&client, sizeof(client));}}

 UdpServer析构

析构函数不解释

    ~UdpServer(){if (sockfd_ > 0) close(sockfd_);    }

Udpclient

        客户端就是给对应的服务端发送数据。 数据被服务端处理后接收即可:

#include<iostream>
#include<cstdlib>
#include<unistd.h>
using namespace std;
#include<sys/types.h>
#include<strings.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<cstring>
#include<string>//./udpclient serverip serverport 
int main(int argc, char* argv[])
{if (argc != 3){cout << "has return" << endl;return 1;}//string serverip = argv[1];uint16_t serverprot = stoi(argv[2]);//创建套接字struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverprot);server.sin_addr.s_addr = inet_addr(serverip.c_str());socklen_t serlen = sizeof(server);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){cout << "socker error" << endl; return 1;}string message;char buffer[1024];while(true){   cout << "please Enter@:" << endl;getline(cin, message);  //发送数据sendto(sockfd, message.c_str(), message.size(), 0,  (struct sockaddr*)&server, serlen);cout << "yes" << endl;//接收数据sockaddr_in temp;socklen_t socklen;ssize_t sz = recvfrom(sockfd, (void*)buffer, sizeof(buffer) - 1, 0, (sockaddr*)&temp, &socklen); //sockfd其实就是网卡的pidif(sz > 0){buffer[sz] = 0;cout << buffer << endl;}}close(sockfd);return 0;
}

实现客户端之间的聊天功能

Udpserver

        实现客户端之间的聊天可是说是在上面的代码中改两个地方。 一个是创建一个哈希表存储有多少客户端连接了服务端。 然后以后发消息就直接便利整个哈希表, 然后将数据发给每一个客户端。 如下:

#include<iostream>
using namespace std;
#include<sys/types.h>
#include<string>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<strings.h>
#include"Log.hpp"
#include<functional>
#include<netinet/in.h>
#include<unordered_map>int defaultport = 8080;
string defaultip = "0.0.0.0";//包装类要进行修改一下
using func_t = function<string(string, sockaddr_in&, unordered_map<string, sockaddr_in>&)>; Log lg;enum
{SockError = 2,BindError = 3,RecvError = 4,
};class UdpServer
{
public:UdpServer(uint16_t port = defaultport) : port_(port), ip_(defaultip), isrunning_(false){}//Init不变void Init(){//先创建套接字变量并且完成初始化。 然后就创建网卡文件sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);local.sin_addr.s_addr = inet_addr(ip_.c_str());sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){lg(Fatal, "create sock error, errno: %d, strerror: %s", errno, strerror(errno));exit(SockError);}lg(Info, "create sock success");//绑定if (bind(sockfd_, (sockaddr*)&local, sizeof(local)) < 0) {lg(Fatal, "bind error, errno: %d, strerror: %s", errno, strerror(errno));exit(BindError);}lg(Info, "bind success");}//遍历哈希表,将数据分发给所有的客户端void BroadCast(string message, unordered_map<string, sockaddr_in>& clients){cout << "1 "<< endl;for (auto& e : clients){sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)&e.second, sizeof(e.second));}cout << "2 " << endl;}void run(func_t func){isrunning_ = true;char inbuffer[1024];while (isrunning_){memset(inbuffer, 0, sizeof(inbuffer));sockaddr_in client;socklen_t client_len;memset(&client, 0, sizeof(client));//接收数据的同时监听到客户端的来源ssize_t s = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)&client, &client_len);if (s < 0) {lg(Waring, "recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));continue;}inbuffer[s] = 0;//处理数据//创建套接字, 用来监听是哪一个客户端string message = inbuffer;message = func(message, client, clients);//这里的处理使用一个外部的接口//处理完成后, 返回发送给客户端BroadCast(message, clients);}}~UdpServer(){if (sockfd_ > 0) close(sockfd_);    }
private:int sockfd_;uint16_t port_;string ip_;bool isrunning_;unordered_map<string, sockaddr_in> clients; //添加哈希表};

#include"UdpServer.hpp"
#include<memory>//main.cc主要修改就是Handler函数, 我们要通过client里面的ip地址和端口号作为key,client作为value,然后放入哈希表。 同时将message处理一下,方便我们观看结果。 
string Handler(string message, sockaddr_in& client, unordered_map<string, sockaddr_in>& clients)
{string tmp = inet_ntoa(client.sin_addr) + to_string(client.sin_port);if (!clients.count(tmp)){clients[tmp] = client; cout << "ip " << inet_ntoa(client.sin_addr) << " : port " << client.sin_port << " has add in talk room" << endl;}message = "[" + string(inet_ntoa(client.sin_addr)) + ":" + to_string(client.sin_port) + "]#: " + message; return message;
}// int main(int argc, char* argv[])
{if (argc != 2){cout << "has return" << endl;return 1;}//uint16_t serverport = stoi(argv[1]);  unique_ptr<UdpServer> svr(new UdpServer(serverport));//svr->Init();svr->run(Handler);return 0;
}

Udpclient

#include<iostream>
using namespace std;
#include<string>
#include<sys/types.h>
#include"Log.hpp"
#include<sys/socket.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<string.h>
#include<netinet/in.h>
Log lg;class ThreadData
{
public:sockaddr_in server;int sockfd;
};//数据接收函数
void* recv_message(void* args)
{char buffer[1024];ThreadData* td = static_cast<ThreadData*>(args);    while (true){//接收数据sockaddr_in temp;socklen_t len;string info;ssize_t s = recvfrom(td->sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr*)&temp, &len);if (s < 0){lg(Error, "recv error, error: %d, strerror: %s", errno, strerror(errno));continue;}buffer[s] = 0;info = buffer;cout << info << endl;}}//数据发送函数
void* send_message(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);string message;while (true){  getline(cin, message);  //获取数据//发送数据sendto(td->sockfd, message.c_str(), message.size(), 0, (sockaddr*)&td->server, sizeof(td->server));}
}int main(int argc, char* argv[])
{if (argc != 3){cout << "Client server" << endl;}//先拿到套接字的参数string serverip = argv[1];uint16_t serverport = stoi(argv[2]);ThreadData td;//创建套接字与打开网卡memset(&td.server, 0, sizeof(td.server));td.server.sin_family = AF_INET;td.server.sin_port = htons(serverport);td.server.sin_addr.s_addr = inet_addr(serverip.c_str());td.sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建文件描述符, 网卡的文件描述符, 网络传输就是使用网络文件描述符找到对应的文件内的数据if (td.sockfd < 0){lg(Error, "client create sockfd error, errno: %d, strerror: %s", errno, strerror(errno));exit(1);}//创建线程, 然后运行线程, 等待线程pthread_t recv, send;pthread_create(&recv, nullptr, recv_message, &td);pthread_create(&send, nullptr, send_message, &td);pthread_join(recv, nullptr);pthread_join(send, nullptr);close(td.sockfd);return 0;
}

运行结果

最后就是运行结果, 运行结果就是下图了, 我们已经能够成功的进行两个客户端之间的远程交流

 ——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!! 

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

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

相关文章

vivo 轩辕文件系统:AI 计算平台存储性能优化实践

在早期阶段&#xff0c;vivo AI 计算平台使用 GlusterFS 作为底层存储基座。随着数据规模的扩大和多种业务场景的接入&#xff0c;开始出现性能、维护等问题。为此&#xff0c;vivo 转而采用了自研的轩辕文件系统&#xff0c;该系统是基于 JuiceFS 开源版本开发的一款分布式文件…

Java | Leetcode Java题解之第520题检测大写字母

题目&#xff1a; 题解&#xff1a; class Solution {public boolean detectCapitalUse(String word) {// 若第 1 个字母为小写&#xff0c;则需额外判断第 2 个字母是否为小写if (word.length() > 2 && Character.isLowerCase(word.charAt(0)) && Charact…

如何封装一个可取消的 HTTP 请求?

前言 你可能会好奇什么样的场景会需要取消 HTTP 请求呢&#xff1f; 确实在实际的项目开发中&#xff0c;可能会很少有这样的需求&#xff0c;但是不代表没有&#xff0c;比如&#xff1a; 假如要实现上述这个公告栏&#xff0c;每点击一个 tab 按钮就会切换展示容器容器中…

图的最短路径算法-迪杰斯特拉(Dijkstra)算法与弗洛伊德(Frolyd)算法(更新中)

一、最短路径算法&#xff08;Shortest Path&#xff09; 最短路径问题是图论研究中的一个经典算法问题&#xff0c;旨在寻找图&#xff08;由结点和路径组成的&#xff09;中两结点之间的最短路径。 最短路径不一定是经过边最少的路径&#xff0c;但在这些最短路径中&#x…

ubuntu编译ffmpeg

配置 运行环境&#xff1a;vmware ubuntu 20.04 时间&#xff1a;2024年10月24日 权限问题&#xff1a;由于ubuntu权限问题 建议使用root权限编译&#xff0c;且~是根据用户组来进行定位的。 环境配置更新 cd ~ && \ mkdir ffmpeg_sources ffmpeg_build bin &…

EasyExcel自定义下拉注解的三种实现方式

文章目录 一、简介二、关键组件1、ExcelSelected注解2、ExcelDynamicSelect接口&#xff08;仅用于方式二&#xff09;3、ExcelSelectedResolve类4、SelectedSheetWriteHandler类 三、实际应用总结 一、简介 在使用EasyExcel设置下拉数据时&#xff0c;每次都要创建一个SheetWr…

【vs2022】windows可用的依赖预编译库

ffmpeg 、x264 、x265 等。obs是基于qt6+vs2022+64bit obs的官网传统构建已经不用了obs的s2022构建OBS Deps Build 2024-09-12FFmpeg4.4 库,x64 可用。

每天五分钟深度学习pytoroch:基于pytorch搭建逻辑回归算法模型

本文重点 前面我们学习了线性回归模型的搭建,无论是基于pytorch还是不基于pytorch,以上的模型都是回归模型,本文我们将使用pytorch搭建逻辑回归模型,逻辑回归模型是一个经典的分类问题。 模型搭建 class LogisticRegression(nn.Module) : def __init__(self) :super (Lo…

玩转Docker | 使用Docker部署推箱子网页小游戏

玩转Docker | 使用Docker部署推箱子网页小游戏 一、项目介绍项目简介项目预览 二、系统要求环境要求环境检查Docker版本检查检查操作系统版本 三、部署推箱子网页小游戏下载镜像创建容器检查容器状态检查服务端口安全设置 四、访问推箱子网页小游戏五、总结 一、项目介绍 项目…

Iperius Backup(数据备份软件) v8.3.0 中文免费版

下载&#xff1a; 【1】https://pan.quark.cn/s/19ef716c02d5 【2】https://drive.uc.cn/s/197acba8d8d94?public1 Iperius Backup是一款专业的备份还原软件&#xff0c;功能强大&#xff0c;支持DAT备份、LTO备份、NAS备份、磁带备份、RDX驱动器、USB备份&#xff0c;满足用…

K8S测试pod内存和CPU资源不足

只设置requests参数 mysql主从pod启动后监控 读压测之后 同时设置limits和requests&#xff0c;只调低内存值 监控 压力测试 同时设置limits和requests&#xff0c;只调低CPU值 初始状态 开始压测 结论 对于CPU&#xff0c;如果pod中服务使用CPU超过设置的limits&…

谷歌云GCP基础概念讲解

概览 云的基础是虚拟化&#xff1a;服务器&#xff0c;存储&#xff0c;网络。服务器是远程计算机的逻辑分区。存储是物理硬盘的逻辑划分。网络则是虚拟私有云。 谷歌是唯一一个拥有全球私有基础设施的公司&#xff1b;他们的谷歌云基础设施没有任何一部分通过公共互联网。换句…

Python 爬虫的寻宝大冒险:如何捕获 API 数据的宝藏

在这个信息爆炸的数字时代&#xff0c;数据就像是隐藏在网络深处的宝藏&#xff0c;等待着勇敢的探险家去发现。今天&#xff0c;我们要讲述的是如何成为一名 Python 爬虫探险家&#xff0c;装备你的代码工具&#xff0c;深入 API 的迷宫&#xff0c;捕获那些珍贵的数据宝藏。 …

C++,STL 051(24.10.28)

内容 1.map容器的构造函数。 2.map容器的赋值操作。 运行代码 #include <iostream> #include <map>using namespace std;void printMap(map<int, int> &m) {for (map<int, int>::iterator it m.begin(); it ! m.end(); it){cout << &quo…

《链表篇》---环形链表II(返回节点)

题目传送门 方法一&#xff1a;哈希表&#xff08;与环形链表类似&#xff09; 很容易就可以找到链表的相交位置。 public class Solution {public ListNode detectCycle(ListNode head) {if(head null || head.next null){return null;}Set<ListNode> visited new Ha…

从0开始深度学习(17)——数值稳定性和模型初始化

在每次训练之前&#xff0c;都会对模型的参数进行初始化&#xff0c;初始化方案的选择在神经网络学习中起着举足轻重的作用&#xff0c; 它对保持数值稳定性至关重要。 我们选择哪个函数以及如何初始化参数可以决定优化算法收敛的速度有多快。 糟糕选择可能会导致我们在训练时遇…

重学SpringBoot3-怎样优雅停机

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-怎样优雅停机 1. 什么是优雅停机&#xff1f;2. Spring Boot 3 优雅停机的配置3. Tomcat 和 Reactor Netty 的优雅停机机制3.1 Tomcat 优雅停机3.2 Reac…

【C++初阶】模版入门看这一篇就够了

文章目录 1. 泛型编程2. 函数模板2. 1 函数模板概念2. 2 函数模板格式2. 3 函数模板的原理2. 4 函数模板的实例化2. 5 模板参数的匹配原则2. 6 补充&#xff1a;使用调试功能观察函数调用 3. 类模板3 .1 类模板的定义格式3. 2 类模板的实例化 1. 泛型编程 在C语言中&#xff0…

动态规划-子序列问题——1027.最长等差数列

1.题目解析 题目来源&#xff1a;1027.最长递增子序列——力扣 测试用例 2.算法原理 1.状态表示 等差数列至少是三个数&#xff0c;所以一维数组显然无法满足状态表示&#xff0c;所以需要开辟一个二维数组来表示&#xff0c;二维数组的两个下标分别是倒数第一与倒数第二个数 …

【JSON相关漏洞(Hijacking+Injection)挖掘技巧及实战案例全汇总】

JSON相关漏洞&#xff08;HijackingInjection&#xff09;挖掘技巧及实战案例全汇总 本文一是在为测试过程中遇到json返回格式时提供测试思路&#xff0c;二是几乎所有国内的资料都混淆了json和jsonp的区别——这是两种技术&#xff1b;以及json和jsonp hijacking的区别——这…