【Linux】基于UDP/TCP服务器与客户端的实现

目录

一、UDP

(一)Server.hpp

(二)Server.cpp

(三)Client.hpp

(四)Client.cpp

(五)User.hpp

二、TCP

(一)多进程版本的服务器与客户端

1、Server.hpp

2、Server.cpp

3、Client.hpp

4、Client.cpp

5、log.hpp

(二)多线程版本的服务器与客户端

(三)线程池版本的服务器与客户端

1、Server.hpp

2、thread.hpp

3、pthreadpool.hpp

4、Task.hpp

(四)线程池版本+守护进程的服务器与客户端

1、daemon.hpp

2、Server.cpp


一、UDP

(一)Server.hpp

cpp 复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
using namespace std;
class Server
{
    typedef function<void(int, string, uint16_t, string)> func_t;

public:
    Server(const func_t func, const uint16_t &port, const string &ip = "0.0.0.0") : _func(func), _port(port), _ip(ip) {}
    void init()
    {
        _fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_fd == -1)
        {
            cerr << "socket :" << strerror(errno) << endl;
            exit(1);
        }
        cerr << "socket succcess" << endl;

        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_port);
        addr.sin_addr.s_addr = inet_addr(_ip.c_str());
        int n = bind(_fd, (const sockaddr *)&addr, sizeof(addr));
        if (n != 0)
        {
            cerr << "bind error : " << strerror(errno) << endl;
            exit(2);
        }
        cerr << "bind success" << endl;
    }
    void start()
    {
        char buffer[1024];
        while (1)
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            size_t n = recvfrom(_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&addr, &len);
            if (n > 0)
            {
                buffer[n] = 0;
                uint16_t port = ntohs(addr.sin_port);
                string ip = inet_ntoa(addr.sin_addr);
                string messages = buffer;
                _func(_fd, ip, port, messages);
            }
        }
    }

private:
    func_t _func;
    int _fd;
    uint16_t _port;
    string _ip;
};

(二)Server.cpp

cpp 复制代码
#include "Server.hpp"
#include "User.hpp"
#include <memory>

userManager m;
void handlerMessage(int sockfd, const string &ip, const uint16_t &port, const string &message)
{
    cout << "[" + ip + "]" + "[" + to_string(port) + "]: " + message << endl;
    if (message == "online")
        m.insert(ip, port);
    else if (message == "offline")
        m.erase(ip, port);
    else if (m.isOnline(ip, port))
        m.broadcast(sockfd, ip, port, message);
    else
    {
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        addr.sin_port = htons(port);
        string response = "请先输入 online 上线";
        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&addr, sizeof(addr));
    }
}
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<Server> usvr(new Server(handlerMessage, port));
    usvr->init();
    usvr->start();
    return 0;
}

(三)Client.hpp

cpp 复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

using namespace std;
class Client
{
public:
    Client(const uint16_t &Sport, const string &Sip) : _Sport(Sport), _Sip(Sip) {}
    void init()
    {
        _fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_fd == -1)
        {
            cerr << "socket : " << strerror(errno) << endl;
            exit(1);
        }
        cout << "socket success" << endl;
    }
    static void *readMessage(void *arg)
    {
        int sockfd = *(static_cast<int *>(arg));
        while (true)
        {
            char buffer[1024];
            struct sockaddr_in temp;
            socklen_t temp_len = sizeof(temp);
            size_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &temp_len);
            if (n >= 0)
                buffer[n] = 0;
            cout << buffer << endl;
        }

        return nullptr;
    }
    void run()
    {
        pthread_create(&_read, nullptr, readMessage, (void *)&_fd);
        pthread_detach(_read);
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_Sport);
        addr.sin_addr.s_addr = inet_addr(_Sip.c_str());
        socklen_t len = sizeof(addr);
        while (1)
        {
            cerr << "Please input" << endl;
            string messages;
            cin >> messages;
            sendto(_fd, messages.c_str(), messages.size(), 0, (struct sockaddr *)&addr, sizeof(addr));
        }
    }

private:
    int _fd;
    uint16_t _Sport;
    string _Sip;
    pthread_t _read;
};

(四)Client.cpp

cpp 复制代码
#include "Client.hpp"
#include <memory>
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(3);
    }
    string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    unique_ptr<Client> ucli(new Client(port, ip));
    ucli->init();
    ucli->run();
    return 0;
}

(五)User.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;
class user
{
public:
    user(const string &ip, const uint16_t &port) : _ip(ip), _port(port) {}
    string &getIp()
    {
        return _ip;
    }
    uint16_t &getPort()
    {
        return _port;
    }

private:
    string _ip;
    uint16_t _port;
};

class userManager
{
public:
    void insert(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        _um.insert(make_pair(id, user(ip, port)));
    }
    void erase(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        _um.erase(id);
    }
    bool isOnline(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        return _um.find(id) == _um.end() ? false : true;
    }
    void broadcast(int sockfd, const string &ip, const uint16_t &port, const string &message)
    {
        for (auto &user : _um)
        {
            struct sockaddr_in addr;
            bzero(&addr, sizeof(addr));
            addr.sin_family = AF_INET;
            addr.sin_port = htons(user.second.getPort());
            addr.sin_addr.s_addr = inet_addr(user.second.getIp().c_str());
            string response = "[" + ip + "]" + "[" + to_string(port) + "]: " + message;
            sendto(sockfd, response.c_str(), response.size(), 0, (sockaddr *)&addr, sizeof(addr));
        }
    }

private:
    unordered_map<string, user> _um;
};

二、TCP

(一)多进程版本的服务器与客户端

1、Server.hpp

cpp 复制代码
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>
#include "log.hpp"
using namespace std;
class Server
{
public:
    Server(const uint16_t &port) : _fd(-1), _port(port) {}
    void init()
    {
        _fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_fd == -1)
        {
            cerr << strerror(errno) << endl;
            exit(1);
        }
        logMessage(NORMAL, "socket success");
        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(_port);
        int n = bind(_fd, (struct sockaddr *)&addr, sizeof(addr));
        if (n == -1)
        {
            logMessage(ERROR, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "bind success");

        n = listen(_fd, 5);
        if (n < 0)
        {
            cerr << strerror(errno) << endl;
            exit(2);
        }
        logMessage(NORMAL, "listen success");
    }
    // 进程版
    void start()
    {
        signal(SIGCHLD, SIG_IGN);
        while (1)
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int socket = accept(_fd, (struct sockaddr *)&addr, &len);
            cout << "accept successs : " << socket << endl;
            if (socket == -1)
            {
                cerr << strerror(errno) << endl;
                continue;
            }
            pid_t id = fork();
            if (id == 0)
            {
                close(_fd);
                serviceIO(socket);
                close(socket);
                exit(0);
            }
            close(socket);
        }
    }
    void serviceIO(const int &fd)
    {
        while (1)
        {
            char buffer[1024];
            ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
            if (size > 0)
            {
                buffer[size] = 0;
                cout << "[Client] : " << buffer << endl;
                string messages = buffer;
                ssize_t ret = write(fd, messages.c_str(), messages.size());
                if (ret < 0)
                {
                    cerr << strerror(errno) << endl;
                }
            }
            else if (size == 0)
            {
                cerr << "Client quit" << endl;
                break;
            }
            else
            {
                cerr << strerror(errno) << endl;
                exit(3);
            }
        }
    }

    void start()
    {
        while (1)
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int socket = accept(_fd, (struct sockaddr *)&addr, &len);
            if (socket == -1)
            {
                cerr << strerror(errno) << endl;
                continue;
            }
            logMessage(NORMAL, "accept successs : %d", socket);
        }
    }

    ~Server()
    {
        close(_fd);
    }

private:
    int _fd;
    uint16_t _port;
};

2、Server.cpp

cpp 复制代码
#include "Server.hpp"
#include "daemon.hpp"
#include <memory>
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<Server> usvr(new Server(port));
    usvr->init();
    usvr->start();
    return 0;
}

3、Client.hpp

cpp 复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
using namespace std;
class Client
{
public:
    Client(const string &ip, const uint16_t &port) : _fd(-1), _Sip(ip), _Sport(port) {}
    void init()
    {
        _fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_fd == -1)
        {
            cerr << strerror(errno) << endl;
            exit(1);
        }
        logMessage(NORMAL, "socket success");
    }
    void run()
    {
        struct sockaddr_in addr;
        bzero(&addr, 0);
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(_Sip.c_str());
        addr.sin_port = htons(_Sport);
        if (connect(_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
        {
            cerr << strerror(errno) << endl;
            exit(2);
        }
        else
        {
            while (true)
            {
                string messages;
                cout << " please input#";
                getline(cin, messages);
                write(_fd, messages.c_str(), messages.size());
                char buffer[1024];
                int n = read(_fd, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    cout << "[Server] : " << buffer << endl;
                }
                else
                    break;
            }
        }
    }
    ~Client()
    {
        close(_fd);
    }

private:
    int _fd;
    string _Sip;
    uint16_t _Sport;
};

4、Client.cpp

cpp 复制代码
#include "Client.hpp"
#include <memory>
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(3);
    }
    string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    unique_ptr<Client> ucli(new Client(ip, port));
    ucli->init();
    ucli->run();
    return 0;
}

5、log.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <ctime>
#include <stdio.h>
#include <stdarg.h>
using namespace std;
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
#define NUM 1024
const char *getLevel(const int &level)
{
    switch (level)
    {
    case 0:
        return "DEBUG";
    case 1:
        return "NORMAL";
    case 2:
        return "WARNING";
    case 3:
        return "ERROR";
    case 4:
        return "FATAL";
    default:
        return nullptr;
    }
}
void logMessage(int level, const char *format, ...)
{
    char logprefix[NUM];
    sprintf(logprefix, "[%s][%ld]:", getLevel(level), time(nullptr));
    char logContext[NUM];
    va_list arg;
    va_start(arg, format);
    vsnprintf(logContext, sizeof(logContext), format, arg);
    cout << logprefix << logContext << endl;
}

(二)多线程版本的服务器与客户端

多线程版本只有Server.hpp与多进程不同,其他文件相同。

cpp 复制代码
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>
#include "log.hpp"
using namespace std;
class Server
{
public:
    Server(const uint16_t &port) : _fd(-1), _port(port) {}
    void init()
    {
        _fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_fd == -1)
        {
            cerr << strerror(errno) << endl;
            exit(1);
        }
        logMessage(NORMAL, "socket success");
        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(_port);
        int n = bind(_fd, (struct sockaddr *)&addr, sizeof(addr));
        if (n == -1)
        {
            logMessage(ERROR, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "bind success");

        n = listen(_fd, 5);
        if (n < 0)
        {
            cerr << strerror(errno) << endl;
            exit(2);
        }
        logMessage(NORMAL, "listen success");
    }
   // 线程版
    void start()
    {
        while (1)
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int socket = accept(_fd, (struct sockaddr *)&addr, &len);
            cout << "accept successs : " << socket << endl;
            if (socket == -1)
            {
                cerr << strerror(errno) << endl;
                continue;
            }
            pthread_t t;
            pthread_create(&t, nullptr, serviceIO, (void *)&socket);
            pthread_detach(t);
        }
    }
    static void *serviceIO(void *arg)
    {
        int fd = *(static_cast<int *>(arg));
        while (1)
        {
            char buffer[1024];
            ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
            if (size > 0)
            {
                buffer[size] = 0;
                cout << "[Client] : " << buffer << endl;
                string messages = buffer;
                ssize_t ret = write(fd, messages.c_str(), messages.size());
                if (ret < 0)
                {
                    cerr << strerror(errno) << endl;
                }
            }
            else if (size == 0)
            {
                cerr << "Client quit" << endl;
                break;
            }
            else
            {
                cerr << strerror(errno) << endl;
                exit(3);
            }
        }
        close(fd);
        return nullptr;
    }
    void start()
    {
        while (1)
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int socket = accept(_fd, (struct sockaddr *)&addr, &len);
            if (socket == -1)
            {
                cerr << strerror(errno) << endl;
                continue;
            }
            logMessage(NORMAL, "accept successs : %d", socket);
        }
    }

    ~Server()
    {
        close(_fd);
    }

private:
    int _fd;
    uint16_t _port;
};

(三)线程池版本的服务器与客户端

其他文件与多进程版本相同。

1、Server.hpp

cpp 复制代码
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>
#include "pthreadpool.hpp"
#include "Task.hpp"
#include "log.hpp"
using namespace std;
class Server
{
public:
    Server(const uint16_t &port) : _fd(-1), _port(port) {}
    void init()
    {
        _fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_fd == -1)
        {
            cerr << strerror(errno) << endl;
            exit(1);
        }
        logMessage(NORMAL, "socket success");
        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(_port);
        int n = bind(_fd, (struct sockaddr *)&addr, sizeof(addr));
        if (n == -1)
        {
            logMessage(ERROR, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "bind success");

        n = listen(_fd, 5);
        if (n < 0)
        {
            cerr << strerror(errno) << endl;
            exit(2);
        }
        logMessage(NORMAL, "listen success");
    }
    void start()
    {
        pthreadPool<Task>::getInstance()->run();
        while (1)
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int socket = accept(_fd, (struct sockaddr *)&addr, &len);
            if (socket == -1)
            {
                cerr << strerror(errno) << endl;
                continue;
            }
            logMessage(NORMAL, "accept successs : %d", socket);
            pthreadPool<Task>::getInstance()->push(Task(socket, serviceIO));
        }
    }

    ~Server()
    {
        close(_fd);
    }

private:
    int _fd;
    uint16_t _port;
};

2、thread.hpp

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <functional>
#include <string>
using namespace std;
class Thread
{
    typedef std::function<void *(void *)> func_t;

private:
    static void *start_routine(void *arg)
    {
        Thread *th = static_cast<Thread *>(arg);
        th->callback(th->_arg);
    }
    void *callback(void *arg)
    {
        return _func(arg);
    }

public:
    void start(func_t func, void *arg = nullptr)
    {
        _func = func;
        _arg = arg;
        pthread_create(&_tid, nullptr, start_routine, this);
    }
    void join()
    {
        pthread_join(_tid, nullptr);
    }
    ~Thread()
    {
        join();
    }

private:
    func_t _func;
    pthread_t _tid;
    void *_arg;
};

3、pthreadpool.hpp

cpp 复制代码
#include <vector>
#include <queue>
#include "thread.hpp"
#include "Task.hpp"
template <class T>
class pthreadPool
{
private:
    void pop(T &date)
    {
        date = _tasks.front();
        _tasks.pop();
    }

    static void *handlerTask(void *arg)
    {
        pthreadPool *th = static_cast<pthreadPool *>(arg);
        while (1)
        {
            pthread_mutex_lock(&(th->_mutex));
            while (th->_tasks.empty())
                pthread_cond_wait(&(th->_cond), &(th->_mutex));
            T task;
            th->pop(task);
            pthread_mutex_unlock(&(th->_mutex));
            task();
        }
        return nullptr;
    }

public:
    pthreadPool(int num = 10) : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; ++i)
        {
            _threads.push_back(new Thread());
        }
    }
    ~pthreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for (int i = 0; i < _num; ++i)
        {
            delete _threads[i];
        }
    }
    void run()
    {
        for (int i = 0; i < _num; ++i)
        {
            _threads[i]->start(handlerTask, this);
        }
    }
    void push(const T &date)
    {
        pthread_mutex_lock(&_mutex);
        _tasks.push(date);
        pthread_cond_signal(&_cond);
        pthread_mutex_unlock(&_mutex);
    }
    static pthreadPool<T> *getInstance()
    {
        if (_tp == nullptr)
        {
            pthread_mutex_lock(&_sin);
            if (_tp == nullptr)
            {
                _tp = new pthreadPool();
            }
            pthread_mutex_unlock(&_sin);
        }
        return _tp;
    }
    pthreadPool(const pthreadPool<T> &tp) = delete;
    pthreadPool<T> operator=(pthreadPool<T>) = delete;

private:
    int _num;
    vector<Thread *> _threads;
    queue<T> _tasks;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static pthreadPool<T> *_tp;
    static pthread_mutex_t _sin;
};
template <class T>
pthreadPool<T> *pthreadPool<T>::_tp = nullptr;

template <class T>
pthread_mutex_t pthreadPool<T>::_sin = PTHREAD_MUTEX_INITIALIZER;

4、Task.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <functional>
#include "log.hpp"
using namespace std;
void serviceIO(const int &fd)
{
    while (1)
    {
        char buffer[1024];
        ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
        if (size > 0)
        {
            buffer[size] = 0;
            cout << "[Client] : " << buffer << endl;
            string messages = buffer;
            ssize_t ret = write(fd, messages.c_str(), messages.size());
            if (ret < 0)
            {
                cerr << strerror(errno) << endl;
            }
        }
        else if (size == 0)
        {
            logMessage(NORMAL, "Client quit");
            break;
        }
        else
        {
            cerr << strerror(errno) << endl;
            exit(3);
        }
    }
    close(fd);
}
class Task
{
    typedef function<void(int)> func_t;

public:
    Task() {}
    Task(const int &socket, func_t func) : _fd(socket), _func(func)
    {
    }
    void operator()()
    {
        _func(_fd);
    }

private:
    int _fd;
    func_t _func;
};

(四)线程池版本+守护进程的服务器与客户端

其他文件与线程池版本相同。

1、daemon.hpp

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
#define DEV "/dev/null"
void daemonSelf(const char *currPath = nullptr)
{
    signal(SIGPIPE, SIG_IGN);
    
    if (fork() > 0)
        exit(0);
    pid_t id = setsid();
    assert(id != -1);
    int fd = open(DEV, O_RDWR);
    if (fd >= 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
    }
    else
    {
        close(0);
        close(1);
        close(2);
    }
    if (currPath)
        chdir(currPath);
}

2、Server.cpp

cpp 复制代码
#include "Server.hpp"
#include "daemon.hpp"
#include <memory>
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<Server> usvr(new Server(port));
    usvr->init();
    daemonSelf();
    usvr->start();
    return 0;
}
相关推荐
cv高级工程师YKY1 小时前
SRE - - PV、UV、VV、IP详解及区别
大数据·服务器·uv
眠修2 小时前
Kuberrnetes 服务发布
linux·运维·服务器
BAOYUCompany3 小时前
暴雨服务器成功中标华中科技大学集成电路学院服务器采购项目
运维·服务器
鳄鱼皮坡4 小时前
仿muduo库One Thread One Loop式主从Reactor模型实现高并发服务器
运维·服务器
即将头秃的程序媛4 小时前
centos 7.9安装tomcat,并实现开机自启
linux·运维·centos
fangeqin5 小时前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
小Mie不吃饭5 小时前
FastAPI 小白教程:从入门级到实战(源码教程)
运维·服务器
爱奥尼欧6 小时前
【Linux 系统】基础IO——Linux中对文件的理解
linux·服务器·microsoft
戒不掉的伤怀6 小时前
【Navicat 连接MySQL时出现错误1251:客户端不支持服务器请求的身份验证协议;请考虑升级MySQL客户端】
服务器·数据库·mysql
超喜欢下雨天6 小时前
服务器安装 ros2时遇到底层库依赖冲突的问题
linux·运维·服务器·ros2