【网络】TCP套接字创建服务客户端与守护进程

news2024/10/6 8:28:20

文章目录

  • Tcp服务端
    • TcpServer.hpp
    • TcpServer.cc
  • Tcp客户端
    • TcpClient.hpp
    • TcpClient.cc
  • TCP客户端处理
  • 守护进程
  • 守护进程化

Tcp服务端

TcpServer.hpp

TCP服务端创建流程如下:

创建socket文件套接字对象,面向字节流SOCK_STREAM

bind绑定自己的网络信息,通常端口是固定的,IP地址默认为(0.0.0.0或者NADDR_ANY)

设置socket为监听状态(listen),一直帮我们获取新连接,接收请求,UDP没有链接,发过来的就是数据,TCP需要listen状态,是因为TCP是面向连接的,这点与UDP不同,TCP还需要进行监听

服务端获取客服端连接请求(accept)

最后进行通信,由于TCP是面向字节流,后续全是文件操作(read/write)

       void initServer()
        {
            // 1. 创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success: %d", _listensock);

            // 2. bind绑定自己的网络信息
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                logMessage(FATAL, "bind socket error");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");

            // 3. 设置socket 为监听状态
            if (listen(_listensock, gbacklog) < 0)
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen socket success");
        }

		void start()
        {
            for (;;)
            {
                // 4. server 获取新链接
                // sock, 和client进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next");
                    continue;
                }
                logMessage(NORMAL, "accept a new link success, get new sock: %d", sock); 
                cout<<"sock: "<<sock<<endl;
                
                //5.未来通信就用这个sock
                serviceIO(sock);
                close(sock);
            }
        }

void serviceIO(int sock)
{
    char buffer[1024];
    while (true)
    {
        ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "recv message: " << buffer << std::endl;

            std::string outbuffer = buffer;
            outbuffer += " server[echo]";
            write(sock, outbuffer.c_str(), outbuffer.size()); 
        }
        else if (n == 0)
        {
            logMessage(NORMAL, "client quit, me too!");
            break;
        }
    }
}

logMessage函数:打印出自己模拟相关的日志信息

TcpServer.cc

TcpServer.cc主函数进行相关函数的调用

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
// tcp服务器,启动上和udp server一模一样
// ./tcpserver local_port
int main(int argc, char *argv[])
{ 
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);

    unique_ptr<TcpServer> tsvr(new TcpServer(port));
    tsvr->initServer();
    tsvr->start();
    return 0;
}

Tcp客户端

TcpClient.hpp

Tcp客户端创建流程如下:

创建套接字(socket)对象,面向字节流SOCK_STREAM

客户端需要bind,但是客户端的绑定不需要我们自己写,操作系统会去绑定(无需程序员bind)

客户端发起连接请求(connect)

进行通信,客户端输入通信的内容,进行文件操作进行读写通信(read/write)

 void initClient()
    {
        // 1. 创建socket
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if(_sock < 0)
        {
            std::cerr << "socket create error" << std::endl;
            exit(2);
        }
        // 2. tcp的客户端要bind,不要显示的bind
    }
    void start()
    {
        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());

        if(connect(_sock, (struct sockaddr*)&server, sizeof(server)) != 0)
        {
            std::cerr << "socket connect error" << std::endl;
        }
        else
        {
            std::string msg;
            while(true)
            {
                std::cout << "Enter# ";
                std::getline(std::cin, msg);
                write(_sock, msg.c_str(), msg.size());

                char buffer[NUM];
                int n = read(_sock, buffer, sizeof(buffer)-1);
                if(n > 0)
                {
                    buffer[n] = 0;
                    std::cout << "Server回显# " << buffer << std::endl;
                }
                else
                {
                    break;
                }
            }
        }
    }

TcpClient.cc

TcpClinet.cc主函数进行相关函数的调用

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}
// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    unique_ptr<TcpClient> tcli(new TcpClient(serverip, serverport));
    tcli->initClient();
    tcli->start();
    return 0;
}

如上就是TCP套接字创建的整体流程

上述TCP服务端TcpServer.hpp整体代码如下👇

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "log.hpp"
namespace server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };
    static const uint16_t gport = 8080;
    static const int gbacklog = 5;
    class TcpServer;

    class ThreadData
    {
    };

    class TcpServer
    {
    public:
        TcpServer(const uint16_t &port = gport) : _listensock(-1), _port(port)
        {
        }

        void initServer()
        {
            // 1. 创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success: %d", _listensock);

            // 2. bind绑定自己的网络信息
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                logMessage(FATAL, "bind socket error");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");

            // 3. 设置socket 为监听状态
            if (listen(_listensock, gbacklog) < 0)
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen socket success");
        }

        void start()
        {
            for (;;)
            {
                // 4. server 获取新链接
                // sock, 和client进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next");
                    continue;
                }
                logMessage(NORMAL, "accept a new link success, get new sock: %d", sock);
                cout << "sock: " << sock << endl;
                serviceIO(sock);
                close(sock);
            }
        }

        void serviceIO(int sock)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    std::cout << "recv message: " << buffer << std::endl;

                    std::string outbuffer = buffer;
                    outbuffer += " server[echo]";
                    write(sock, outbuffer.c_str(), outbuffer.size());
                }
                else if (n == 0)
                {
                    logMessage(NORMAL, "client quit, me too!");
                    break;
                }
            }
        }

        ~TcpServer()
        {
        }

    private:
        int _listensock;
        uint16_t _port;
    };
}

TCP客户端处理

  • 多进程版本处理

上面的是单进程版本,要想实现多进程,我们只需要改造TcpServer.hpp即可:

namespace server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };
    static const uint16_t gport = 8080;
    static const int gbacklog = 5;
    class TcpServer
    {
    public:
        TcpServer(const uint16_t &port = gport) : _listensock(-1), _port(port)
        {
        }

        void initServer()
        {
            // 1. 创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success: %d", _listensock);

            // 2. bind绑定自己的网络信息
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                logMessage(FATAL, "bind socket error");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");

            // 3. 设置socket 为监听状态
            if (listen(_listensock, gbacklog) < 0)
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen socket success");
        }

        void start()
        {
            for (;;)
            {
                // 4. server 获取新链接
                // sock, 和client进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next");
                    continue;
                }
                logMessage(NORMAL, "accept a new link success, get new sock: %d", sock);
                cout << "sock: " << sock << endl;

                pid_t id = fork();
                if (id == 0) // child
                {
                    close(_listensock);
                    if(fork()>0) exit(0);
                    serviceIO(sock);
                    close(sock);
                    exit(0);
                }
                close(sock);

                //father
                pid_t ret = waitpid(id, nullptr, 0);
                if(ret>0)
                {
                    std::cout << "waitsuccess: " << ret << std::endl;
                }
            }
        }

        void serviceIO(int sock)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    std::cout << "recv message: " << buffer << std::endl;

                    std::string outbuffer = buffer;
                    outbuffer += " server[echo]";
                    write(sock, outbuffer.c_str(), outbuffer.size());
                }
                else if (n == 0)
                {
                    logMessage(NORMAL, "client quit, me too!");
                    break;
                }
            }
        }

        ~TcpServer()
        {
        }

    private:
        int _listensock;
        uint16_t _port;
    };
}

进程创建的理解:

利用fork创建子进程,id==0时是子进程,此时关闭子进程的监听sock,即close(_listensock)(注意:虽然后续代码会让子进程退出,最好还是close一下);对于if(fork()>0)此时由子进程去创建进程,创建出来的进程,我们为了方便描述,称为孙子进程,如果fork()>0,说明是父进程,也就是此时我们的子进程,让子进程退出,父进程在外部就不用阻塞等待子进程退出了,而我们的孙子进程成为孤儿进程,会被1号进程领养,无需关心。
孙子进程close(sock),关闭s使用完sock文件描述符,防止泄漏(后续代码是退出,最好还是close一下)。父进程close(sock),关闭通信的sock,父进程与顺子进程都有,父进程关闭,文件描述符引用计数–,直到孙子进程退出,fd才减为0.关闭。父进程提前关闭并不会影响孙子进程。父进程如果不关会造成文件描述符泄漏,最后等待采用阻塞等待

  • 多线程版本处理

对于一个进程中的所有线程,它们共享相同的文件描述符表,所以对于一个线程所对应的fd在使用完毕之后我们需要对其进行close关闭:

namespace server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };
    static const uint16_t gport = 8080;
    static const int gbacklog = 5;
    class TcpServer;

    class ThreadData
    {
    public:
        ThreadData(TcpServer *self, int sock) : _self(self), _sock(sock)
        {
        }

    public:
        TcpServer *_self;
        int _sock;
    };

    class TcpServer
    {
    public:
        TcpServer(const uint16_t &port = gport) : _listensock(-1), _port(port)
        {
        }

        void initServer()
        {
            // 1. 创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success: %d", _listensock);

            // 2. bind绑定自己的网络信息
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                logMessage(FATAL, "bind socket error");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");

            // 3. 设置socket 为监听状态
            if (listen(_listensock, gbacklog) < 0) // 第二个参数backlog后面在填这个坑
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen socket success");
        }

        void start()
        {
            for (;;)
            {
                // 4. server 获取新链接
                // sock, 和client进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next");
                    continue;
                }
                logMessage(NORMAL, "accept a new link success, get new sock: %d", sock);
                cout << "sock: " << sock << endl;

                pthread_t tid;
                ThreadData *td = new ThreadData(this, sock);
                pthread_create(&tid, nullptr, threadRoutine, td);
                pthread_join(tid, nullptr);
            }
        }
        static void *threadRoutine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadData *td = static_cast<ThreadData *>(args);
            td->_self->serviceIO(td->_sock);
            close(td->_sock);
            delete td;
            return nullptr;
        }

        void serviceIO(int sock)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    std::cout << "recv message: " << buffer << std::endl;

                    std::string outbuffer = buffer;
                    outbuffer += " server[echo]";
                    write(sock, outbuffer.c_str(), outbuffer.size());
                }
                else if (n == 0)
                {
                    logMessage(NORMAL, "client quit, me too!");
                    break;
                }
            }
        }

        ~TcpServer()
        {
        }

    private:
        int _listensock;
        uint16_t _port;
    };
}
  • 线程池版本处理

Tcpserver.hpp

#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"
namespace server
{
    static const uint16_t gport = 8080;
    static const int gbacklog = 5;
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };

    class TcpServer;
    class ThreadData
    {
    public:
        ThreadData(TcpServer *self, int sock)
            : _self(self), _sock(sock)
        {
        }

    public:
        TcpServer *_self;
        int _sock;
    };

    class TcpServer
    {
    public:
        TcpServer(const uint16_t port = gport)
            : _port(port), _listensock(-1)
        {
        }

        void initServer()
        {
            // 1.创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success : %d",_listensock);

            // 2.绑定自己的网络信息
            struct sockaddr_in local;
            memset(&local, 0, sizeof local);
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensock, (struct sockaddr *)&local, sizeof local) < 0)
            {
                logMessage(FATAL, "bind socket error");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");

            // 3.设置socket为监听状态,面向链接
            if (listen(_listensock, gbacklog) < 0)
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "liseten socket success");
        }

        void start()
        {
            // 线程池的初始化
            ThreadPool<Task>::getInstance()->run();
            logMessage(NORMAL,"Thread init success");
            // signal(SIGCHLD,SIG_IGN);
            for (;;)
            {
                // server获取新链接
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                // sock:和client进程通信的fd
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(FATAL, "accept error,next");
                    continue;
                }
                logMessage(NORMAL, "accept a new link success,get new sock:%d",sock);
                std::cout << "sock: " << sock << std::endl;
       
                //线程池
                ThreadPool<Task>::getInstance()->Push(Task(sock,serverIO));
            }
        }
        ~TcpServer()
        {
        }

    private:
        int _listensock; 
        uint16_t _port;
    };
}

Task.hpp

#pragma once
#include <iostream>
#include <functional>

void serverIO(int sock)
{
    char buffer[1024];
    while (true)
    {
        ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "Server message: " << buffer << std::endl;

            std::string outbuffer = buffer;
            outbuffer += "server[echo]";
            write(sock, outbuffer.c_str(), outbuffer.size());
        }
        else if (n == 0)
        {
            logMessage(NORMAL, "client quit,mee too");
            break;
        }
    }
}
class Task
{
    using func_t = std::function<void(int)>;

public:
    Task() {}

    Task(int sock,func_t func)
        : _sock(sock), _callback(func)
    {
    }

    void operator()()
    {
        _callback(_sock);
    }
private:
    int _sock;
    func_t _callback;
};

Threadpool.hpp

#pragma once
#include "Thread.hpp"
#include "LockGuard.hpp"
#include "log.hpp"
#include <vector>
#include <queue>
#include <pthread.h>
#include <mutex>
#include <unistd.h>
#include <mutex>
using namespace ThreadNs;

const int gnum = 10;

template <class T>
class ThreadPool;

template <class T>
class ThreadData
{
public:
    ThreadPool<T> *threadpool;
    std::string name;

public:
    ThreadData(ThreadPool<T> *tp, const std::string &n) : threadpool(tp), name(n)
    {
    }
};
template <class T>
class ThreadPool
{
private:
    static void *handlerTask(void *args)
    {
        ThreadData<T> *td = (ThreadData<T> *)args;
        while (true)
        {
            T t;
            {
                LockGuard lockguard(td->threadpool->mutex());

                while (td->threadpool->isQueueEmpty())
                {
                    td->threadpool->threadWait();
                }
                t = td->threadpool->pop(); // pop的本质是将任务从公共队列中拿到当前线程独立的栈中
            }
            t();
        }
        delete td;
        return nullptr;
    }

    ThreadPool(const int &num = gnum) : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; i++)
        {
            _threads.push_back(new Thread());
        }
    }

    void operator=(const ThreadPool &) = delete;
    ThreadPool(const ThreadPool &) = delete;

public:
    void lockQueue() { pthread_mutex_lock(&_mutex); }
    void unlockQueue() { pthread_mutex_unlock(&_mutex); }
    bool isQueueEmpty() { return _task_queue.empty(); }
    void threadWait() { pthread_cond_wait(&_cond, &_mutex); }
    T pop()
    {
        T t = _task_queue.front();
        _task_queue.pop();
        return t;
    }

    pthread_mutex_t *mutex()
    {
        return &_mutex;
    }

public:
    void run()
    {
        for (const auto &t : _threads)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->threadname());
            t->start(handlerTask, td);
            //std::cout << t->threadname() << "start..." << std::endl;
            logMessage(DEBUG,"%s start ...",t->threadname().c_str());
        }
    }
    void Push(const T &in)
    {
        LockGuard lockguard(&_mutex);
        _task_queue.push(in);
        pthread_cond_signal(&_cond);
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for (const auto &t : _threads)
            delete t;
    }
    static ThreadPool<T> *getInstance()
    {
        if (nullptr == tp)
        {

            _singlock.lock();
            if (nullptr == tp)
            {
                tp = new ThreadPool<T>();
            }
            _singlock.unlock();
            return tp;
        }
    }
private:
    int _num;
    std::vector<Thread *> _threads;
    std::queue<T> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *tp;
    static std::mutex _singlock;
};
template <class T>
ThreadPool<T> *ThreadPool<T>::tp = nullptr;
template <class T>
std::mutex ThreadPool<T>::_singlock;

守护进程

守护进程:服务器要做到一点:服务器启动之后,不在受到用户的登录退出影响,服务器可以自定义运行,不受用户登录注销影响的进程是守护进程

&:让一个命令在后台运行

jobs命令用于显示当前shell会话中的活动作业(jobs),包括前台作业和后台作业。

image-20230515200350177

任务由进程组完成

image-20230515200755734

从哪里看出是同一个会话:SID

image-20230515201420364

现在,把作业放在前台。fg 2:既我们把上面的2号作业放在前台

image-20230515201517163

ctrl+z:

一个任务在前台如果暂停了会立马放在后台:

image-20230515201616577

此时我们的2号任务是stopped状态的,我们让它启动起来:bg 2:

image-20230515201702848

fg:放前台然后ctrl+z停止,bg:启动,作业是可以互相前后端转化的:

image-20230515202205591

把任务提到前台后,命令行解释器就不会做响应了:这是因为当前整个会话中只能有一个前台任务,退出时任务都会被清掉,这样的任务是可能受到用户登录和注销的影响的!

要想不受影响,我们要独立出来,自成会话,自成进程组,和终端设备无关。这就是守护进程,可以一直运行,除非未来不让它运行了。

守护进程化

系统提供了接口。守护进程的原理以及代码:

daemon.hpp: setsid:形成一个新的进程组,创建一个新的会话,不能随便掉,调用这个函数的进程不能是组长

#pragma once
#include <unistd.h>
#include <signal.h>
#include <cstdlib>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define DEV "/dev/null"//数据黑洞,写入的数据会被吃掉,读取数据什么都读不到

void daemonSelf(const char *currPath = nullptr)
{
    // 1. 让调用进程忽略掉异常的信号
    signal(SIGPIPE, SIG_IGN);

    // 2. 如何让自己不是组长,setsid
    if (fork() > 0)
        exit(0);
    // 子进程 -- 守护进程,精灵进程,本质就是孤儿进程的一种!
    pid_t n = setsid();
    assert(n != -1);

    // 3. 守护进程是脱离终端的,关闭或者重定向以前进程默认打开的文件
    int fd = open(DEV, O_RDWR);
    if(fd >= 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);

        close(fd);
    }
    else
    {
        close(0);
        close(1);
        close(2);
    }

    // 4. 可选:进程执行路径发生更改

    if(currPath) chdir(currPath);

直接在TcpServer.cc文件中进行调用即可:

#include "tcpServer.hpp"
#include "memory"
#include "daemon.hpp"
using namespace Server;
static void Usage(std::string proc)
{
    std::cout<<"Usage:\n\t"<<proc<<"serverPort\n\n";
}

int main(int argc,char* argv[])
{
    if(argc!=2)//判断外部传入的参数是否为2
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port=std::stoi(argv[1]);
    std::unique_ptr<TcpServer> tsvr(new TcpServer(port));
    tsvr->InitServer();
    daemonSele();//守护进程化,让这个独立的孤儿进程去启动服务器
    tsvr->Start();
    return 0;
}

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

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

相关文章

nacos注册中心配置springboot动态刷新源码解读

0丶简介 如何快速启动,请去官网看文档,言简意赅 https://nacos.io/zh-cn/docs/quick-start.html 只描述为何实现动态刷新的关键源码 1丶基于目前最新版本 <!-- https://mvnrepository.com/artifact/com.alibaba.boot/nacos-config-spring-boot-starter --> <depend…

命名管道详解

一、命名管道 1、命名管道与匿名管道一个很显著的区别是&#xff1a;匿名管道只能在有血缘关系的进程间进行通信&#xff0c;但命名管道可以让两个毫无关系的进程进行通信。 2、如果我们想在不相关的进程间交换数据&#xff0c;我们可以用到FIFO文件来进行通信&#xff0c;这…

【Nginx rewrite】

目录 一、常见的Nginx 正则表达式二、location1、location 解释2、location 示例说明&#xff1a;3、实际网站使用中&#xff0c;至少有三个匹配规则定义&#xff1a; 二、rewrite1、rewrite跳转实现&#xff1a;2、rewrite 执行顺序如下&#xff1a;3、rewrite 示例 一、常见的…

B-3:Linux 系统渗透提权

B-3&#xff1a;Linux 系统渗透提权 任务环境说明&#xff1a; 服务器场景&#xff1a;Server2204&#xff08;关闭链接&#xff09; 用户名&#xff1a;hacker 密码&#xff1a;123456 1.使用渗透机对服务器信息收集&#xff0c;并将服务器中 SSH 服务端口号作为 flag 提 …

Java工厂模式(随笔)

前言&#xff1a;Java工厂模式是一种创建型设计模式&#xff0c;它提供了一种将对象创建过程封装到一个单独的类中的方式&#xff0c;这个类就是被称为‘工厂类’&#xff0c;它根据特定的条件来决定应该创建哪个对象&#xff01; 文章目录&#xff1a; 三大工厂模式介绍特殊工…

计算机网络第二章——物理层(上)

提示&#xff1a;男儿何不带吴钩&#xff0c;收取关山五十州 文章目录 2.1.1 物理层基本概念知识引导物理层接口特征 2.1.2 数据通信基础知识数据通信相关术语数据通信系统要考虑的问题三种通信方式串行传输&并行传输同步传输&异步传输 脑图时刻 2.1.3 数据通信基础知识…

Google Play上架aab保姆级教程(纯aab上架/已上架apk转aab上架)

0、上传密钥 & 应用签名密钥 “Google 会使用上传证书验证您的身份&#xff0c;并使用您的应用签名密钥为 APK 签名以进行分发” 以上为官方解释。 2021年8月起&#xff0c;上传google play的应用必须以aab格式&#xff0c;aab的签名流程要比之前apk的复杂一些。需要上传…

如何真正有效地应对项目中的需求变更?

需求变更在奉行唯快不破的互联网公司&#xff0c;可算程序员头号噩梦&#xff0c;“996”直接元凶。 阿里口号拥抱变化。既然需求变更无法被消灭&#xff0c;就要通过学习&#xff0c;掌握更好应对需求变更方法。 1 常见的需求变更流程 先要发起变更申请&#xff0c;由变更委…

从IO多路复用到redis线程模型

文章目录 Unix IO模型分类阻塞IO - Blocking IO非阻塞IO - NoneBlocking IOIO多路复用 - IO multiplexing信号驱动IO - signal driven IO异步IO - asynchronous IO同步与异步的定义阻塞与非阻塞的定义 IO多路复用有哪些实现IO多路复用的大致实现selectpollepoll redis的线程模型…

Python实战基础19-异常处理及程序调试

1、异常概述 在程序运行过程中&#xff0c;经常会遇到各种各样的错误&#xff0c;这些错误统称为“异常”。 这些异常有的是由于开发者将关键字敲错&#xff0c;这类错误产生的是SyntaxError:invalid syntax&#xff08;无效语法&#xff09;&#xff0c;这将直接导致程序不能…

3.二进制高可用安装k8s 1.23集群(生产级)

二进制高可用安装k8s集群(生产级) 本文档适用于kubernetes1.23 节点 Etcd Cluster Etcd是一个数据库,k8s做的一些变更啥的都会存到Etcd中 如果集群比较大建议与master节点分装,单独装Etcd master节点 master分为几个重要的组件 你所有的流量都会经过Kube-APIServer Co…

排序算法——希尔排序图文详解

文章目录 希尔排序基本思想整体插入思想预排序结论 代码实现实现代码直接插入排序与希尔排序的效率比较测试代码&#xff1a; 时间复杂度 希尔排序 注1&#xff1a;本篇是基于对直接插入排序法的拓展&#xff0c;如果对直接插入法不了解&#xff0c;建议先看看直接插入排序 注…

Learning C++ No.27 【布隆过滤器实战】

引言 北京时间&#xff1a;2023/5/31/22:02&#xff0c;昨天的计算机导论考试&#xff0c;三个字&#xff0c;哈哈哈&#xff0c;摆烂&#xff0c;大致题目都是一些基础知识&#xff0c;但是这些基础知识都是非常非常理论的知识&#xff0c;理论的我一点不会&#xff0c;像什么…

【自制C++深度学习框架】表达式层的设计思路

表达式层的设计思路 在深度学习框架中&#xff0c;Expression Layer&#xff08;表达式层&#xff09;是指一个通用的算子&#xff0c;其允许深度学习网络的不同层之间结合和嵌套&#xff0c;从而支持一些更复杂的操作&#xff0c;如分支之间的加减乘除&#xff08;elementAdd…

PyTorch 深度学习 || 专题二:PyTorch 实验框架的搭建

PyTorch 实验框架的搭建 1. PyTorch简介 PyTorch是由Meta AI(Facebook)人工智能研究小组开发的一种基于Lua编写的Torch库的Python实现的深度学习库&#xff0c;目前被广泛应用于学术界和工业界&#xff0c;PyTorch在API的设计上更加简洁、优雅和易懂。 1.1 PyTorch的发展 “…

Numpy---生成数组的方法、从现有数组中生成、生成固定范围的数组

1. 生成数组的方法 np.ones(shape, dtypeNone, orderC) 创建一个所有元素都为1的多维数组 参数说明: shape : 形状&#xff1b; dtypeNone: 元素类型&#xff1b; order &#xff1a; {‘C’&#xff0c;‘F’}&#xff0c;可选&#xff0c;默认值&#xff1a;C 是否在内…

BPMN2.0自动启动模拟流程

思路&#xff1a;BPMN的流程模拟启动&#xff0c;主要是通过生成令牌&#xff0c;并启动令牌模拟 流程模拟的开启需要关键性工具&#xff1a;bpmn-js-token-simulation&#xff0c;需要先行下载 注&#xff1a;BPMN2.0的流程模拟工具版本不同&#xff0c;启动方式也不一样&am…

Kafka某Topic的部分partition无法消费问题

今天同事反馈有个topic出现积压。于是上kfk管理平台查看该topic对应的group。发现6个分区中有2个不消费&#xff0c;另外4个消费也较慢&#xff0c;总体lag在增长。查看服务器日志&#xff0c;日志中有rebalance 12 retry 。。。Exception&#xff0c;之后改消费线程停止。 查…

chatgpt赋能python:Python实现数据匹配的方法

Python实现数据匹配的方法 在数据分析和处理中&#xff0c;经常需要将两组数据进行匹配。Python作为一门强大的编程语言&#xff0c;在数据匹配方面也有着其独特的优势。下面我们将介绍Python实现数据匹配的方法。 数据匹配 数据匹配通常指的是将两组数据根据某些特定的规则…

理解calico容器网络通信方案原理

0. 前言 Calico是k8s中常用的容器解决方案的插件&#xff0c;本文主要介绍BGP模式和IPIP模式是如何解决的&#xff0c;并详细了解其原理&#xff0c;并通过实验加深理解。 1. 介绍Calico Calico是属于纯3层的网络模型&#xff0c;每个容器都通过IP直接通信&#xff0c;中间通…