Linux第三十九章

2024-05-25 04:52
文章标签 linux 第三十九章

本文主要是介绍Linux第三十九章,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

popen

sz和rz

简单的聊天室

Comm.hpp

InetAddr.hpp

Lockguard.hpp

Log.hpp

nocopy.hpp

thread.hpp

ThreadPool.hpp

Udpserver.hpp

main.cc

UdpClient.cc

Makefile

运行结果


popen

在Linux中,popen是一个用于执行shell命令并建立一个管道连接的函数。它允许你在程序中执行一个shell命令,并通过标准输入或标准输出与命令进行交互。
popen函数的原型如下:

FILE *popen(const char *command, const char *mode);
其中,command参数是一个字符串,表示要执行的shell命令。mode参数是一个字符串,
指定管道连接的模式,可以是"r"(读模式)或"w"(写模式)。
popen函数会返回一个FILE类型的指针,可以像操作普通文件一样使用它来读取或写入数据。

以下是一个示例,展示如何使用popen函数执行一个shell命令并读取其输出:

#include <stdio.h>
int main() {FILE *fp;char output[1024];// 执行shell命令并读取输出fp = popen("ls -l", "r");if (fp == NULL) {printf("Failed to run command\n");return 1;}// 从管道中读取输出while (fgets(output, sizeof(output), fp) != NULL) {printf("%s", output);}// 关闭管道连接pclose(fp);return 0;
}
在上述示例中,我们使用popen函数执行了一个ls -l命令,并将其输出读取到缓冲区中,然后逐行打印出来。

需要注意的是,在使用popen时,要小心处理命令参数,以避免潜在的安全风险,例如通过用户输入直接构造命令参数可能导致命令注入漏洞。

sz和rz

sz 和 rz 是两个用于在 Linux 系统上进行文件传输的命令行工具。

* sz:用于在从远程主机传输文件到本地时使用。它的作用是将文件从远程主机发送到本地主机。通常情况下,它与 rz 配合使用,以实现从本地上传文件到远程主机的功能。

* rz:用于在从本地主机传输文件到远程主机时使用。它的作用是在本地选择文件,然后将其发送到远程主机。通常情况下,它与 sz 配合使用,以实现从本地上传文件到远程主机的功能。

这两个命令通常用于通过 SSH 连接到远程主机,并在命令行界面上传或下载文件。要使用这些命令,你需要在本地和远程主机上都安装了 lrzsz 软件包。在大多数 Linux 发行版中,这个软件包是默认安装的,但如果没有安装,你可以使用包管理器来安装它。

下载lrzsz 软件包

在 Ubuntu 和 Debian 等基于 Debian 的发行版中
sudo apt-get update
sudo apt-get install lrzsz

简单的聊天室

Comm.hpp

用于定义一些错误码

#pragma once
enum
{Usage_Err=1,Socket_Err,Bind_Err
};

InetAddr.hpp

用于将网络字节序的ip地址转为主机字节序、用于将网络字节序的端口号转为主机字节序

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>using namespace std;//用于将网络字节序的ip地址转为主机字节序
//用于将网络字节序的端口号转为主机字节序
class InetAddr
{
public:InetAddr(struct sockaddr_in &addr):_addr(addr){_port = ntohs(_addr.sin_port);   // 想看看客户端的端口号,ntohs(peer.sin_port),因为我们是从网络拿的数据,我需要将网络字节序转为主机序列_ip = inet_ntoa(_addr.sin_addr); // 想看看客户端的ip,将网络字节序的ip地址转为主机字节序}string Ip(){return _ip;}uint16_t Port(){return _port;}string PrintDebug(){string clientinfo = _ip + ":" + to_string(_port);return clientinfo;}const struct sockaddr_in& GetAddr(){return _addr;}bool operator==(InetAddr& addr){return this->_ip==addr.Ip()&&this->_port==addr.Port();}~InetAddr(){}private:string _ip;uint16_t _port;struct sockaddr_in _addr;
};

Lockguard.hpp

用于创建锁

#pragma once
#include <pthread.h>class Mutex
{
public:Mutex(pthread_mutex_t* lock):_lock(lock){}void Lock(){pthread_mutex_lock(_lock);}void Unlock(){pthread_mutex_unlock(_lock);}
private:pthread_mutex_t *_lock;
};class Lockguard
{public:Lockguard(pthread_mutex_t* lock):_mutex(lock){_mutex.Lock();}~Lockguard(){_mutex.Unlock();}private:Mutex _mutex;
};

Log.hpp

用于记录日志,可以选择将日志输出到显示器、一个文件、根据日志等级进行分类文件

#include <iostream>
#include <stdarg.h>
#include <string>
#include <ctime>
#include <unistd.h>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;// 定义一个枚举,表示日志级别
enum
{Debug = 0,Info,Warning,Error,Fatal
};// 定义一个枚举,表示输出方式
enum
{Screen = 10,OneFile,ClassFile
};// 定义一个函数,将日志级别转换为字符串
string Leveltostring(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 "Unknown";}
}const int default_style = Screen;       // 默认想显示器打印
const string default_filename = "log."; // 默认的文件名是log.
const string logdir="log";
// 定义一个日志类
class Log
{
public:Log() : style(default_style), filename(default_filename){mkdir(logdir.c_str(), 0777);// 创建一个目录}// 定义一个函数,将时间戳转换为字符串string Timelocaltime(){time_t curtime = time(nullptr);struct tm *curr = localtime(&curtime);char time_buffer[128];snprintf(time_buffer, sizeof(time_buffer), "%d年-%d月-%d日 %d时:%d分:%d秒",curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday, curr->tm_hour, curr->tm_min, curr->tm_sec);return time_buffer;}// 定义一个函数,设置一个style,向哪里输出,默认是向屏幕输出void SetStyle(int style) // 设置一个style,向哪里输出,默认是向屏幕输出{this->style = style;}// 定义一个函数,设置一个文件名void SetFilename(const string &filename){this->filename = filename;}// 定义一个函数,将日志写入文件void WriteLogToOneFile(const string &logname, const string &message){int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if (fd < 0){exit(-1);}write(fd, message.c_str(), message.size());close(fd);}// 定义一个函数,将日志写入文件void WriteLogToClassFile(const string &levelstr, const string &message) // 将日志写入文件{string logname = logdir;logname += "/";logname += filename;logname += levelstr;WriteLogToOneFile(logname, message);}//void WriteLog(const string &levelstr, const string &message){switch (style){case Screen:cout << message;break;case OneFile:WriteLogToClassFile("all", message);break;case ClassFile:WriteLogToClassFile(levelstr, message);break;default:break;}}void LogMessage(int level, const char *format, ...){char rightbuffer[1024]; // 日志的内容va_list args;           // va_list其实就是char* 类型的va_start(args, format); // 获取可变参数的位置,由args去指向可变参数部分vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);va_end(args); // 相等于args=nullptrchar leftbuffer[1024];string levelstr = Leveltostring(level);string curtime = Timelocaltime();string idstr = to_string(getpid());snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s]", levelstr.c_str(), curtime.c_str(), idstr.c_str());string loginfo = leftbuffer;loginfo.append(rightbuffer);WriteLog(levelstr, loginfo);}~Log(){}private:int style;string filename;
};

nocopy.hpp

主要是用来设计一个不能继承的类

#pragma once#include<iostream>class nocopy
{public:nocopy(){}nocopy(const nocopy&)=delete;const nocopy& operator=(const nocopy&)=delete;~nocopy(){}
};

thread.hpp

用于创建线程

#pragma once
#include<iostream>
#include<string>
#include<functional>
#include<pthread.h>using namespace std;//typedef function<void()> func_t
template<class T>
using func_t=function<void(T&)>;template<class T>
class Thread
{
public:Thread(const string& threadname,func_t<T> func,T& data):_tid(0),_threadname(threadname),_isrunning(false),_func(func),_data(data){}static void* Threadroutine(void* args)//类内成员方法,其第一个参数是this指针,所以会导致编译错误//这里使用static,让Thraedroutine成为类的方法,{(void)args;//仅仅是为了消除警告,变量未使用Thread* ts=static_cast<Thread*>(args);ts->_func(ts->_data);return nullptr;}bool Start(){int n=pthread_create(&_tid,nullptr,Threadroutine,this);//把当前对象传递给线程执行的方法if(n==0){_isrunning=true;return true;}else return false;}bool Join(){if(!_isrunning)return true;int n=pthread_join(_tid,nullptr);if(n==0){_isrunning=false;return true;}return false;}bool Isrunning(){return _isrunning;}string Threadname(){return _threadname;}
private:pthread_t _tid;string _threadname;bool _isrunning;func_t<T> _func;T _data;
};

ThreadPool.hpp

用于创建线程池

#pragma once#include <iostream>
#include <queue>
#include "Log.hpp"
#include "thread.hpp"
#include "Lockguard.hpp"
#include <functional>
#include<unistd.h>
using namespace std;
using namespace std::placeholders;
static int defaultnum=5;Log lg;//全局的日志对象,用于记录线程池的日志信息//给线程执行的方法传递的参数
class ThreadData
{public:ThreadData(const string& threadname):_threadname(threadname){}string _threadname;
};template <class T>
class ThreadPool
{
public:static ThreadPool<T>* Getinstance(){{Lockguard lockguard(&_mutex_q);if(instance==nullptr){lg.LogMessage(Info,"单例创建成功...\n");instance=new ThreadPool<T>();}}return instance;}
private:ThreadPool(int thread_num=defaultnum): _thread_num(thread_num){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_cond,nullptr);//构建线程for(int i=0;i<_thread_num;++i){string threadname="thread -";threadname+=to_string(i+1);ThreadData td(threadname);//这里使用了bind绑定成员函数Thread<ThreadData> t(threadname, bind(&ThreadPool<T>::ThreadRun,this,_1),td);lg.LogMessage(Info,"%s is created ...\n",threadname.c_str());_threads.push_back(t);}}
public://启动线程池,让线程执行自己的方法thread_routinebool Start(){//启动for(auto& thread:_threads){thread.Start();lg.LogMessage(Info,"%s is running...\n",thread.Threadname().c_str());}return true;}//封装了pthread_cond_waitvoid ThreadWait(const ThreadData &td){lg.LogMessage(Debug,"no task,%s is sleeping...\n",td._threadname.c_str());pthread_cond_wait(&_cond,&_mutex);}//封装了pthread_cond_signalvoid ThreadWakeup(){lg.LogMessage(Debug,"have task\n");pthread_cond_signal(&_cond);}//线程执行的任务void ThreadRun(ThreadData& td){while(true){T t;{//这个花括号,为了设置lockguard的生命周期的,这样才可以调用析构函数进行解锁Lockguard lockguard(&_mutex);//方法1)pthread_mutex_lock(&_mutex);//加锁while(_q.empty())//如果任务队列是空的,就不用去拿任务了{ThreadWait(td);//pthread_cond_wait(&_cond,&mutex);//让线程阻塞,因为没有任务,然后解锁,让其他线程申请到锁,如果队列还是为空的话,仍然会阻塞....}t=_q.front();//取任务_q.pop();//1)pthread_mutex_unlock(&_mutex);}//执行任务t();// lg.LogMessage(Debug,"%s handler %s done , result is :%s\n",\// td._threadname.c_str(),t.Printtask().c_str(),t.Printresult().c_str());//cout<<"handler done"<<t.Printresult()<<endl;}}//线程池中插入任务void Push( T& in){Lockguard lockguard(&_mutex);_q.push(in);ThreadWakeup();//每次插入任务后,唤醒一个线程}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}//for debug//主线程等待线程void Wait(){for(auto& thread: _threads){thread.Join();}}
private:queue<T> _q;//队列,用于存储线程池中线程要执行的任务vector<Thread<ThreadData>> _threads;//线程池其实是一个顺序表类型,里面存储的多线程int _thread_num;//创建线程的数量pthread_mutex_t _mutex; // 锁pthread_cond_t _cond; // 条件变量//懒汉单例static pthread_mutex_t _mutex_q;//单例锁static ThreadPool* instance;};template<class T>
ThreadPool<T>* ThreadPool<T>::instance=nullptr;
template<class T>
pthread_mutex_t  ThreadPool<T>::_mutex_q=PTHREAD_MUTEX_INITIALIZER;

Udpserver.hpp

服务端

#pragma once
#include "nocopy.hpp"
// #include "Log.hpp"
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include "Comm.hpp"
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <functional>
#include "ThreadPool.hpp"
#include <vector>
#include <pthread.h>#include "InetAddr.hpp"
using namespace std;static const string defaultip = "0.0.0.0";
static const uint16_t defaultport = 8888;
static const int defaultfd = -1;
static const int defaultsize = 1024;using task_t = function<void()>;class UdpServer : public nocopy
{
public:UdpServer(uint16_t port = defaultport, string ip = defaultip): _ip(ip), _port(port), _sockfd(defaultfd){pthread_mutex_init(&_user_mutex, nullptr);}void Init(){// 创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){lg.LogMessage(Fatal, "socket error,%d :%s\n", errno, strerror(errno));exit(Socket_Err);}lg.LogMessage(Info, "socket success ,sockfd:%d \n", _sockfd);// 2.绑定,指定网络信息struct sockaddr_in local;      // 创建套接字地址结构体对象bzero(&local, sizeof(local));  // 初始化locallocal.sin_family = AF_INET;    // 指定协议族local.sin_port = htons(_port); // 指定端口号(htons的功能就是将我们创建的端口号转成网络子节序)// local.sin_addr.s_addr = inet_addr(_ip.c_str());                    // 指定ip,需要传递整形的ip(inet_addr就是将字符串ip地址转为整形,同时也转为网络子节序)local.sin_addr.s_addr = INADDR_ANY;                                // 指定ip,INADDR_ANY表示本机的任意一个ip地址int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); // 将套接字地址结构体绑定到套接字if (n != 0){lg.LogMessage(Fatal, "bind err ,%d:%s", errno, strerror(errno));exit(Bind_Err);}ThreadPool<task_t>::Getinstance()->Start(); // 启动线程池}void AddOnlineUser(InetAddr addr) // 将addr插入到_online_user中{{Lockguard lockguard(&_user_mutex);// cout << "添加用户" << endl;for (size_t i = 0; i < _online_user.size(); i++){if (addr == _online_user[i]){// cout << "用户已存在" << endl;return;}}// cout << "添加用户成功" << endl;_online_user.push_back(addr);lg.LogMessage(Debug, "add user to onlinelist success, %s:%d\n", addr.Ip().c_str(), addr.Port());}}// 服务器路由void Route(int sock, const string &message){{Lockguard lockguard(&_user_mutex);for (auto &user : _online_user){// cout << "发送给client" << endl;sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&user.GetAddr(), sizeof(user.GetAddr()));lg.LogMessage(Debug, "send message to client success, %s:%d\n", user.Ip().c_str(), user.Port());}}}void Start(){// 服务器永远是在循环运行char buffer[defaultsize]; // 创建一个缓冲区for (;;){struct sockaddr_in peer;                                                                      // 创建一个套接字地址空间,用于存储客户端的套接字地址socklen_t len = sizeof(peer);                                                                 // 获取套接字的地址空间大小ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len); // 用于接收来自客户端的数据,并将其存储在buffer中// cout << "判断服务器收到消息没" << buffer << endl;if (n > 0){InetAddr addr(peer);AddOnlineUser(addr);buffer[n] = 0;string message = "[";message += addr.Ip() + ":" + to_string(addr.Port()) + "]" + "#";message += buffer;task_t task = std::bind(&UdpServer::Route, this, _sockfd, message);ThreadPool<task_t>::Getinstance()->Push(task);// 处理信息// string response=_OnMessage(buffer);// sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&peer, len); // 向指定的套接字peer进行接收数据}}}~UdpServer(){pthread_mutex_destroy(&_user_mutex);}private:string _ip;uint16_t _port;int _sockfd;vector<InetAddr> _online_user; // 会被多线程同时访问pthread_mutex_t _user_mutex;   // 锁
};

main.cc

服务器端

#include"Udpserver.hpp"
#include"Comm.hpp"
#include<memory>
#include<stdio.h>using namespace std;string OnMessageDefault(string request)
{return request+"[haha, got you!!]";
}string ExecuteCommand(string command)//我们的处理客户端发来的消息,不一定直接返回字符串,我们还可以让客户端输入shell命令,然后执行
{cout<<"get a message :"<<command<<endl;FILE* fp=popen(command.c_str(),"r");if(fp==nullptr){return "execute error, reason is uknown";}string response;char buffer[1024]={0};while(true){char* s=fgets(buffer,sizeof(buffer),fp);if(!s) break;//如果读取为空就返回else response+=buffer;}pclose(fp);return response;
}int main(int argc,char* argv[])
{if(argc!=2){cout<<"Usage:\n     ./udp_echo_server <port>"<<endl;return -1;}// string ip=argv[1];uint16_t port=stoi(argv[1]);//unique_ptr<UdpServer> usvr=make_unique<UdpServer>();???//UdpServer* usvr=new UdpServer(OnMessageDefault,port);//UdpServer* usvr=new UdpServer(ExecuteCommand,port);UdpServer* usvr=new UdpServer(port);usvr->Init();usvr->Start();return 0;
}

UdpClient.cc

客户端

#include "Log.hpp"
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include "Comm.hpp"
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <cerrno>
#include"thread.hpp"
#include"InetAddr.hpp"using namespace std;class ThreadData
{public:ThreadData(int sock,struct sockaddr_in& server):_sockfd(sock),_serveraddr(server){}~ThreadData(){}public:int _sockfd;InetAddr _serveraddr;
};void RecvRoutine(ThreadData& td)
{char buffer[4096];while(true){//收消息struct sockaddr_in temp;socklen_t len = sizeof(temp);size_t n = recvfrom(td._sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &len);if (n > 0){buffer[n] = 0;cerr<<buffer << endl;}elsebreak;}
};void SendRoutine(ThreadData& td)
{while(true){// 我们要发的数据string inbuffer;cout << "Please Enter#:";getline(cin, inbuffer);//cout << inbuffer << endl;// 我们要发给谁?serverauto server=td._serveraddr.GetAddr();int n = sendto(td._sockfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr *)&server, sizeof(server));if(n<=0)cout<<"send error"<<endl;}
};int main(int argc, char *argv[])
{if (argc != 3){cout << "Usage:\n     ./udp_echo_client <ip> <port>" << endl;return -1;}string serverip = argv[1];           // 服务器ipuint16_t serverport = atoi(argv[2]); // 服务器端口号// 创建套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){cerr << "socket error" << strerror(errno) << endl;return 1;}cout << "client create socket success:" << sockfd << endl;// 2.客户端也需要绑定套接字空间,但是,不需要显示的绑定,client会在首次发送数据的时候自动绑定// 服务器的端口号,一定众所周知的,不可以随意改变,client需要port,客户端需要绑定随机端口// 因为客户端非常多,所以客户端需要绑定随机端口// 2.1填充一下server的信息struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());ThreadData td(sockfd,server);Thread<ThreadData>recver("recver",RecvRoutine,td);Thread<ThreadData>sender("sender",SendRoutine,td);recver.Start();sender.Start();recver.Join();//主线程等待子线程sender.Join();close(sockfd);return 0;
}

Makefile

工程管理,形成客户端和服务端

.PHONY:all
all:udp_server udp_clientudp_server:main.ccg++ -o $@ $^ -std=c++11 -lpthreadudp_client:UdpClient.ccg++ -o $@ $^ -std=c++11 -lpthread.PHONY:clean
clean:rm -f udp_server udp_client

运行结果

首先编译形成客户端和服务端

make

运行服务端

bch@hcss-ecs-6176:~/linux/4_3/UDPsever/udp_echo_server_chat.4.02$ ./udp_server 8888
[Info][2024年-5月-24日 15时:28分:36秒][93230]socket success ,sockfd:3 
[Info][2024年-5月-24日 15时:28分:36秒][93230]单例创建成功...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -1 is created ...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -2 is created ...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -3 is created ...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -4 is created ...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -5 is created ...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -1 is running...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -2 is running...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -3 is running...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -4 is running...
[Info][2024年-5月-24日 15时:28分:36秒][93230]thread -5 is running...
[Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -3 is sleeping...
[Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -2 is sleeping...
[Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -1 is sleeping...
[Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -5 is sleeping...
[Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -4 is sleeping...

启动客户端

  🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

这篇关于Linux第三十九章的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1000536

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo