• 网络套接字2


    网络套接字2

    📟作者主页:慢热的陕西人

    🌴专栏链接:Linux

    📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

    本博客主要内容实现了一个tcp的网络通信的示例代码,并且详细阐述了从中遇到的一些新概念

    1.实现一个例子

    我们实现一个例子来感受tcp和udp的区别和相同之处

    TCP:

    1. 创建socket: socket(AF_INET, SOCK_STREAM, 0)
    2. 绑定socket: bind()
    3. 开始监听: listen()
    4. 接受新的连接: accept()
    5. 发送和接收数据: send(), recv()
    6. 关闭socket: close()

    UDP:

    1. 创建socket: socket(AF_INET, SOCK_DGRAM, 0)
    2. 绑定socket: bind()
    3. 发送和接收数据: sendto(), recvfrom()
    4. 关闭socket: close()

    我们发现udp相比较tcp减少了两步监听和建立连接两步:这也对应了tcp和udp的特性!

    1.1V1版本

    这个版本只是简单的实现整个过程。

    我们实现简单案例的源码:

    //tcpServer.hpp
    
    #pragma once
    
    #include"err.hpp"
    
    #include
    #include
    #include
    #include
    #include
    #include 
    #include 
    #include 
    #include 
    
    namespace ns_server
    {
        static const uint16_t defaultport = 8081; 
        static const int backlog = 32; 
        using func_t = std::function<std::string(const std::string&)>; 
        class TcpServer
        {
        public:
            TcpServer(func_t func, uint16_t port = defaultport):func_(func), port_(port), quit_(true)
            {
            }
            
            void Initserver()
            {   
                //1.创建socket文件
                listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listen_socket_ < 0)
                {
                    std::cerr<< "create socket error" << std::endl;
                    exit(SOCKET_ERR);
                }
    
                //2.创建对应的socket结构体,并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 = htonl(INADDR_ANY);
    
                if(bind(listen_socket_, (struct sockaddr*)&local, sizeof(local)) < 0)
                {
                    std::cerr<< "bind socket error" << std::endl;
                    exit(BIND_ERR);               
                }
                
                //3.监听
                if(listen(listen_socket_, backlog) < 0)
                {
                    std::cerr<< "listen error" << std::endl;
                    exit(LISTEN_ERR);                    
                }
            }
            void start()
            {   
                quit_ = false;
                while(true)
                {
                    //4.获取连接
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
    
                    std::cout << "我正在等待连接" << std::endl;
                    int sock = accept(listen_socket_, (struct sockaddr*)&client, &len);
                    if(sock < 0)
                    {
                        std::cerr << "accept error" << std::endl;   
                        continue;
                    }
    
                    //5.获取新连接成功,开始进行业务处理
                    std::cout << "获取新连接成功" << sock << "from" << listen_socket_ << std::endl;
    
                    service(sock);
                }
            }
    
            void service(int sock)
            {
                char buffer[1024];
                while(true)
                {
                    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
                    if(s > 0)
                    {
                        buffer[s] = 0;
                        //执行回调函数
                        std::string res = func_(buffer);
                        std::cout << res << std::endl; 
    
                        write(sock, res.c_str(), res.size());
                    }
                    else if(s == 0)
                    {
                        //表示对应的远端关闭了通信
                        close(sock);
                        std::cout << "quit" << std::endl;
                        break;
                    }
                    else // s < 0 
                    {   
                        close(sock);
                        std::cerr << "read error" << std::endl;   
                        break;
                    }
                }
            }
    
    
            ~TcpServer()
            {
    
            }
        private:
            uint16_t port_;
            int listen_socket_;
            bool quit_; //表示当前服务器是否运行
            func_t func_;
        };
    }
    
    //tcpServer.cc
    #include"tcpServer.hpp"
    using namespace std;
    using namespace ns_server;
    
    static void usage(string proc)
    {   
        std::cout << "Usage:\n\t" << proc <<"port\n"<< std::endl;
    }
    
    
    std::string echo (const std::string& message)
    {
        return message;
    }
    
    int main(int argc, char* argv[])
    {
        if(argc != 2)
        {
            usage(argv[1]);
            exit(USAGE_ERR);
        }
    
        uint16_t port = atoi(argv[1]);
        unique_ptr<TcpServer> tsvr(new TcpServer(echo, port));
    
        tsvr->Initserver();
        tsvr->start();
        return 0;
    }
    
    //tcpClient.cc
    #include"err.hpp"
    
    
    #include
    #include
    #include
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    static void Usage(string proc)
    {
        cout << "Usage:\n\t" << proc << "serverip  serverport  "<< "port\n" << endl;
    }
    
    
    int main(int argc, char* argv[])
    {
        if(argc != 3)
        {
            Usage(argv[0]);
            exit(USAGE_ERR);
        }
    
        string serverip = argv[1];
        uint16_t serverport = atoi(argv[2]);
    
    
        //1.create sock
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if(sock < 0)
        {
            std::cerr << "socket error:" << strerror(errno) << std::endl;
            exit(SOCKET_ERR); 
        }
    
        //2.connect
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_port = htons(serverport);
        server.sin_family = AF_INET;
        inet_aton(serverip.c_str(), &(server.sin_addr));
    
        int cnt = 5;
        while(connect(sock, (struct sockaddr*)& server, sizeof(server)) != 0)
        {
            sleep(1);
            cout << "正在尝试重连, 重连次数还有" << cnt-- << "次" << endl;
            if(cnt <= 0) break;
        }
    
        if(cnt <= 0)
        {
            //连接失败
            std::cerr << "connect error" << endl;
            exit(CONNECT_ERR);
        }
    
        char buffer[1024];
        while(true)
        {
            string line;
            cout << "Enter>>>";
            getline(cin, line);
    
            write(sock, line.c_str(), line.size());
    
            ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
            if(s > 0)
            {
                buffer[s] = 0;
                cout << "srever echo>>>" << buffer << endl;
            }
            else if(s == 0)
            {
                cerr << "server quit" << endl;
                break;
            }
            else 
            {
                cerr <<"read error: "  << strerror(errno) << endl;
                break;
            }
        }
    
        close(sock);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253

    1.2V2版本

    实现一个多进程版本,因为我们的上一个版本,连接的时候会阻塞在那里,非常的不好。

    我们让子进程帮我们去执行对应service服务:

    细节:

    • 进程等待问题:a.使用孤儿进程 b.使用信号让父进程忽略等待
    • 文件描述符的问题:父进程及时关闭不需要的文件描述符,提高文件描述符的利用率
    //tcpServer.hpp
    #pragma once
    
    #include"err.hpp"
    
    #include
    #include
    #include
    #include
    #include
    #include 
    #include 
    #include 
    #include 
    
    namespace ns_server
    {
        // signal(SIGCHLD, SIG_IGN); // 我最推荐的
    
        static const uint16_t defaultport = 8081; 
        static const int backlog = 32; 
        using func_t = std::function<std::string(const std::string&)>; 
        class TcpServer
        {
        public:
            TcpServer(func_t func, uint16_t port = defaultport):func_(func), port_(port), quit_(true)
            {
    
            }
            
            void Initserver()
            {   
                //1.创建socket文件
                listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listen_socket_ < 0)
                {
                    std::cerr<< "create socket error" << std::endl;
                    exit(SOCKET_ERR);
                }
    
                //2.创建对应的socket结构体,并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 = htonl(INADDR_ANY);
    
                if(bind(listen_socket_, (struct sockaddr*)&local, sizeof(local)) < 0)
                {
                    std::cerr<< "bind socket error" << std::endl;
                    exit(BIND_ERR);               
                }
                
                //3.监听
                if(listen(listen_socket_, backlog) < 0)
                {
                    std::cerr<< "listen error" << std::endl;
                    exit(LISTEN_ERR);                    
                }
            }
            void start()
            {   
                quit_ = false;
                while(true)
                {
                    //4.获取连接
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
    
                    std::cout << "我正在等待连接" << std::endl;
                    int sock = accept(listen_socket_, (struct sockaddr*)&client, &len);
                    if(sock < 0)
                    {
                        std::cerr << "accept error" << std::endl;   
                        continue;
                    }
    
                    std::string clientip = inet_ntoa(client.sin_addr);
                    uint16_t clientport = ntohs(client.sin_port);
    
                    //5.获取新连接成功,开始进行业务处理
                    std::cout << "获取新连接成功" << sock << "from" << listen_socket_ << std::endl;
                    
                    //v1
                    //service(sock, clientip, clientport);
    
                    //v2:多线程版本
                    pid_t id = fork();
                    if(id < 0)  //父进程
                    { 
                        //这里因为子进程继承去了父进程的文件描述符表之后,我们这里的sock就没必要了
                        //所以我们直接关闭,也可以提高文件描述符的利用率
                        close(sock);
                        continue;
                    }
                    else if(id == 0) //child, 父进程的fd, 会被child继承吗?会。 父子会用同一张文件描述符表吗?不会,子进程拷贝继承父进程的fd table;
                    {
                        //建议关闭掉不需要的fd
                        close(listen_socket_); 
                        if(fork() > 0) exit(0); 
                        //这里直接在创建一个子进程,也就是说子进程创建了一个进程,然后自己推出掉
                        //那么这时候,就形成了孤儿进程,那么孤儿进程是会被操作系统领养的,而不是留给父进程去等待了
                        //或者我们也可以直接忽略等待子进程,signal(SIGCHLD, SIG_IGN); // 我最推荐的
    
                        service(sock, clientip, clientport);
                        exit(0);
                    }
    
                    close(sock);
                }
            }
    
            void service(int sock, std::string ip, uint16_t port)
            {
                std::string who = ip + "-" + std::to_string(port);
    
                char buffer[1024];
                while(true)
                {
                    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
                    if(s > 0)
                    {
                        buffer[s] = 0;
                        //执行回调函数
                        std::string res = func_(buffer);
                        std::cout << who << ">>" << res << std::endl; 
                        write(sock, res.c_str(), res.size());
                    }
                    else if(s == 0)
                    {
                        //表示对应的远端关闭了通信
                        close(sock);
                        std::cout << "quit" << std::endl;
                        break;
                    }
                    else // s < 0 
                    {   
                        close(sock);
                        std::cerr << "read error" << std::endl;   
                        break;
                    }
                }
            }
    
    
            ~TcpServer()
            {
    
            }
        private:
            uint16_t port_;
            int listen_socket_;
            bool quit_; //表示当前服务器是否运行
            func_t func_;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158

    1.3V3版本

    采用多线程的思路。

    #pragma once
    
    #include"err.hpp"
    
    #include
    #include
    #include
    #include
    #include
    #include 
    #include 
    #include 
    #include 
    
    namespace ns_server
    {
        // signal(SIGCHLD, SIG_IGN); // 我最推荐的
    
        static const uint16_t defaultport = 8081; 
        static const int backlog = 32; 
        using func_t = std::function<std::string(const std::string&)>; 
    
        class TcpServer;
    
        class ThreadData
        {
        public:
            ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer* ts):
            sock(fd),
            clientip(ip),
            clientport(port),
            current(ts)
            {}
    
        public:
            int sock;
            std::string clientip;
            uint16_t clientport;
            TcpServer* current;
        };
    
    
        class TcpServer
        {
        public:
            TcpServer(func_t func, uint16_t port = defaultport):func_(func), port_(port), quit_(true)
            {
    
            }
            
            void Initserver()
            {   
                //1.创建socket文件
                listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listen_socket_ < 0)
                {
                    std::cerr<< "create socket error" << std::endl;
                    exit(SOCKET_ERR);
                }
    
                //2.创建对应的socket结构体,并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 = htonl(INADDR_ANY);
    
                if(bind(listen_socket_, (struct sockaddr*)&local, sizeof(local)) < 0)
                {
                    std::cerr<< "bind socket error" << std::endl;
                    exit(BIND_ERR);               
                }
                
                //3.监听
                if(listen(listen_socket_, backlog) < 0)
                {
                    std::cerr<< "listen error" << std::endl;
                    exit(LISTEN_ERR);                    
                }
            }
            void start()
            {   
                quit_ = false;
                while(true)
                {
                    //4.获取连接
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
    
                    std::cout << "我正在等待连接" << std::endl;
                    int sock = accept(listen_socket_, (struct sockaddr*)&client, &len);
                    if(sock < 0)
                    {
                        std::cerr << "accept error" << std::endl;   
                        continue;
                    }
    
                    std::string clientip = inet_ntoa(client.sin_addr);
                    uint16_t clientport = ntohs(client.sin_port);
    
                    //5.获取新连接成功,开始进行业务处理
                    std::cout << "获取新连接成功" << sock << "from" << listen_socket_ << std::endl;
    
                    //v3多线程
                    pthread_t tid;
                    ThreadData* td = new ThreadData(sock, clientip, clientport, this);
                    pthread_create(&tid, nullptr, threadRoutine, td);
    
                }
            }
    
    
            static void* threadRoutine(void* args)
            {
                //分离线程,防止主进程阻塞的情况
                pthread_detach(pthread_self());
    
                ThreadData* td = static_cast<ThreadData*>(args);
                td->current->service(td->sock, td->clientip, td->clientport);
    
                delete td;
                return nullptr;
            }
    
            void service(int sock, std::string ip, uint16_t port)
            {
                std::string who = ip + "-" + std::to_string(port);
    
                char buffer[1024];
                while(true)
                {
                    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
                    if(s > 0)
                    {
                        buffer[s] = 0;
                        //执行回调函数
                        std::string res = func_(buffer);
                        std::cout << who << ">>" << res << std::endl; 
                        write(sock, res.c_str(), res.size());
                    }
                    else if(s == 0)
                    {
                        //表示对应的远端关闭了通信
                        close(sock);
                        std::cout << "quit" << std::endl;
                        break;
                    }
                    else // s < 0 
                    {   
                        close(sock);
                        std::cerr << "read error" << std::endl;   
                        break;
                    }
                }
            }
    
    
            ~TcpServer()
            {
    
            }
        private:
            uint16_t port_;
            int listen_socket_;
            bool quit_; //表示当前服务器是否运行
            func_t func_;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170

    1.4V4版本

    使用线程池的思路实现+单例模式的实现

    思路:我们引入之前实现的线程池,Task类,Thread类和LockGuard类;

    首先我们构造一个Task类的实例对象,传入我们需要的参数(也就是service函数需要的参数);

    细节:在传入的过程中我们需要绑定一个参数就是this指针。

    之后将Task的实例放入到线程池中,让线程去执行即可。

    但是因为我们的service函数内部是死循环,所以我们只有一个有限的线程来提供有限的服务,换句话说就是当我们的进程数等于当前的用户访问量时,那么之后进来的用户就会连接失败或者,服务不会被响应,所以我们可以将每一次的响应变成不是死循环,而是每次相应完成就退出。

    执行效果:

    image-20240312173311676

    代码:

    //tcp_server.hpp
    #pragma once
    
    
    using namespace std;
    
    #include
    #include
    #include
    #include
    #include
    #include
    
    #include 
    #include 
    #include 
    #include 
    
    #include"Thread.hpp"
    #include"LockGuard.hpp"
    #include"err.hpp"
    #include"Task.hpp"
    #include"ThreadPool.hpp"
    
    namespace ns_server
    {
        // signal(SIGCHLD, SIG_IGN); // 我最推荐的
    
        static const uint16_t defaultport = 8081; 
        static const int backlog = 32; 
        using func_t = std::function<std::string(const std::string&)>; 
    
        class TcpServer;
    
        class ThreadData
        {
        public:
            ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer* ts):
            sock(fd),
            clientip(ip),
            clientport(port),
            current(ts)
            {}
    
        public:
            int sock;
            std::string clientip;
            uint16_t clientport;
            TcpServer* current;
        };
    
    
    
    
    
        class TcpServer
        {
        public:
            TcpServer(func_t func, uint16_t port = defaultport):func_(func), port_(port), quit_(true)
            {
    
            }
            
            void Initserver()
            {   
                //1.创建socket文件
                listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listen_socket_ < 0)
                {
                    std::cerr<< "create socket error" << std::endl;
                    exit(SOCKET_ERR);
                }
    
                //2.创建对应的socket结构体,并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 = htonl(INADDR_ANY);
    
                if(bind(listen_socket_, (struct sockaddr*)&local, sizeof(local)) < 0)
                {
                    std::cerr<< "bind socket error" << std::endl;
                    exit(BIND_ERR);               
                }
                
                //3.监听
                if(listen(listen_socket_, backlog) < 0)
                {
                    std::cerr<< "listen error" << std::endl;
                    exit(LISTEN_ERR);                    
                }
            }
            void start()
            {   
                quit_ = false;
                while(true)
                {
                    //4.获取连接
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
    
                    std::cout << "我正在等待连接" << std::endl;
                    int sock = accept(listen_socket_, (struct sockaddr*)&client, &len);
                    if(sock < 0)
                    {
                        std::cerr << "accept error" << std::endl;   
                        continue;
                    }
    
                    std::string clientip = inet_ntoa(client.sin_addr);
                    uint16_t clientport = ntohs(client.sin_port);
    
                    //5.获取新连接成功,开始进行业务处理
                    std::cout << "获取新连接成功" << sock << "from" << listen_socket_ << std::endl;
                    
    
                    //v4线程池
                    Task t(sock, clientip, clientport, std::bind(&TcpServer::service, this, std::placeholders::_1, std::placeholders::_2,
                    std::placeholders::_3));
                    ThreadPool<Task>::Getinstance()->TaskPush(t);
                }
            }
    
    
            static void* threadRoutine(void* args)
            {
                //分离线程,防止主进程阻塞的情况
                pthread_detach(pthread_self());
    
                ThreadData* td = static_cast<ThreadData*>(args);
                td->current->service(td->sock, td->clientip, td->clientport);
    
                delete td;
                return nullptr;
            }
    
            void service(int sock, std::string ip, uint16_t port)
            {
                std::string who = ip + "-" + std::to_string(port);
    
                char buffer[1024];
                while(true)
                {
                    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
                    if(s > 0)
                    {
                        buffer[s] = 0;
                        //执行回调函数
                        std::string res = func_(buffer);
                        std::cout << who << ">>" << res << std::endl; 
                        write(sock, res.c_str(), res.size());
                    }
                    else if(s == 0)
                    {
                        //表示对应的远端关闭了通信
                        close(sock);
                        std::cout << "quit" << std::endl;
                        break;
                    }
                    else // s < 0 
                    {   
                        close(sock);
                        std::cerr << "read error" << std::endl;   
                        break;
                    }
                }
            }
    
    
            ~TcpServer()
            {
    
            }
        private:
            uint16_t port_;
            int listen_socket_;
            bool quit_; //表示当前服务器是否运行
            func_t func_;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183

    改进:为了服务更多的用户

    #pragma once
    
    
    using namespace std;
    
    #include
    #include
    #include
    #include
    #include
    #include
    
    #include 
    #include 
    #include 
    #include 
    
    #include"Thread.hpp"
    #include"LockGuard.hpp"
    #include"err.hpp"
    #include"Task.hpp"
    #include"ThreadPool.hpp"
    
    namespace ns_server
    {
        // signal(SIGCHLD, SIG_IGN); // 我最推荐的
    
        static const uint16_t defaultport = 8081; 
        static const int backlog = 32; 
        using func_t = std::function<std::string(const std::string&)>; 
    
        class TcpServer;
    
        class ThreadData
        {
        public:
            ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer* ts):
            sock(fd),
            clientip(ip),
            clientport(port),
            current(ts)
            {}
    
        public:
            int sock;
            std::string clientip;
            uint16_t clientport;
            TcpServer* current;
        };
    
    
    
    
    
        class TcpServer
        {
        public:
            TcpServer(func_t func, uint16_t port = defaultport):func_(func), port_(port), quit_(true)
            {
    
            }
            
            void Initserver()
            {   
                //1.创建socket文件
                listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listen_socket_ < 0)
                {
                    std::cerr<< "create socket error" << std::endl;
                    exit(SOCKET_ERR);
                }
    
                //2.创建对应的socket结构体,并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 = htonl(INADDR_ANY);
    
                if(bind(listen_socket_, (struct sockaddr*)&local, sizeof(local)) < 0)
                {
                    std::cerr<< "bind socket error" << std::endl;
                    exit(BIND_ERR);               
                }
                
                //3.监听
                if(listen(listen_socket_, backlog) < 0)
                {
                    std::cerr<< "listen error" << std::endl;
                    exit(LISTEN_ERR);                    
                }
            }
            void start()
            {   
                quit_ = false;
                while(true)
                {
                    //4.获取连接
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
    
                    std::cout << "我正在等待连接" << std::endl;
                    int sock = accept(listen_socket_, (struct sockaddr*)&client, &len);
                    if(sock < 0)
                    {
                        std::cerr << "accept error" << std::endl;   
                        continue;
                    }
    
                    std::string clientip = inet_ntoa(client.sin_addr);
                    uint16_t clientport = ntohs(client.sin_port);
    
                    //5.获取新连接成功,开始进行业务处理
                    std::cout << "获取新连接成功" << sock << "from" << listen_socket_ << std::endl;
    
                    //v4线程池
                    Task t(sock, clientip, clientport, std::bind(&TcpServer::service, this, std::placeholders::_1, std::placeholders::_2,
                    std::placeholders::_3));
                    ThreadPool<Task>::Getinstance()->TaskPush(t);
                }
            }
    
    
            static void* threadRoutine(void* args)
            {
                //分离线程,防止主进程阻塞的情况
                pthread_detach(pthread_self());
    
                ThreadData* td = static_cast<ThreadData*>(args);
                td->current->service(td->sock, td->clientip, td->clientport);
    
                delete td;
                return nullptr;
            }
    
            void service(int sock, std::string ip, uint16_t port)
            {
                std::string who = ip + "-" + std::to_string(port);
    
                char buffer[1024];
    
                ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
                if (s > 0)
                {
                    buffer[s] = 0;
                    // 执行回调函数
                    std::string res = func_(buffer);
                    std::cout << who << ">>" << res << std::endl;
                    write(sock, res.c_str(), res.size());
                }
                else if (s == 0)
                {
                    // 表示对应的远端关闭了通信
                    std::cout << "quit" << std::endl;
                }
                else // s < 0
                {
                    std::cerr << "read error" << std::endl;
                }
    
                close(sock);
            }
    
    
            ~TcpServer()
            {
    
            }
        private:
            uint16_t port_;
            int listen_socket_;
            bool quit_; //表示当前服务器是否运行
            func_t func_;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177

    1.5V5版本

    加入一个简单的日志信息,我们尝试用这个函数去实现输出我们的错误信息或者其他信息。

    //log.hpp
    #pragma once
    
    #include
    #include
    #include
    #include
    #include
    
    enum
    {
        Debug = 0,
        Info,
        Warning,
        Error,
        Fatal,
        Uknown
    };
    
    
    std::string LogLevel(int level)
    {
        switch(level)
        {
        case Debug:
            return "Debug";
        case Info:
            return "Info";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "Uknown";
        }
    }
    
    
    std::string GetTime()
    {
        time_t curr = time(nullptr);
        struct tm* tmp = localtime(&curr);
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d", tmp->tm_year + 1900, tmp->tm_mon + 1, 
        tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
    
        return buffer;
    }
    
    // 日志格式: 日志等级 时间 pid 消息体
    
    void LogMessage(int level, const char* format, ...)
    {
        //1.等级
        //2.时间
        //3.格式化
        //4.打印
        char Logleft[1024];
        std::string level_string = LogLevel(level);
        std::string cur_time = GetTime();
        snprintf(Logleft, sizeof Logleft, "[%s][%s][%d] ", level_string.c_str(), cur_time.c_str(), getpid());
    
    
        char Logright[1024];
        va_list p;
        va_start(p, format);
        vsnprintf(Logright, sizeof(Logright), format, p);
        va_end(p);
    
    
        printf("%s%s\n", Logleft, Logright);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    实例代码:

    //server.hpp
    #pragma once
    
    using namespace std;
    
    #include
    #include
    #include
    #include
    #include
    #include
    
    #include 
    #include 
    #include 
    #include 
    
    #include"log.hpp"
    #include"Thread.hpp"
    #include"LockGuard.hpp"
    #include"err.hpp"
    #include"Task.hpp"
    #include"ThreadPool.hpp"
    
    namespace ns_server
    {
        // signal(SIGCHLD, SIG_IGN); // 我最推荐的
    
        static const uint16_t defaultport = 8081; 
        static const int backlog = 32; 
        using func_t = std::function<std::string(const std::string&)>; 
    
        class TcpServer;
    
        class ThreadData
        {
        public:
            ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer* ts):
            sock(fd),
            clientip(ip),
            clientport(port),
            current(ts)
            {}
    
        public:
            int sock;
            std::string clientip;
            uint16_t clientport;
            TcpServer* current;
        };
    
        class TcpServer
        {
        public:
            TcpServer(func_t func, uint16_t port = defaultport):func_(func), port_(port), quit_(true)
            {
    
            }
            
            void Initserver()
            {   
                //1.创建socket文件
                listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listen_socket_ < 0)
                {
                    // std::cerr<< "create socket error" << std::endl;
                    LogMessage(Error, "create socket error");
                    exit(SOCKET_ERR);
                }
    
                //2.创建对应的socket结构体,并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 = htonl(INADDR_ANY);
    
                if(bind(listen_socket_, (struct sockaddr*)&local, sizeof(local)) < 0)
                {
                    //std::cerr<< "bind socket error" << std::endl;
                    LogMessage(Error, "bind socket error");                
                    exit(BIND_ERR);               
                }
                
                //3.监听
                if(listen(listen_socket_, backlog) < 0)
                {
                    //std::cerr<< "listen error" << std::endl;
                    LogMessage(Error, "listen error");
                    exit(LISTEN_ERR);                    
                }
            }
            void start()
            {   
                quit_ = false;
                while(true)
                {
                    //4.获取连接
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
    
                    // std::cout << "我正在等待连接" << std::endl;
                    LogMessage(Info, "我正在等待连接");
    
                    int sock = accept(listen_socket_, (struct sockaddr*)&client, &len);
                    if(sock < 0)
                    {
                        // std::cerr << "accept error" << std::endl;  
                        LogMessage(Error, "accept error"); 
                        continue;
                    }
    
                    std::string clientip = inet_ntoa(client.sin_addr);
                    uint16_t clientport = ntohs(client.sin_port);
    
                    //v4线程池
                    Task t(sock, clientip, clientport, std::bind(&TcpServer::service, this, std::placeholders::_1, std::placeholders::_2,
                    std::placeholders::_3));
                    ThreadPool<Task>::Getinstance()->TaskPush(t);
                }
            }
    
    
            static void* threadRoutine(void* args)
            {
                //分离线程,防止主进程阻塞的情况
                pthread_detach(pthread_self());
    
                ThreadData* td = static_cast<ThreadData*>(args);
                td->current->service(td->sock, td->clientip, td->clientport);
    
                delete td;
                return nullptr;
            }
    
            void service(int sock, std::string ip, uint16_t port)
            {
                std::string who = ip + "-" + std::to_string(port);
    
                char buffer[1024];
    
                ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
                if (s > 0)
                {
                    buffer[s] = 0;
                    // 执行回调函数
                    std::string res = func_(buffer);
                    // std::cout << who << ">>" << res << std::endl;
                    LogMessage(Info, "%s >>> %s", who.c_str(), res.c_str()); 
                    write(sock, res.c_str(), res.size());
                }
                else if (s == 0)
                {
                    // 表示对应的远端关闭了通信
                    // std::cout << "quit" << std::endl;
                    LogMessage(Info, "quit"); 
                }
                else // s < 0
                {
                    // std::cerr << "read error" << std::endl;
                    LogMessage(Error, "read error"); 
    
                }
    
                close(sock);
            }
    
    
            ~TcpServer()
            {
    
            }
        private:
            uint16_t port_;
            int listen_socket_;
            bool quit_; //表示当前服务器是否运行
            func_t func_;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181

    2.守护进程

    当我们在Linux命令行中输入ps -axj的时候

    [mi@lavm-5wklnbmaja tcp]$ ps -axj
     PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        0     1     1     1 ?           -1 Ss       0  74:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
        0     2     0     0 ?           -1 S        0   0:13 [kthreadd]
        2     3     0     0 ?           -1 S        0   1:08 [ksoftirqd/0]
        2     5     0     0 ?           -1 S<       0   0:00 [kworker/0:0H]
        2     7     0     0 ?           -1 S        0   0:38 [migration/0]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    那么表头的这些信息都是什么含义呢?

    1. PPID(Parent Process ID): 父进程的进程ID。它指示了启动当前进程的进程的ID。

    2. PID(Process ID): 当前进程的进程ID。它是唯一标识当前正在运行的进程的数字。

    3. PGID(Process Group ID): 进程组的ID。进程组是一个或多个进程的集合,它们共享一个组ID,并且通常由作业控制系统使用。

    4. SID(Session ID): 会话的ID。会话是一组相关进程的集合,通常由一个用户登录到系统时创建。所有进程都属于一个会话,并且每个会话都有一个唯一的会话ID。

    5. TTY(Controlling Terminal): 控制终端的名称。对于与终端相关的进程,此字段将显示其所属的终端设备的名称。

      其实它也是一个文件,所以Linux下一切皆文件,我们也可以直接向该文件中写入来实现一些神奇的功能。

    6. TPGID(Foreground Process Group ID): 前台进程组的ID。在前台运行的进程组将接收终端输入。

    7. STAT(Process State): 进程的状态。常见的状态包括:

      • R:运行(running)
      • S:睡眠(sleeping)
      • D:不可中断的睡眠(uninterruptible sleep)
      • T:停止(stopped)
      • Z:僵尸(zombie)
      • <:高优先级
      • N:低优先级
      • L:有些页面被锁入内存
    8. UID(User ID): 进程所有者的用户ID。它标识了启动进程的用户。

    9. TIME(CPU Time): 进程占用CPU的总时间。它包括用户态和内核态的时间。

    10. COMMAND(Command): 启动进程的命令行。这是启动进程时使用的完整命令行。

    我们再详细了解一下会话ID -> SID

    Session ID(会话ID,SID)是一个与Linux系统中会话(session)相关联的唯一标识符。在Linux中,会话是一组相关联的进程的集合,通常由一个用户登录到系统时创建。会话ID被用来标识和管理这些进程组。

    1. 创建会话: 当用户登录到系统时,通常会创建一个新的会话。这个会话会包含用户的登录Shell以及与之关联的其他进程。会话的ID由内核自动分配。
    2. 进程组和会话: 在Linux中,进程可以被分组成进程组(process group),而进程组则可以被组合成会话。每个进程组都有一个唯一的ID(PGID),而会话则有一个唯一的会话ID(SID)。
    3. setsid系统调用: setsid是一个系统调用,用于创建一个新的会话并将进程设置为会话的领头进程。当进程调用setsid时,它会脱离当前的会话,创建一个新的会话,并成为该会话的领头进程(session leader)。
    4. Shell会话: 用户登录到系统时通常会打开一个Shell会话。这个Shell会话将会有一个对应的会话ID(SID),而该会话的所有进程都会属于这个会话。
    5. 控制终端: 会话可以关联一个控制终端(controlling terminal)。控制终端是用户与系统交互的接口,例如终端或SSH会话。与会话相关的进程通常会在控制终端上接收输入和发送输出。
    6. 终端控制: 控制终端可以控制会话中的前台进程组(foreground process group),这些进程组将接收终端输入。会话中的其他进程组通常在后台运行。

    2.1前置知识

    • jobs命令:

      1. jobs:简单地列出当前shell会话中的所有后台任务。
      2. jobs -l:以长格式列出后台任务,包括任务号(job number)、进程号(PID)、状态以及命令。
      3. jobs -p:仅打印后台任务的进程号(PID)。
      4. jobs -r:仅列出正在运行中的后台任务。
      5. jobs -s:仅列出已停止的后台任务。
      6. jobs %n:列出指定作业号(job number)为n的后台任务。
      7. fg %n:将指定作业号(job number)为n的后台任务调回前台运行。
      8. bg %n:将指定作业号(job number)为n的后台任务在后台继续运行。

      在这些选项中,%n表示作业号,可以通过jobs命令或在任务启动时由shell分配的作业号来指定。

    image-20240313084329227

    • setsid

    setsid不仅是一个命令行工具,还是一个系统调用(system call)。在C语言中,您可以使用setsid系统接口来执行与命令行setsid相同的功能。

    setsid系统调用的原型如下:

    #include 
    
    pid_t setsid(void);
    
    • 1
    • 2
    • 3

    这个系统调用会创建一个新的会话(session)并将调用进程设置为该会话的领头进程(session leader)。如果调用进程已经是一个会话的领头进程,setsid调用会失败并返回-1。否则,它会返回一个新会话的会话ID。

    一般情况下,setsid系统调用是在创建守护进程**(daemon)**时使用的。当创建守护进程时,为了脱离终端会话的控制,并确保即使终端关闭它也能继续运行,可以使用setsid来创建一个新的会话。

    以下是一个简单的示例,演示了如何在C语言中使用setsid系统调用来创建一个守护进程:

    #include 
    #include 
    #include 
    
    int main() {
        pid_t pid = fork();
        
        if (pid < 0) {
            // fork失败
            perror("fork");
            exit(EXIT_FAILURE);
        }
        
        if (pid > 0) {
            // 父进程退出
            exit(EXIT_SUCCESS);
        }
        
        // 子进程
        umask(0); // 设置文件权限掩码
        chdir("/"); // 切换工作目录
        close(STDIN_FILENO); // 关闭标准输入
        close(STDOUT_FILENO); // 关闭标准输出
        close(STDERR_FILENO); // 关闭标准错误输出
        
        // 创建新会话
        if (setsid() < 0) {
            perror("setsid");
            exit(EXIT_FAILURE);
        }
        
        // 此时,该进程已经是一个守护进程,可以在后台持续运行
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    这段代码创建了一个守护进程,通过调用setsid来创建一个新会话,并将该进程设置为会话的领头进程

    2.2我们实现一个守护进程

    在Linux环境下实现守护进程(Daemon)的过程通常涉及以下步骤和细节:

    1. 创建子进程: 首先,通过调用fork函数创建一个子进程。父进程退出,而子进程继续执行后续步骤。
    2. 创建新会话: 在子进程中,调用setsid函数创建一个新的会话,并使其成为该会话的领头进程。这个步骤将使守护进程摆脱终端的控制,即使终端关闭,守护进程也能继续运行。
    3. 更改工作目录: 为了防止守护进程占用任何挂载的文件系统,通常将工作目录切换到根目录。这可以通过调用chdir("/")函数来实现。
    4. 关闭文件描述符: 为了避免守护进程与终端会话关联,并且不受任何终端输入输出的影响,需要关闭标准输入、标准输出和标准错误输出。这可以通过调用close(STDIN_FILENO)close(STDOUT_FILENO)close(STDERR_FILENO)函数来实现。
    5. 重定向标准输入输出: 可选地,您可以将标准输入、标准输出和标准错误输出重定向到/dev/null或其他适当的文件描述符,以防止守护进程产生不必要的输出或错误。例如,可以使用opendup2函数来实现这一点。
    6. 处理信号: 守护进程通常需要处理一些信号,例如SIGTERM(终止信号)和SIGINT(中断信号),以便在关闭守护进程时进行清理操作。可以使用signal函数或sigaction函数来注册信号处理函数。
    7. 记录日志: 守护进程通常会记录其活动和状态,以便进行故障排除和监视。您可以使用系统日志(syslog)或自己的日志文件来记录守护进程的日志。
    8. 其他细节: 在实现守护进程时,还应考虑一些其他细节,如创建锁文件以防止多个守护进程同时运行,设置适当的文件权限,以及在启动时检查守护进程是否已经在运行。
    //daemon.hpp
    #pragma once
    
    #include
    #include
    #include
    #include
    #include 
    #include 
    #include 
    
    #include"log.hpp"
    
    
    
    void Daedmon()
    {
    
        //1.忽略信号
        signal(SIGPIPE, SIG_IGN);
        signal(SIGCHLD, SIG_IGN);
    
        //2.让自己不成为组长
        if(fork() > 0) exit(0);
    
        //3.新建会话让自己成为新会话的新进程
    
        pid_t ret = setsid();
        if(ret < 0)
        {
            LogMessage(Error, "setsid error ,error code: %d - %s", errno, strerror(errno));
        }
    
        //4.(可选)可以更改守护进程的工作进程
    
    
        //5.处理后续的对于文件描述符012的问题
        int fd = open("/dev/null", O_RDWR);
        if(fd < 0)
        {
            LogMessage(Error, "open /dev/null error ,error code: %d - %s", errno, strerror(errno));
        }
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    至此我们已经可以实现我们的server在后台执行,并且当我们关闭xshell会话的时候,也并不会引起服务的关闭,一遍我们随时去访问。

    到这本篇博客的内容就到此结束了。
    如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
    如果文章内容有错误,欢迎在评论区指正

    在这里插入图片描述

  • 相关阅读:
    互联网Java工程师面试题·Spring篇·第一弹
    真正牛的项目经理,都做到了这几点
    MySQL数据库的事务
    Java多线程/spring boot多线程
    大数据Flink(六十七):SQL & Table 简介及运行环境
    JavaScript+Jquery知识点速查
    python单例模式的使用
    【C++面向对象侯捷】8.栈,堆和内存管理
    API接口采集商品评论数据,item_review-获得淘宝商品评论
    字符串的扩展
  • 原文地址:https://blog.csdn.net/weixin_61766635/article/details/136669514