Socket编程UDP

Socket--UDP

我们先认识udp接口,做一个小实验,实现udp通信

1. version1-udp通信

代码链接:gitee

main.cc

C++ 复制代码
#include"udpserver.hpp"
#include"log.hpp"
#include<memory>
void usage(std::string str)
{
    std::cout<<"usage: "<<str<<" server_port"<<std::endl;
}
int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        usage(argv[0]);
        exit(USAGE_ERROR);
    }
    EnableScrean();//在屏幕上打印日志
    uint16_t server_port = std::stoi(argv[1]);
    std::unique_ptr<udpserver> server_ptr = std::make_unique<udpserver>(server_port);
    server_ptr->init_server();
    server_ptr->start();
    return 0;
}

udpserver.hpp

c++ 复制代码
#pragma once
#include <iostream>
#include <string>
#include<sys/types.h>
#include<sys/socket.h>
#include<cstring>
#include<arpa/inet.h>
#include<netinet/in.h>
#include"log.hpp"
#include"InetAddr.hpp"
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};
const static int defaultsockfd = -1;
class udpserver
{
public:
    udpserver(uint16_t port)
    :_port(port)
    ,_sockfd(defaultsockfd)
    ,_isrunning(false)
    {
    }
    ~udpserver()
    {
    }
    void init_server()
    {
        //1. 创建socket 套接字
        //sockfd 唯一表示套接字
        //AF_INET:address family internetd, 创建 IPv4 套接字,AF_INET6 用于 IPv6 地址族
        //SOCK_DGRAM:这是一个宏,指定了套接字的类型,用与udp通信
        _sockfd = socket(AF_INET,SOCK_DGRAM,0);
        if(_sockfd < 0)
        {
            LOG(FATAL,"socket error ,%s,%d\n",strerror(errno),errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO,"socket success,socket is : %d \n",_sockfd);
        //2. 填充sockaddr_in 结构
        struct sockaddr_in local;// struct sockaddr_in 系统提供的数据类型用于IPv4。local是变量,用户栈上开辟空间。
        bzero(&local,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);//port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节
        local.sin_addr.s_addr = INADDR_ANY;//任意ip绑定

        //3. bind sockfd 和 网络信息(ip + port)
        //addr 需要是一个指向 sockaddr 结构体的指针,在使用的时候需要强制转化成sockaddr_in
        //这样的好处是程序的通用性, 可以接收IPv4, IPv6
        int n = bind(_sockfd,(struct sockaddr * )&local,sizeof(local));
        if(n < 0)
        {
            LOG(FATAL,"bind error,%s, %d\n",strerror(errno),errno);
            exit(BIND_ERROR);
        }
        LOG(INFO,"sockfd bind success\n");
    }
    void start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
            if(n > 0)
            {
                buffer[n] = 0;
                InetAddr addr(peer);
                LOG(DEBUG,"get message from [%s %d]: %s\n",addr.Ip().c_str(),addr.Port(),buffer);
                sendto(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,len);
            } 
        }
        _isrunning = false;
    }

private:
    int _sockfd;     // 文件描述符
    uint16_t _port;  // 服务器端口号
    bool _isrunning; // 服务器是否在运行
};

udpclient.cc

C++ 复制代码
#include <iostream>
#include "udpserver.hpp"
void usage(std::string str)
{
    std::cout << "usage: " << str << "  server_ip server_port" << std::endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        exit(USAGE_ERROR);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        LOG(FATAL, "socket error ,%s,%d\n", strerror(errno), errno);
        exit(SOCKET_ERROR);
    }
    struct sockaddr_in local; // struct sockaddr_in 系统提供的数据类型用于IPv4。local是变量,用户栈上开辟空间。
    bzero(&local, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(server_port); // port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
    // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节
    local.sin_addr.s_addr = inet_addr(server_ip.c_str());
    std::string message;
    while(true)
    {
        std::cout<<"please enter#";
        std::getline(std::cin,message);
        sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr* )&local,sizeof(local));
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];
        ssize_t n = recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,&len);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout<<"server echo#"<<buffer<<std::endl;
        }
    }
    return 0;
}

InetAddr.hpp

C++ 复制代码
#pragma once
#include <iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
class InetAddr
{
public:
    //有参构造,sockaddr_in 构造
    InetAddr(const sockaddr_in& addr)
    :_addr(addr)
    {
        get_address(&_ip,&_port);
    }
    //有参构造,ip 和 port 构造
    InetAddr(const std::string& ip,uint16_t port)
    :_ip(ip)
    ,_port(port)
    {
        _addr.sin_family = AF_INET;
        _addr.sin_port = htons(_port);
        _addr.sin_addr.s_addr = inet_addr(_ip.c_str());
    }
    //默认构造
    InetAddr()
    {}
    //返回ip
    std::string Ip()
    {
        return _ip;
    }
    //返回:端口号
    uint16_t Port()
    {
        return _port;
    }
    //判断相等
    bool operator==(const InetAddr& addr) 
    {
        if(_ip == addr._ip&&_port == addr._port)
        {
            return true;
        }
        else
        return false;
    }
    //返回sockaddr_in
    struct sockaddr_in addr()
    {
        return _addr;
    }
    //析构函数
    ~InetAddr()
    {}
private:
    //从_addr 中获取ip 和 port 信息
    void get_address(std::string* ip,uint16_t* port)
    {
        *ip = inet_ntoa(_addr.sin_addr);
        *port = ntohs(_addr.sin_port);
    }
    struct sockaddr_in _addr;
    std::string _ip;
    uint16_t _port;
};

实验结果

2. version2-udp 字典

代码链接:gitee

main.cc

C++ 复制代码
#include"udpserver.hpp"
#include"log.hpp"
#include"dict.hpp"
#include<memory>
using namespace dict_ns;
void usage(std::string str)
{
    std::cout<<"usage: "<<str<<" server_port"<<std::endl;
}
int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        usage(argv[0]);
        exit(USAGE_ERROR);
    }
    EnableScrean();//在屏幕上打印日志
    dict _dict;
    uint16_t server_port = std::stoi(argv[1]);
    std::unique_ptr<udpserver> server_ptr = std::make_unique<udpserver>(server_port,\
    std::bind(&dict::translate,&_dict,std::placeholders::_1,std::placeholders::_2));
    server_ptr->init_server();
    server_ptr->start();
    return 0;
}

udpserver.hpp

C++ 复制代码
#pragma once
#include <iostream>
#include <string>
#include<sys/types.h>
#include<sys/socket.h>
#include<cstring>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<functional>
#include"log.hpp"
#include"InetAddr.hpp"
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};
using func_t  = std::function<std::string (const std::string&,bool &ok)>;
const static int defaultsockfd = -1;
class udpserver
{
public:
    udpserver(uint16_t port,func_t func)
    :_port(port)
    ,_sockfd(defaultsockfd)
    ,_func(func)
    ,_isrunning(false)
    {
    }
    ~udpserver()
    {
    }
    void init_server()
    {
        //1. 创建socket 套接字
        //sockfd 唯一表示套接字
        //AF_INET:address family internetd, 创建 IPv4 套接字,AF_INET6 用于 IPv6 地址族
        //SOCK_DGRAM:这是一个宏,指定了套接字的类型,用与udp通信
        _sockfd = socket(AF_INET,SOCK_DGRAM,0);
        if(_sockfd < 0)
        {
            LOG(FATAL,"socket error ,%s,%d\n",strerror(errno),errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO,"socket success,socket is : %d \n",_sockfd);
        //2. 填充sockaddr_in 结构
        struct sockaddr_in local;// struct sockaddr_in 系统提供的数据类型用于IPv4。local是变量,用户栈上开辟空间。
        bzero(&local,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);//port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节
        local.sin_addr.s_addr = INADDR_ANY;//任意ip绑定

        //3. bind sockfd 和 网络信息(ip + port)
        //addr 需要是一个指向 sockaddr 结构体的指针,在使用的时候需要强制转化成sockaddr_in
        //这样的好处是程序的通用性, 可以接收IPv4, IPv6
        int n = bind(_sockfd,(struct sockaddr * )&local,sizeof(local));
        if(n < 0)
        {
            LOG(FATAL,"bind error,%s, %d\n",strerror(errno),errno);
            exit(BIND_ERROR);
        }
        LOG(INFO,"sockfd bind success\n");
    }
    void start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            char request[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd,request,sizeof(request)-1,0,(struct sockaddr*)&peer,&len);
            if(n > 0)
            {
                request[n] = 0;
                InetAddr addr(peer);
                LOG(DEBUG,"get message from [%s %d]: %s\n",addr.Ip().c_str(),addr.Port(),request);
                bool ok;
                std::string response = _func(request,ok);//将请求回调出去,在外部处理
                sendto(_sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&peer,len);
            } 
        }
        _isrunning = false;
    }

private:
    int _sockfd;     // 文件描述符
    uint16_t _port;  // 服务器端口号
    bool _isrunning; // 服务器是否在运行
    func_t _func;   //回调函数,交给上层来处理
};

dict.hpp

c++ 复制代码
#pragma once
#include <iostream>
#include <unordered_map>
#include <string>
#include<stdio.h>
#include <fstream>
#include "log.hpp"
namespace dict_ns
{
    const std::string defaultpath = "./dect.txt";
    const std::string sep = ": ";
    class dict
    {
    private:
        bool load()
        {
            // happy: 快乐的
            std::ifstream in(_dict_conf_filepath);
            if (!in.is_open())
            {
                LOG(FATAL, "open %s error\n", _dict_conf_filepath.c_str());
                return false;
            }
            std::string line;
            while (std::getline(in, line))
            {
                if (line.empty())
                    continue;
                auto pos = line.find(sep);
                if (pos == std::string::npos)
                    continue;
                std::string word = line.substr(0, pos);
                if (word.empty())
                    continue;
                std::string chinese = line.substr(pos + sep.size());
                if (chinese.empty())
                    continue;
                LOG(DEBUG, "load info, %s : %s \n", word.c_str(), chinese.c_str());
                _dict.insert(std::make_pair(word, chinese));
            }
            in.close();
            LOG(DEBUG, "load %s success \n", _dict_conf_filepath.c_str());
            return true;
        }

    public:
        dict(const std::string &path = defaultpath)
            : _dict_conf_filepath(path)
        {
            std::cout << "1" << std::endl;
            load(); // 将磁盘数据加载到内存中
        }
        std::string translate(const std::string &word, bool &ok)
        {
            ok = true;
            auto iter = _dict.find(word);
            if (iter == _dict.end())
            {
                ok = false;
                return "未找到";
            }
            return iter->second;
        }
        ~dict()
        {
        }

    private:
        std::unordered_map<std::string, std::string> _dict; // 字典数据结构
        std::string _dict_conf_filepath;                    // 字典数据路径
    };
}

udpclient.cc

C++ 复制代码
#include <iostream>
#include "udpserver.hpp"
void usage(std::string str)
{
    std::cout << "usage: " << str << "  server_ip server_port" << std::endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        exit(USAGE_ERROR);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        LOG(FATAL, "socket error ,%s,%d\n", strerror(errno), errno);
        exit(SOCKET_ERROR);
    }
    struct sockaddr_in local; // struct sockaddr_in 系统提供的数据类型用于IPv4。local是变量,用户栈上开辟空间。
    bzero(&local, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(server_port); // port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
    // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节
    local.sin_addr.s_addr = inet_addr(server_ip.c_str());
    std::string message;
    while(true)
    {
        std::cout<<"please enter#";
        std::getline(std::cin,message);
        sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr* )&local,sizeof(local));
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];
        ssize_t n = recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,&len);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout<<"server echo#"<<buffer<<std::endl;
        }
    }
    return 0;
}

实验结果:

3. version3-udp聊天室

实验:实现任意客户端连接服务器,可以看见其他所有客户端发送的信息\

完整代码:gitee

main.cc

c++ 复制代码
#include"udpserver.hpp"
#include"log.hpp"
#include"message_route.hpp"
#include<memory>
void usage(std::string str)
{
    std::cout<<"usage: "<<str<<" server_port"<<std::endl;
}
int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        usage(argv[0]);
        exit(USAGE_ERROR);
    }
    EnableScrean();//在屏幕上打印日志
    uint16_t server_port = std::stoi(argv[1]);
    message_route _route;

    std::unique_ptr<udpserver> server_ptr = std::make_unique<udpserver>(server_port,\
    std::bind(&message_route::route,&_route,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
    server_ptr->init_server();
    server_ptr->start();
    return 0;
}

udpserver.cc

C++ 复制代码
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <functional>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include "InetAddr.hpp"
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};
using hander_message = std::function<void(int sockfd, const std::string message, const InetAddr who)>;
const static int defaultsockfd = -1;
class udpserver
{
public:
    udpserver(uint16_t port, hander_message func)
        : _port(port), _sockfd(defaultsockfd), _isrunning(false), _hander_message(func)
    {
    }
    ~udpserver()
    {
    }
    void init_server()
    {
        // 1. 创建socket 套接字
        // sockfd 唯一表示套接字
        // AF_INET:address family internetd, 创建 IPv4 套接字,AF_INET6 用于 IPv6 地址族
        // SOCK_DGRAM:这是一个宏,指定了套接字的类型,用与udp通信
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket error ,%s,%d\n", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket success,socket is : %d \n", _sockfd);
        // 2. 填充sockaddr_in 结构
        struct sockaddr_in local; // struct sockaddr_in 系统提供的数据类型用于IPv4。local是变量,用户栈上开辟空间。
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节
        local.sin_addr.s_addr = INADDR_ANY; // 任意ip绑定

        // 3. bind sockfd 和 网络信息(ip + port)
        // addr 需要是一个指向 sockaddr 结构体的指针,在使用的时候需要强制转化成sockaddr_in
        // 这样的好处是程序的通用性, 可以接收IPv4, IPv6
        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind error,%s, %d\n", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "sockfd bind success\n");
    }
    void start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            char message[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, message, sizeof(message) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n > 0)
            {
                message[n] = 0;
                InetAddr addr(peer);
                LOG(DEBUG, "get message from [%s %d]: %s\n", addr.Ip().c_str(), addr.Port(), message);

                _hander_message(_sockfd, message, addr); // 回调函数,交给上层处理
                // sendto(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,len);
            }
        }
        _isrunning = false;
    }

private:
    int _sockfd;     // 文件描述符
    uint16_t _port;  // 服务器端口号
    bool _isrunning; // 服务器是否在运行

    hander_message _hander_message; // 回调函数
};

udpclient.hpp

C++ 复制代码
#include <iostream>
#include "udpserver.hpp"
#include "Thread.hpp"
#include "comm.hpp"
using namespace ThreadModule;
void usage(std::string str)
{
    std::cout << "usage: " << str << "  server_ip server_port" << std::endl;
}

int init_client(const std::string &server_ip, uint16_t server_port, struct sockaddr_in *local)
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        LOG(FATAL, "socket error ,%s,%d\n", strerror(errno), errno);
        exit(SOCKET_ERROR);
    }
    memset(local, 0, sizeof(struct sockaddr_in));
    local->sin_family = AF_INET;
    local->sin_port = htons(server_port);//主机端口转网络端口
    local->sin_addr.s_addr = inet_addr(server_ip.c_str());
    return sockfd;
}
void recv_message(int sockfd, std::string name)
{
    // int fd = OpenDev("/dev/pts/1", O_WRONLY);
    while (true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];
        ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
        if (n > 0)  
        {
            buffer[n] = 0;
            fprintf(stderr, "[%s]%s\n", name.c_str(), buffer); // 将收到的信息向标准错误中写入
            // write(fd, buffer, strlen(buffer));//将收到的信息向fd中写入
        }
    }
}
void send_message(int sockfd, struct sockaddr_in &local, std::string name)
{
    std::string message;
    while (true)
    {
        printf("%s | enter$", name.c_str());
        fflush(stdout);
        std::getline(std::cin, message);
        sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&local, sizeof(local));
    }
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        exit(USAGE_ERROR);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);
    struct sockaddr_in local; // struct sockaddr_in 系统提供的数据类型用于IPv4。local是变量,用户栈上开辟空间。
    int sockfd = init_client(server_ip, server_port, &local);
    if (sockfd == -1)
    {
        return 1;
    }
    func_t r = std::bind(&recv_message, sockfd, std::placeholders::_1);
    func_t s = std::bind(&send_message, sockfd, local, std::placeholders::_1);
    // 创建两个线程分别执行收发信息
    Thread Recver(r, "recver");
    Thread Sender(s, "sender");
    Recver.Start();
    Sender.Start();
    Recver.Join();
    Sender.Join();
    return 0;
}

message_route.hpp

C++ 复制代码
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <vector>
#include "InetAddr.hpp"
#include "LockGuard.hpp"
#include <functional>
#include "Thread.hpp"
#include "ThreadPool.hpp"
using task_t = std::function<void()>;
class message_route
{
private:
    bool is_exists(const InetAddr &addr)
    {
        for (auto u : _online_user)
        {
            if (u == addr)
            {
                return true;
            }
        }
        return false;
    }

public:
    message_route()
    {
        pthread_mutex_init(&_mutex, nullptr);
    }
    void add_user(const InetAddr &who)
    {
        LockGuard lockguard(_mutex);
        if (is_exists(who))
        {
            return;
        }
        _online_user.push_back(who);
    }
    void del_user(const InetAddr &who)
    {
        LockGuard lockguard(_mutex);
        for (auto iter = _online_user.begin(); iter != _online_user.end(); iter++)
        {
            if (*iter == who)
            {
                _online_user.erase(iter);
                break;
            }
        }
    }
    void route_helper(int sockfd, const std::string message, InetAddr who)
    {
        LockGuard lockguard(_mutex);
        // 消息转发
        for (auto u : _online_user)
        {
            std::string send_message = "\n[" + who.Ip() + " : " + std::to_string(who.Port()) + "]#" + message + "\n";
            struct sockaddr_in clientaddr = u.addr();
            ::sendto(sockfd, send_message.c_str(), send_message.size(), 0, (struct sockaddr *)&clientaddr, sizeof(clientaddr));
        }
    }
    void route(int sockfd, const std::string& message, InetAddr who)
    {
        // 1. 当用户首次发信息时,将用户插入在线用户中
        add_user(who);
        // 1.1 客户端退出时
        if (message == "Q" || message == "quit")
        {
            del_user(who);
        }
        // 构建任务对象,入队列,让线程池进行转发
        task_t t = std::bind(&message_route::route_helper, this, sockfd, message, who);
        thread_pool<task_t>::GetInstance()->Enqueue(t);
    }
    ~message_route()
    {
        pthread_mutex_destroy(&_mutex);
    }

private:
    std::vector<InetAddr> _online_user;
    pthread_mutex_t _mutex;
};

实验结果:

完结!!!👍👍👍

相关推荐
正在努力的小河3 小时前
Linux设备树简介
linux·运维·服务器
荣光波比3 小时前
Linux(十一)——LVM磁盘配额整理
linux·运维·云计算
LLLLYYYRRRRRTT3 小时前
WordPress (LNMP 架构) 一键部署 Playbook
linux·架构·ansible·mariadb
轻松Ai享生活3 小时前
crash 进程分析流程图
linux
大路谈数字化5 小时前
Centos中内存CPU硬盘的查询
linux·运维·centos
luoqice5 小时前
linux下查看 UDP Server 端口的启用情况
linux
倔强的石头_7 小时前
【Linux指南】动静态库与链接机制:从原理到实践
linux
赏点剩饭7787 小时前
linux中的hostpath卷、nfs卷以及静态持久卷的区别
linux·运维·服务器
神鸟云7 小时前
DELL服务器 R系列 IPMI的配置
linux·运维·服务器·网络·边缘计算·pcdn
herderl7 小时前
**僵尸进程(Zombie Process)** 和**孤儿进程(Orphan Process)**
linux·运维·服务器·网络·网络协议