【Linux网络编程】Socket-TCP实例

该代码利用socket套接字建立Tcp连接,包含服务器和客户端。当服务器和客户端启动时需要把端口号或ip地址以命令行参数的形式传入。服务器启动如果接受到客户端发来的请求连接,accept函数会返回一个打开的socket文件描述符,区别于监听连接的listensock,它用来为客户端提供服务的。因为有线程池的存在,可以立即使用已经创建好的线程来为客户端提供服务。线程池中存在一个数据结构专门用来存放客户端IP与端口信息,如果没有新的客户端连接服务器,那么该数据结构内容为空,那么多余的线程就会因为没有用户连接而阻塞,直到新用户的到来。以上就是对代码的大概介绍了。

下面是关于代码的六点细节解释:

1.查看网络连接 

  netstat  -nltp

2.可以用read函数读取TCP套接字的数据,而UDP不行。因为UDP是面向数据报,而TCP是面向数据流。所以代码使用了read与write函数进行收发消息,这也印证了网络并没有多么高大上,socket也是一个文件描述符。

3.客户端不需要手动bind,listen,accept,但是客户端需要自己connect服务器,connect会做两件事,bind和connect。

4.客户端的端口号要操作系统随机分配,防止客户端出现启动冲突。想想如果多个应用程序都想占用一个端口号进行网络通信的场景。

5.inet_aton函数

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

   

int inet_aton(const char *cp, struct in_addr *inp);//cp必须是点分十进制字符串

把“192.168.1.1”这样的点分十进制字符串转换成struct sockaddr_in结构体里面的in_addr结构体(网络序列)。已知in_addr结构体里面只有一个成员,一个32位无符号整数。

成功返回0,失败返回非0;
 

 使用方法:

6.使用signal(SIGCHLD, SIG_IGN)处理僵尸进程
通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN)。表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。
 

代码:

tcp_server.cc

#include "tcp_server.hpp"
#include<iostream>
#include<cstdlib>
#include <memory>
using namespace ns_server;static void Usage(string proc)
{cout << "Usage:\n\t" << proc << " port\n"<< endl;
}static string echo(string message)
{return message;
}// .tcp_server serverport
int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t serverport=atoi(argv[1]);unique_ptr<TcpServer> tsvr(new TcpServer(serverport,echo));tsvr->InitServer();tsvr->Start();return 0;
}

tcp_server.hpp

#pragma once
#include <iostream>
using namespace std;
#include <functional>
#include "err.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include<errno.h>
#include<pthread.h>
#include"ThreadPool.hpp"
#include "Task.hpp"
#include "Thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"namespace ns_server
{class TcpServer;class ThreadData{public:ThreadData(TcpServer* current,int sock,string client_ip,uint16_t client_port):_current(current),_sock(sock),_client_ip(client_ip),_client_port(client_port){}~ThreadData(){}TcpServer* _current;int _sock;string _client_ip;uint16_t _client_port;};static const uint32_t backlog = 32;static const uint16_t defaultport = 8888;using func_t = function<string(const string&)>;class TcpServer{public:TcpServer(uint16_t port ,func_t func): _port(port), _func(func), _quit(true){}void InitServer(){// 1.创建监听套接字_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){//cerr << "Socket create error!" << endl;logMessage(Fatal, "create socket error, code: %d, error string: %s",errno,strerror(errno));exit(SOCKET_ERR);}logMessage(Info, "create socket success, code: %d, error string: %s", errno, strerror(errno));// 2.绑定本地端口与IPstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = htons(INADDR_ANY); // 32位全零,不用转成网络序列,因为主机序列与网络序列一样;local.sin_port = htons(_port);int n1 = bind(_listensock, (const struct sockaddr *)&local, sizeof(local));if (n1 < 0){//cerr << "Bind socket error!" << endl;logMessage(Fatal, "bind socket error, code: %d, error string: %s",errno,strerror(errno));exit(BIND_ERR);}logMessage(Info, "bind socket success, code: %d, error string: %s",errno,strerror(errno));// 3.监听int n2 = listen(_listensock, backlog);if (n2 < 0){//cerr << "Listen socket error!" << endl;logMessage(Fatal, "listen socket error, code: %d, error string: %s", errno, strerror(errno));exit(LISTEN_ERR);}logMessage(Info, "listen socket success, code: %d, error string: %s", errno, strerror(errno));}void Start(){// signal(SIGCHLD,SIG_IGN);//不关心子进程的退出,由内核回收;_quit = false;while (!_quit){struct sockaddr_in client;socklen_t len = sizeof(client);// 4.不断获取新的客户端的连接,没有就阻塞;int sock = accept(_listensock, (struct sockaddr *)&client, &len);if (sock < 0){//cerr << "ACCEPT  error!" << endl;logMessage(Warning, "accept  error, code: %d, error string: %s", errno, strerror(errno));continue;} // 提取client信息---debug;string client_ip = inet_ntoa(client.sin_addr);//从网络序列转成主机序列,并将点分十进制字符串uint16_t client_port = ntohs(client.sin_port);// 网络序列转为主机序列//cout << client_ip << "-" << client_port<<"连接成功"<< endl; logMessage(Info, "accept  success,%d from %d,name:%s-%d",sock,_listensock,client_ip.c_str(),client_port);//线程池(主线程只负责接受客户端信息,并为其创建套接字进行沟通)//1.创建线程池//2.利用次线程处理沟通//线程池一定是有限个线程个数,一定是处理短任务Task t(sock,client_ip,client_port,bind(&TcpServer::service,this,placeholders::_1,placeholders::_2,placeholders::_3));ThreadPool<Task>::getinstance()->pushtask(t);// //多线程// pthread_t tid;// ThreadData* td=new ThreadData(this,sock,client_ip,client_port);// //因为threadRoutine函数只能有一个参数,想让线程执行service函数就必须把this指针,还有其它参数传过去,这时候可以利用一个结构体;// pthread_create(&tid,nullptr,threadRoutine,td);//    //多进程(父进程负责连接,子进程负责业务)//     pid_t id=fork();//     if(id<0)//     {//         //创建子进程失败;//         close(sock);//         cerr<<strerror(errno)<<endl;//         continue;//     }//     else if(id==0)//     {//         //子进程//         close(_listensock);//         // if(fork>0) exit(0);//创建一个孙子进程,儿子进程直接退出。利用孤儿进程处理业务,系统自动回收资源;//         service(sock,client_ip,client_port);//         exit(0);//子进程执行完服务直接退出;//     }//     //父进程//     close(sock);//子进程已经继承到sock文件描述符,关闭父进程的sock;//     // //回收子进程//     // int ret=waitpid(id,nullptr,0);//不获取退出码//     // if(ret==id) cout<<"回收子进程"<<id<<"成功!"<<endl;}}// static void *threadRoutine(void* args)// {//     //直接分离不用回收//     pthread_detach(pthread_self());//     ThreadData* td=static_cast<ThreadData*>(args);//     td->_current->service(td->_sock,td->_client_ip,td->_client_port); //     delete td;//     return nullptr;// }void service(int sock,string client_ip,uint16_t client_port){string name;name+=client_ip;name+="-";name+=to_string(client_port);char buffer[1024];while (true)//为某个客户端不间断服务{int n = read(sock, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n]=0;string res=_func(buffer);logMessage(Debug, "%s# %s", name.c_str(), res.c_str());write(sock,res.c_str(),res.size());}else if (n == 0){//说明对方断开连接了close(sock);//关闭文件描述符logMessage(Info, "%s quit,me too",name.c_str());break;}else{//cout << "read error:" <<strerror(errno)<< endl;logMessage(Error, "read error, %d:%s", errno, strerror(errno));break;}}}~TcpServer(){}private:uint16_t _port;int _listensock;bool _quit;func_t _func;};}

tcp_client.cc


#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include<cstdio>
#include<unistd.h>
//sockaddr_in结构体的头文件,当然也包含一些主机转网络序列的函数比如htons
#include <netinet/in.h>
#include <arpa/inet.h>#include "err.hpp"// static void* rfo(void *args)
// {
//     int sock=*(static_cast<int*>(args));
//     while(true)
//     {
//           //收
//         char buffer[4096];
//         struct sockaddr_in tmp;//输入型参数;
//         socklen_t len=sizeof(tmp);//要初始化,不然没法修改;//         //阻塞式接收
//         int n=recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&tmp,&len);//         if(n>0)//接收服务器数据成功
//         {
//             buffer[n]=0;
//             cout<<buffer<<endl;
//         }
//     }// }//当传入程序参数个数不对时,调用这个Usage函数告诉他什么是他妈的惊喜!
static void Usage(string proc)
{cout<<"Usage:\n\t"<<proc<<" serverip "<<" serverport\n"<<endl;
}// ./tcp_client serverip serverport
int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);exit(USAGE_ERR);}//保留输入的服务器的IP地址与端口号string serverip=argv[1];uint16_t serverport=atoi(argv[2]);//1.客户端创建套接字int sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){cerr << " create socket error " << strerror(errno) << endl;exit(SOCKET_ERR);}//明确server是谁struct sockaddr_in server;memset((void*)&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(serverport);//主机序列转网络序列//把字符串转成sockaddr_in结构体里的结构体inet_aton(serverip.c_str(),&(server.sin_addr));socklen_t len =sizeof(server);int cnt=5;cout<<"cnt=5"<<endl;//2.连接服务器while(connect(sock,(struct sockaddr*)&server,len)!=0){cout<<"正在重新连接中,还有"<<cnt<<"次重新连接机会"<<endl;if(cnt--<=0) break;}if(cnt<=0){cout<<"连接失败"<<endl;exit(CONNECT_ERR);}else{cout<<"连接成功"<<endl;}char buffer[1024];while(true){string message;cout<<"Enter>>>";getline(cin,message);write(sock,message.c_str(),message.size());//给服务器发数据ssize_t n=read(sock,buffer,sizeof(buffer)-1);//如果服务器没有发送数据,这里会阻塞;if(n>0){buffer[n]=0;cout<<"server echo>>>"<<buffer<<endl;//打印服务器传输来的数据;}else if(n==0){cout<<"与服务器断开了"<<endl;break;}else{cout<<"read error"<<strerror(errno)<<endl;break;}}//关闭文件描述符(虽然进程退出自动关闭)close(sock);return 0;
}

log.hpp

#pragma once#include <iostream>
#include <stdarg.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include<cstdio>
#include<time.h>
enum
{Debug = 0,Info,Warning,Error,Fatal,Uknown
};static std::string toLevelString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Uknown";}
}static std::string gettime()
{time_t curr = time(nullptr);struct tm *tmp = localtime(&curr);char buffer[128];snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,tmp->tm_hour, tmp->tm_min, tmp->tm_sec);return buffer;
}// 日志格式: 日志等级 时间 pid 消息体void logMessage(int level, const char *format, ...) // format是%d、%s这样的形式,也就是printf("%d %s",a,b);
{char logLeft[1024];std::string level_string = toLevelString(level);std::string curr_time = gettime();snprintf(logLeft, sizeof(logLeft), "[%s] [%s] [%d]", level_string.c_str(), curr_time.c_str(), getpid());char logRight[1024];// 可变参数va_list p;           // 指向可变参数的开始处,//va_arg(p, int);  // 根据类型提取参数,凭借%d这样的格式判定类型与大小。va_start(p, format); // p=const char*& formatvsnprintf(logRight, sizeof(logRight), format, p); // 向logRight缓冲区里面打印所有参数。va_end(p); // p=nullptrprintf("%s%s\n", logLeft, logRight);}

ThreadPool.hpp

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>using namespace std;#include "Task.hpp"
#include "Thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"
const int N = 6;template <class T>
class ThreadPool
{public://                                    单例模式static ThreadPool<T> *getinstance(){if (_instance == nullptr) // 当一个对象已经被创建以后,就不进入申请锁并且判断的环节了;{lockGuard lock(&_mutex_instance);if (_instance == nullptr){_instance = new ThreadPool<T>();logMessage(Debug, "线程池单例形成");_instance->init();_instance->start();}}return _instance;}bool isEmpty(){return _tasks.empty();}void init(){// 创建线程for (int i = 1; i <= _num; ++i)// pthread_create(&_threads[i],nullptr,ThreadRoutine,this);{_threads.push_back(Thread(i, ThreadRoutine, this));logMessage(Debug, "%d thread running", i);}}void start(){// 线程启动for (auto &e : _threads){e.run();}}void check(){for (auto &e : _threads){std::cout << "线程ID" << e.threadid() << " , " << e.threadname() << "is running··· " << std::endl;}}// 放入任务void pushtask(const T &task){lockGuard lock(&_mutex);_tasks.push(task);threadwakeup(); // 有新任务进来,唤醒线程去处理}// 拿出任务T poptask(){T t = _tasks.front();_tasks.pop();return t;}private:// 重点!!!static void *ThreadRoutine(void *args){pthread_detach(pthread_self());// 指针强转成线程池对象类型;ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);while (true){T t;{lockGuard lock(tp->getlock());// 1.判断是否有任务// 有->处理// 无->等待// 如果任务队列为空,则等待while (tp->isEmpty()){tp->threadwait();}t = tp->poptask(); // 从共有区域拿到线程独立栈上;}t(); // 调用task类里面的仿函数处理任务}}private:ThreadPool(int num = N): _num(num){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}ThreadPool(const ThreadPool<T> &tp) = delete;     // 删除默认拷贝构造void operator=(const ThreadPool<T> &tp) = delete; // 删除赋值函数pthread_mutex_t *getlock(){return &_mutex;}void threadwait(){// 挂起一个线程pthread_cond_wait(&_cond, &_mutex);}void threadwakeup(){// 唤醒一个线程pthread_cond_signal(&_cond);}~ThreadPool(){for (auto &e : _threads){e.join();}pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}private:std::vector<Thread> _threads;int _num; // 线程池里有几个线程;std::queue<T> _tasks; // 使用STL的自动扩容的特性pthread_mutex_t _mutex;pthread_cond_t _cond;static ThreadPool<T> *_instance;        // 懒汉方式实现单例模式static pthread_mutex_t _mutex_instance; // 单例对象有自己的锁
};// 静态变量初始化
template <class T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::_mutex_instance = PTHREAD_MUTEX_INITIALIZER;

Thread.hpp

#pragma once#include <iostream>
#include <pthread.h>
#include <string>class Thread
{
public:typedef void* ( *func_t) (void*);typedef enum{NEW=0,RUNNING,EXITED}ThreadStatus;public://状态:new,running,exitedint status(){return _status;}//线程名std::string threadname(){return _name;}//线程ID(共享库中的进程地址空间的虚拟地址)pthread_t threadid(){if(_status==RUNNING)//线程已经被创建,线程id已经输入到成员变量_tid中;return _tid;else {  return 0;}}public://构造函数;Thread(int num,func_t func,void* args):_tid(0),_status(NEW),_func(func),_args(args){char name[128];snprintf(name,sizeof(name),"thread-%d",num);_name=name;}//析构函数~Thread(){}//静态成员函数不能访问类内所有成员,因为没有this指针;static void* runHelper(void *args){Thread* td=(Thread*)args;(*td)();//调用仿函数执行线程的回调函数;return nullptr; }void operator()()//仿函数{//如果函数指针不为空,则执行该函数指针指向的回调函数;if(_func!=nullptr)  _func(_args);}//创建线程void run(){//因为runHelper函数必须只能有一个void*参数,所以runHelper函数在类内必须定义为static,这样才没有this指针;int n=pthread_create(&_tid,nullptr,runHelper,this);if(n!=0) return exit(1);//线程创建失败,那么直接退出进程;_status=RUNNING;}//等待线程结束void join(){int n=pthread_join(_tid,nullptr);if(n!=0) {std::cerr<<"main thread join thread "<<_name<<" error "<<std::endl;return;}_status=EXITED;//线程退出;}private:pthread_t _tid;//线程ID(原生线程库中为该线程所创建的TCB起始虚拟地址)std::string _name;//线程名func_t _func;//线程要执行的回调void* _args;//线程回调函数参数ThreadStatus _status;//枚举类型:状态
};

lockGuard.hpp

#pragma once#include <pthread.h>
#include <iostream>class Mutex//成员:加锁函数和解锁函数
{
public:Mutex(pthread_mutex_t* pmutex):_pmutex(pmutex)   {}void lock(){pthread_mutex_lock(_pmutex);}void unlock(){pthread_mutex_unlock(_pmutex);}~Mutex(){}private:pthread_mutex_t* _pmutex;//需要传入一个互斥量(锁)的指针;
};//对Mutex进行二次封装;
//创建该对象时自动加锁,析构时自动解锁;
class lockGuard
{   
public:lockGuard(pthread_mutex_t* pmutex):_mutex(pmutex)//利用锁的指针构建Mutex对象{_mutex.lock();}~lockGuard(){_mutex.unlock();}private:Mutex _mutex;//类内创建对象
};

Task.hpp

#pragma once
#include<string>
#include<iostream>
#include<functional>
using namespace std;using cb_t=function<void(int,string,uint16_t)>;class Task
{
public:Task(){}Task(int sock,string clientip,uint16_t clientport,cb_t func):_sock(sock),_clientip(clientip),_clientport(clientport),_func(func){}int operator()(){//开始为客户端---处理任务cout<<"开始为客户端"<<_clientip<<"-"<<_clientport<<"服务"<<endl;_func(_sock,_clientip,_clientport);//实际上是一个已经绑了一个参数的TcpServer::service函数;}~Task() {}private:int _sock;string _clientip;uint16_t _clientport;cb_t _func;
};

err.hpp

#pragma onceenum{   USAGE_ERR=1,SOCKET_ERR,BIND_ERR,LISTEN_ERR,CONNECT_ERR,SETSID_ERR};

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

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

相关文章

【校招VIP】前端网络之路由选择协议

考点介绍 当两台非直接连接的计算机需要经过几个网络通信时&#xff0c;通常就需要路由器。路由器提供一种方法来开辟通过一个网状联结的路径。在图R-9中标示了几条存在于洛杉矶和纽约办公室的路径。这种网状网络提供了冗余路径以调整通信负载或倒行链路&#xff0c;通常有一条…

灰狼算法优化ICEEMDAN参数,四种适应度函数任意切换,最小包络熵、样本熵、信息熵、排列熵...

今天给大家带来一期由灰狼算法优化ICEEMDAN参数的MATLAB代码。 优化ICEEMDAN参数的思想可以参考该文献&#xff1a; [1]陈爱午,王红卫.基于HBA-ICEEMDAN和HWPE的行星齿轮箱故障诊断[J].机电工程,2023,40(08):1157-1166. 文献原文提到&#xff1a;由于 ICEEMDAN 方法的分解效果取…

【数据结构】队列知识点总结--定义;基本操作;队列的顺序实现;链式存储;双端队列;循环队列

欢迎各位看官^_^ 目录 1.队列的定义 2.队列的基本操作 2.1初始化队列 2.2判断队列是否为空 2.3判断队列是否已满 2.4入队 2.5出队 2.6完整代码 3.队列的顺序实现 4.队列的链式存储 5.双端队列 6.循环队列 1.队列的定义 队列&#xff08;Queue&#xff09;是一种先…

Vue3记录

Vue3快速上手 1.Vue3简介 2020年9月18日&#xff0c;Vue.js发布3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;海贼王&#xff09;耗时2年多、2600次提交、30个RFC、600次PR、99位贡献者github上的tags地址&#xff1a;https://github.com/vuejs/vue-next/releas…

软件需求怎么写?

前言&#xff1a;一般来说&#xff0c;软件产品的需求人员的主要输出物就是软件需求&#xff0c;如果这个软件产品就XX系统&#xff0c;人们口中的“系统需求”和“软件需求”就没有什么区别了。在车企行业&#xff0c;推行这ASPICE体系&#xff0c;在这个体系中明确申请了系统…

DMNet复现(一)之数据准备篇:Density map guided object detection in aerial image

一、生成密度图 密度图标签生成 采用以下代码&#xff0c;生成训练集密度图gt&#xff1a; import cv2 import glob import h5py import scipy import pickle import numpy as np from PIL import Image from itertools import islice from tqdm import tqdm from matplotli…

哈希及哈希表的实现

目录 一、哈希的引入 二、概念 三、哈希冲突 四、哈希函数 常见的哈希函数 1、直接定址法 2、除留余数法 五、哈希冲突的解决 1、闭散列 2、开散列 一、哈希的引入 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在查找…

浅析三维模型3DTile格式轻量化处理常见问题与处理措施

浅析三维模型3DTile格式轻量化处理常见问题与处理措施 三维模型3DTile格式的轻量化处理是大规模三维地理空间数据可视化的关键环节&#xff0c;但在实际操作过程中&#xff0c;往往会遇到一些问题。下面我们来看一下这些常见的问题以及对应的处理措施。 变形过大&#xff1a;压…

Vue入门--vue的生命周期

一.什么是Vue 二.Vue的简介 官方网址 特点 三. 前后端的分离 重大问题 优势 4.Vue入门 定义一个管理边界 ​编辑 测试结果 vue的优势 ​编辑 测试结果 5.Vue的生命周期 vue的生命周期图 ​编辑建立一个html 测试结果 一.什么是Vue Vue是一种流行的JavaScript前端框…

【Graph Net学习】GNN/GCN代码实战

一、简介 GNN&#xff08;Graph Neural Network&#xff09;和GCN&#xff08;Graph Convolutional Network&#xff09;都是基于图结构的神经网络模型。本文目标就是打代码基础&#xff0c;未用PyG&#xff0c;来扒一扒Graph Net两个基础算法的原理。直接上代码。 二、代码 …

无涯教程-JavaScript - MDETERM函数

描述 MDETERM函数返回数组的矩阵行列式。 语法 MDETERM (array)争论 Argument描述Required/OptionalArrayA numeric array with an equal number of rows and columns.Required Notes 数组可以作为单元格范围给出,如A1:C3;作为数组常量,如{1,2,3; 4,5,6; 7,8,9}&#xff1…

【刷题】蓝桥杯

蓝桥杯2023年第十四届省赛真题-平方差 - C语言网 (dotcpp.com) 初步想法&#xff0c;x y2 − z2&#xff08;yz)(y-z) 即xa*b&#xff0c;ayz&#xff0c;by-z 2yab 即ab是2的倍数就好了。 即x存在两个因数之和为偶数就能满足条件。 但时间是&#xff08;r-l&#xff09;*x&am…

服务网格和微服务架构的关系:理解服务网格在微服务架构中的角色和作用

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

郑州大学图书馆许少辉《乡村振兴战略下传统村落文化旅游设计》中文文献——2023学生开学季辉少许

郑州大学图书馆许少辉《乡村振兴战略下传统村落文化旅游设计》中文文献——2023学生开学季辉少许

六、串口通信

六、串口通信 串口接口介绍使用串口向电脑发送数据电脑发送数据控制LED灯 串口接口介绍 SBUF是串口数据缓存器&#xff0c;物理上是两个独立的寄存器&#xff0c;但占用相同的地址。写操作时&#xff0c;写入的是发送寄存器&#xff1b;读操作时&#xff0c;读出的是接收寄存器…

【uniapp】Dcloud的uni手机号一键登录,具体实现及踩过的坑,调用uniCloud.getPhoneNumber(),uni.login()等

一键登录Dcloud官网请戳这里&#xff0c;感兴趣的可以看看官网&#xff0c;有很详细的示例&#xff0c;选择App一键登录&#xff0c;可以看到一些常用的概述 比如&#xff1a; 1、调用uni.login就能弹出一键登录的页面 2、一键登录的流程&#xff0c;可以选择先预登录uni.prelo…

数据库----数据查询

1.6 查询语句 语法&#xff1a;select [选项] 列名 [from 表名] [where 条件] [group by 分组] [order by 排序][having 条件] [limit 限制]1.6.1 字段表达式 mysql> select 锄禾日当午; ------------ | 锄禾日当午 | ------------ | 锄禾日当午 | ---…

C++---多态

多态 前言多态的概念多态的定义及实现多态的构成条件虚函数虚函数的重写虚函数重写的两个例外协变(基类与派生类虚函数返回值类型不同)析构函数的重写 override和final 虚函数的默认参数 抽象基类 前言 在买火车票的时候&#xff0c;如果你是学生&#xff0c;是买半价票&#…

微服务保护-Sentinel

初识Sentinel 雪崩问题及解决方案 雪崩问题 微服务中&#xff0c;服务间调用关系错综复杂&#xff0c;一个微服务往往依赖于多个其它微服务。 如图&#xff0c;如果服务提供者I发生了故障&#xff0c;当前的应用的部分业务因为依赖于服务I&#xff0c;因此也会被阻塞。此时&a…

基于SpringBoot的旅游系统

基于SpringBootVue的旅游系统、前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色&#xff1a;管理员、用户 用户&#xff1a;浏览旅游…