一个简易的基于线程池的TCP的任务处理

  • 基于队列的线程池
#pragma once
#include <iostream>
#include <cassert>
#include <queue>
#include <pthread.h>
#include <sys/prctl.h>
#include "Log.hpp"
#include "Lock.hpp"
#include "Task.hpp"

using namespace std;

const int gThreadNum = 5;

template <class T>
class threadPool
{
private:
    threadPool(int num = gThreadNum) : threadNums(num), isStart(false)
    {
        assert(threadNums > 0);
        // 初始化互斥锁和条件变量
        pthread_mutex_init(&mutex, nullptr);
        pthread_cond_init(&cond, nullptr);
    }
    // 单例模式  禁止拷贝构造和赋值操作
    threadPool(const threadPool<T> &t) = delete;
    threadPool<T> operator=(const threadPool<T> &t) = delete;

public:
    // 懒汉模式
    static threadPool<T> *getInstance()
    {
        // 初始化锁
        static Mutex m;
        if (nullptr == instance) // 双重判定空指针, 降低锁冲突的概率, 提高性能。
        {
            // RAII思想
            GuardLock guard(&m);
            if (nullptr == instance)
                instance = new threadPool<T>();
        }
        return instance;
    }

    ~threadPool()
    {
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);
    }

    // 线程启动后执行的函数  传入了this指针
    static void *taskRoutine(void *args)
    {
        pthread_detach(pthread_self()); // 分离线程  自动回收
        threadPool<T> *tp = static_cast<threadPool<T> *>(args);
        // prctl(PR_SET_NAME, "follower");//修改线程名称
        while (1)
        {
            // 队列中拿取任务
            tp->lockQueue();
            while (!tp->haveTask())
            {
                // 等待条件信号量
                tp->waitForTask();
            }
            T t = tp->pop();
            tp->unlockQueue();
            t(); // 让指定的线程执行任务
        }
    }

    void start()
    {
        assert(!isStart);
        // 根据线程池中的线程数量创建线程
        for (size_t i = 0; i < threadNums; i++)
        {
            pthread_t temp; // 输出型参数  线程ID
            pthread_create(&temp, nullptr, taskRoutine, this);
        }
        isStart = true;
    }

    // 添加任务
    void push(const T &in)
    {
        lockQueue();
        taskQueue.push(in);
        // 唤醒条件信号量  任务队列里面来任务了,线程可以开始处理任务了
        choiceThreadForHandler();
        unlockQueue();
    }

    int getThreadNum()
    {
        return threadNums;
    }

private:
    void lockQueue() { pthread_mutex_lock(&mutex); }
    void unlockQueue() { pthread_mutex_unlock(&mutex); }
    bool haveTask() { return !taskQueue.empty(); }
    void waitForTask() { pthread_cond_wait(&cond, &mutex); }
    void choiceThreadForHandler() { pthread_cond_signal(&cond); }

    // 获取任务
    T pop()
    {
        T temp = taskQueue.front();
        taskQueue.pop();
        return temp;
    }

private:
    bool isStart;
    int threadNums;
    queue<T> taskQueue;
    pthread_mutex_t mutex;
    pthread_cond_t cond;

    static threadPool<T> *instance;
};

template <class T>
threadPool<T> *threadPool<T>::instance;
  • 简单的任务类(通过回调函数创建并处理任务)
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include "Log.hpp"

using namespace std;

class task
{
public:
    // 创建函数对象
    using called_func = function<void(int, string, uint16_t)>;

    task(int s, string i, int p, called_func f) : sock(s), ip(i), port(p), func(f) {}
    ~task() {}

    // 重载operator()
    void operator()()
    {
        LogMessage(DEBUG, "线程ID[%p]处理%s:%d的请求 开始啦...", pthread_self(), ip.c_str(), port);
        func(sock, ip, port);
        LogMessage(DEBUG, "线程ID[%p]处理%s:%d的请求 结束啦...", pthread_self(), ip.c_str(), port);
    }

private:
    string ip;
    uint16_t port;
    int sock;
    called_func func; // 回调函数
};
  • 工具类(互斥锁类、打印日志文件类、创建守护进程类)
    #include <iostream>
    #include <pthread.h>
    
    using namespace std;
    
    class Mutex
    {
    public:
        Mutex()
        {
            pthread_mutex_init(&_lock, nullptr);
        }
    
        void lock()
        {
            pthread_mutex_lock(&_lock);
        }
    
        void unlock()
        {
            pthread_mutex_unlock(&_lock);
        }
    
        ~Mutex()
        {
            pthread_mutex_destroy(&_lock);
        }
    
    private:
        pthread_mutex_t _lock;
    };
    
    class GuardLock
    {
    public:
        GuardLock(Mutex *mutex) : _mutex(mutex)
        {
            _mutex->lock();
            cout << "加锁成功..." << endl;
        }
    
        ~GuardLock()
        {
            _mutex->unlock();
            cout << "解锁成功..." << endl;
        }
    
    private:
        Mutex *_mutex;
    };
    #pragma once
    
    #include <cassert>
    #include <cstdarg>
    #include <cstring>
    #include <ctime>
    #include <cerrno>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    #define DEBUG 0
    #define NOTICE 1
    #define WARINING 2
    #define FATAL 3
    
    #define LOGFILE "serverTcp.log"
    
    const char *log_level[] = {"DEBUG", "NOTICE", "WARINING", "FATAL"};
    
    // 创建serverTcp.log日志文件  将控制台输出全部重定向到文件中
    // 在Log类对象被销毁时,进行刷盘操作
    class Log
    {
    public:
        Log() : logFd(-1)
        {
        }
        void enable()
        {
            umask(0);
            logFd = open(LOGFILE, O_WRONLY | O_CREAT | O_APPEND, 0666);
            assert(logFd > 0);
            dup2(logFd, 1);
            dup2(logFd, 2);
        }
        ~Log()
        {
            if (logFd > 0)
            {
                fsync(logFd);
                close(logFd);
            }
        }
    
    private:
        int logFd;
    };
    
    // 打印日志信息
    void LogMessage(int level, const char *format, ...)
    {
        assert(level >= DEBUG);
        assert(level <= FATAL);
    
        char *name = getenv("USER");
    
        char buff[1024];
        va_list v;
        va_start(v, format);
    
        vsnprintf(buff, sizeof(buff) - 1, format, v);
    
        va_end(v);
    
        FILE *out = (level == FATAL) ? stderr : stdout;
    
        // umask(0);
        // int fd = open(LOGFILE, O_WRONLY | O_CREAT | O_APPEND, 0666);
        // assert(fd > 0);
    
        fprintf(out, "%s | %u | %s | %s\n",
                log_level[level],
                (unsigned int)time(nullptr),
                name == nullptr ? "unknow" : name,
                buff);
    
        // dup2(fd, 1);
        // dup2(fd, 2);
    
        fflush(out);
    
        // fsync(fd);
    
        // close(fd);
    }
    
    #include <signal.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    void daemonize()
    {
        // 打开linux的垃圾桶
        int fd = open("/dev/null", O_WRONLY);
        // 忽略SIGPIPE
        signal(SIGPIPE, SIG_IGN);
        // 创建子进程,然后关闭其父进程
        if (fork() > 0)
            exit(1);
        //将由BASH 1号进程接管的子进程设置成为独立会话
        setsid();
        
        if (fd != -1)
        {
            dup2(fd, 0);
            dup2(fd, 1);
            dup2(fd, 2);
            if (fd > 2)
                close(fd);
        }
    }
    #pragma once
    
    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cassert>
    #include <ctype.h>
    #include <unistd.h>
    #include <strings.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    using namespace std;
    
    #define SOCKET_ERR 1
    #define BIND_ERR   2
    #define LISTEN_ERR 3
    #define USAGE_ERR  4
    #define CONN_ERR   5
    
    #define BUFFER_SIZE 1024

    TCPserver端

    #include "util.hpp"
    #include "Log.hpp"
    #include "ThreadPool.hpp"
    #include "daemonize.hpp"
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <pthread.h>
    
    class Server;
    
    class ThreadData
    {
    public:
        uint16_t clientPort_;
        std::string clinetIp_;
        int sock_;
        Server *this_;
    
    public:
        ThreadData(uint16_t port, std::string ip, int sock, Server *ts)
            : clientPort_(port), clinetIp_(ip), sock_(sock), this_(ts)
        {
        }
    };
    
    // 创建task时的传入函数
    void transService(int sock, const string &ip, int port)
    {
        assert(sock >= 0);
        assert(!ip.empty());
        assert(port >= 1024);
        char buffer[BUFFER_SIZE];
        while (true)
        {
            ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
            if (s > 0)
            {
                buffer[s] = '\0';
                if (strcasecmp(buffer, "quit") == 0)
                {
                    LogMessage(DEBUG, "client quit -- %s[%d]", ip.c_str(), port);
                    break;
                }
                LogMessage(DEBUG, "trans before: %s[%d]>>> %s", ip.c_str(), port, buffer);
                for (int i = 0; i < s; i++)
                {
                    if (isalpha(buffer[i]) && islower(buffer[i]))
                        buffer[i] = toupper(buffer[i]);
                }
                LogMessage(DEBUG, "trans after: %s[%d]>>> %s", ip.c_str(), port, buffer);
                write(sock, buffer, strlen(buffer));
            }
            else if (s == 0)
            {
                LogMessage(DEBUG, "client quit -- %s[%d]", ip.c_str(), port);
                break;
            }
            else
            {
                LogMessage(DEBUG, "%s[%d] - read: %s", ip.c_str(), port, strerror(errno));
                break;
            }
        }
        close(sock);
        LogMessage(DEBUG, "server close %d done", sock);
    }
    // 创建task时的传入函数
    void commandService(int sock, const string &ip, int port)
    {
        assert(sock >= 0);
        assert(!ip.empty());
        assert(port >= 1024);
        char buffer[BUFFER_SIZE];
        while (true)
        {
            ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
            if (s > 0)
            {
                buffer[s] = 0;
                LogMessage(DEBUG, "[%s:%d] exec [%s]", ip.c_str(), port, buffer);
                FILE *f = popen(buffer, "r");
                if (f == nullptr)
                {
                    LogMessage(WARINING, "exec %s failed, beacuse: %s", buffer, strerror(errno));
                }
                char line[1024];
                while (fgets(line, sizeof(line), f) != nullptr)
                {
                    write(sock, line, sizeof(line));
                }
                pclose(f);
                LogMessage(DEBUG, "[%s:%d] exec [%s] ... done", ip.c_str(), port, buffer);
            }
            else if (s == 0)
            {
                LogMessage(DEBUG, "client quit -- %s[%d]", ip.c_str(), port);
                break;
            }
            else
            {
                LogMessage(DEBUG, "%s[%d] - read: %s", ip.c_str(), port, strerror(errno));
                break;
            }
        }
        close(sock);
        LogMessage(DEBUG, "server close %d done", sock);
    }
    
    class Server
    {
    public:
        Server(int port, string ip = "") : port_(port), ip_(ip), listenSock_(-1) {}
        ~Server() {}
    
        void init()
        {
            // 创建TCP套接字
            listenSock_ = socket(AF_INET, SOCK_STREAM, 0);
            if (listenSock_ < 0)
            {
                LogMessage(FATAL, "socket: %s", strerror(errno));
                exit(SOCKET_ERR);
            }
            LogMessage(DEBUG, "socket success: %s , %d", strerror(errno), listenSock_);
    
            struct sockaddr_in local;
            socklen_t len = sizeof(local);
            memset(&local, 0, sizeof(local));
            local.sin_port = htons(port_);
            local.sin_family = PF_INET;
            ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(ip_.c_str(), &local.sin_addr));
            // 绑定套接字
            if (bind(listenSock_, (sockaddr *)&local, len) < 0)
            {
                LogMessage(FATAL, "bind: %s", strerror(errno));
                exit(BIND_ERR);
            }
            LogMessage(DEBUG, "bind success : %s %d", strerror(errno), listenSock_);
            // 监听套接字  最大连接数5
            if (listen(listenSock_, 5) < 0)
            {
                LogMessage(FATAL, "listen: %s", strerror(errno));
                exit(LISTEN_ERR);
            }
            LogMessage(DEBUG, "listen succcess: %s , %d", strerror(errno), listenSock_);
    
            // 加载线程池
            tp = threadPool<task>::getInstance();
        }
    
        // static void *thradRoutine(void *args)
        // {
        //     pthread_detach(pthread_self());
        //     ThreadData *td = (ThreadData *)args;
        //     td->this_->transService(td->sock_, td->clinetIp_, td->clientPort_);
        //     delete td;
        //     return nullptr;
        // }
    
        void loop()
        {
            // signal(SIGCHLD, SIG_IGN);
            // 初始化线程池 创建线程
            tp->start();
            LogMessage(DEBUG, "thread pool start success, thread num: %d", tp->getThreadNum());
    
            while (true)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                // 接收请求连接 serviceSock服务套接字
                int serviceSock = accept(listenSock_, (sockaddr *)&peer, &len);
                if (serviceSock < 0)
                {
                    LogMessage(WARINING, "accept: %s , %d", strerror(errno), serviceSock);
                    continue;
                }
                uint16_t peerPort = ntohs(peer.sin_port);
                string peerIp = inet_ntoa(peer.sin_addr);
                LogMessage(DEBUG, "accept success: %s | %s[%d], socket fd: %d",
                           strerror(errno), peerIp.c_str(), peerPort, serviceSock);
    
                // demo 5 线程池
                //  5.1
                //  task t(serviceSock, peerIp, peerPort, std::bind(&Server::transService, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                //  tp->push(t);
                //  5.2
                //  task t(serviceSock, peerIp, peerPort,transService);
                //  tp->push(t);
                //  5.3
                // 创建任务
                task t(serviceSock, peerIp, peerPort, commandService);
                // 将任务放入任务队列中
                tp->push(t);
    
                // demo 4 多线程
                // ThreadData *td = new ThreadData(peerPort, peerIp, serviceSock, this);
                // pthread_t pt;
                // pthread_create(&pt, nullptr, thradRoutine, (void *)td);
    
                // demo3 多进程
                // pid_t id = fork();
                // if (id == 0)
                // {
                //     close(listenSock_);
                //     if (fork() > 0)
                //         exit(0);
                //     transService(serviceSock, peerIp, peerPort);
                //     exit(0);
                // }
                // close(serviceSock);
                // pid_t ret = waitpid(id, nullptr, 0);
                // assert(ret > 0);
                // (void)ret;
    
                // demo2 多进程
                // pid_t id = fork();
                // if(id == 0){
                //     close(listenSock_);
                //     transService(serviceSock,peerIp,peerPort);
                //     exit(0);
                // }
                // close(serviceSock);
                // 通过signal(SIGCHLD,SIG_IGN)来处理僵尸子进程
    
                // demo 1
                // transService(serviceSock, peerIp, peerPort);
    
                // LogMessage(DEBUG, "server 提供 service start ...");
                // sleep(1);
            }
        }
    
        // 5.1
        // void transService(int sock, const string &ip, int port)
        // {
        //     assert(sock >= 0);
        //     assert(!ip.empty());
        //     assert(port >= 1024);
        //     char buffer[BUFFER_SIZE];
        //     while (true)
        //     {
        //         ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        //         if (s > 0)
        //         {
        //             buffer[s] = '\0';
        //             if (strcasecmp(buffer, "quit") == 0)
        //             {
        //                 LogMessage(DEBUG, "client quit -- %s[%d]", ip.c_str(), port);
        //                 break;
        //             }
        //             LogMessage(DEBUG, "trans before: %s[%d]>>> %s", ip.c_str(), port, buffer);
        //             for (int i = 0; i < s; i++)
        //             {
        //                 if (isalpha(buffer[i]) && islower(buffer[i]))
        //                     buffer[i] = toupper(buffer[i]);
        //             }
        //             LogMessage(DEBUG, "trans after: %s[%d]>>> %s", ip.c_str(), port, buffer);
        //             write(sock, buffer, strlen(buffer));
        //         }
        //         else if (s == 0)
        //         {
        //             LogMessage(DEBUG, "client quit -- %s[%d]", ip.c_str(), port);
        //             break;
        //         }
        //         else
        //         {
        //             LogMessage(DEBUG, "%s[%d] - read: %s", ip.c_str(), port, strerror(errno));
        //             break;
        //         }
        //     }
        //     close(sock);
        //     LogMessage(DEBUG, "server close %d done", sock);
        // }
    
    private:
        uint16_t port_;
        string ip_;
        int listenSock_;
        threadPool<task> *tp;
    };
    
    static void Usage(string s)
    {
        cout << "Usage: \n\t" << s << "port ip" << endl;
        cout << "ex:    \n\t" << s << "8080 127.0.0.1" << endl;
    }
    
    int main(int args, char *argv[])
    {
        if (args != 2 && args != 3)
        {
            Usage(argv[0]);
            exit(USAGE_ERR);
        }
        uint16_t p = atoi(argv[1]);
        string i;
        if (args == 3)
            i = argv[2];
    
        daemonize();
    
        Log log;
        log.enable();
    
        Server s(p, i);
        s.init();
        s.loop();
    
        return 0;
    }

    TCPclient端

    #include "util.hpp"
    #include "Log.hpp"
    
    static void Usage(string s)
    {
        cout << "Usage: \n\t" << s << "ip port" << endl;
        cout << "ex:    \n\t" << s << "127.0.0.1 8080" << endl;
    }
    
    volatile bool flag = false;
    
    int main(int args, char *argv[])
    {
        if (args != 3)
        {
            Usage(argv[0]);
            exit(USAGE_ERR);
        }
        string ip = argv[1];
        uint16_t port = atoi(argv[2]);
    
        // 创建套接字
        int clientSock = socket(AF_INET, SOCK_STREAM, 0);
        if (clientSock < 0)
        {
            cerr << "socket: " << strerror(errno) << endl;
            exit(SOCKET_ERR);
        }
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        socklen_t len = sizeof(server);
        server.sin_port = htons(port);
        server.sin_family = AF_INET;
        inet_aton(ip.c_str(), &server.sin_addr);
    
        // 请求连接
        if (connect(clientSock, (const sockaddr *)&server, len) == -1)
        {
            cerr << "connect: " << strerror(errno) << endl;
            exit(SOCKET_ERR);
        }
        cout << "info : connect success: " << clientSock << endl;
    
        // 与服务器开始通信
        string message;
        while (!flag)
        {
            message.clear();
            cout << "Please Enter# ";
            getline(cin, message);
            if (strcasecmp(message.c_str(), "quit") == 0)
                flag = true;
            // 发送信息给服务器
            ssize_t s = write(clientSock, message.c_str(), message.size());
            // 发送成功
            if (s > 0)
            {
                message.resize(1024);
                // 读取服务器处理后发送过来的信息
                ssize_t s = read(clientSock, (char *)(message.c_str()), 1024);
                if (s > 0)
                    message[s] = 0;
                cout << "Server Echo>>> " << message << endl;
            }
            // 发送失败
            else if (s <= 0)
            {
                break;
            }
        }
        // 关闭套接字
        close(clientSock);
    
        return 0;
    }

相关推荐

  1. 一个简易基于线TCP任务处理

    2024-03-23 14:36:02       35 阅读
  2. 简易线实现

    2024-03-23 14:36:02       44 阅读
  3. 简单线示例

    2024-03-23 14:36:02       31 阅读
  4. 任务调度与执行C++线

    2024-03-23 14:36:02       24 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-23 14:36:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-23 14:36:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-23 14:36:02       82 阅读
  4. Python语言-面向对象

    2024-03-23 14:36:02       91 阅读

热门阅读

  1. PAT乙级 1054 求平均值 C语言

    2024-03-23 14:36:02       39 阅读
  2. Web 常见的攻击方式有哪些?

    2024-03-23 14:36:02       40 阅读
  3. flink-connector-redis支持select查询

    2024-03-23 14:36:02       35 阅读
  4. web蓝桥杯真题:布局切换

    2024-03-23 14:36:02       40 阅读
  5. arcpy一些常用的数据处理类

    2024-03-23 14:36:02       45 阅读
  6. SurfaceFlinger的面试题目

    2024-03-23 14:36:02       37 阅读
  7. 变量的数据类型:基本数据类型和引用数据类型

    2024-03-23 14:36:02       43 阅读
  8. 双雷达(velodyne)的启动与集成

    2024-03-23 14:36:02       40 阅读
  9. 关于系统部署测试人员的10道经典面试题

    2024-03-23 14:36:02       35 阅读
  10. HTTP Header Fields

    2024-03-23 14:36:02       40 阅读
  11. 【Unity】Stream最好用的Selfhost开源轻量服务

    2024-03-23 14:36:02       46 阅读