• 【网络编程】C++实现网络通信服务器程序||计算机网络课设||Linux系统编程||TCP协议(附源码)


    0000

    🐍 1.程序简洁

    该程序用C++实现了一个支持并发的服务器,实现了大小写转换的功能,当客户端向服务器发起链接请求时,服务器响应请求,分配资源处理任务,实现大小写的转换功能,用到的相关技术栈有:C++编程、socket套接字编程、线程池等。

    🦎2. 服务端ServerTcp程序介绍

    0001

    在ServerTcp函数定义定义了一个init函数,用于配置服务器的网络监听套接字,绑定IP地址和端口,并开始监听来自客户端的连接请求。
    ①首先创建套接字:
    使用 函数创建一个套接字,这里使用的是IPv4()和TCP()协议。如果创建失败,会记录错误信息并退出程序。socketPF_INETSOCK_STREAM
    ②绑定地址和端口:
    创建一个 结构体变量 ,它用于保存服务器的本地地址信息。struct sockaddr_inlocal
    2.2. 清空 结构体,然后设置其成员变量,包括地址族(,设为 表示IPv4)、端口号(,通过 将主机字节序转换为网络字节序)、IP地址(),IP地址可以是配置的服务器IP或者通配地址 。localsin_familyPF_INETsin_porthtonssin_addr.s_addrINADDR_ANY
    2.3. 使用 函数将创建的套接字 与上述配置绑定。如果绑定失败,会记录错误信息并退出程序。bindlistenSock_
    ③监听套接字:
    使用 函数将套接字设置为监听状态,同时指定了允许等待连接的队列长度为5。如果监听失败,会记录错误信息并退出程序。listen
    ④加载线程池:
    在这里,代码通过 获取了一个线程池的单例对象,用于处理客户端的请求。这个线程池的类型是 ,这是一个自定义的任务类,它将在客户端连接时调用来处理请求。ThreadPool::getInstance()Task
    最后的注释解释了程序运行到这一步时,服务器已经配置完成,等待客户端的连接请求。

    0002

    这段代码是服务器的主循环函数 ,它是服务器的核心部分,用于不断接受客户端的连接请求,然后将连接的处理任务交给线程池来执行。
    ①打开线程池:
    使用 启动线程池。这个操作会让线程池中的线程开始运行,并等待任务的到来。tp_->start()
    ②记录线程池启动成功的日志:
    使用 记录一个 DEBUG 级别的日志,表示线程池启动成功,同时记录线程池中线程的数量。
    ③主循环:
    进入一个无限循环,用于不断接受客户端的连接请求。
    ④接受连接:
    使用 函数等待客户端的连接请求,当有客户端连接时, 返回一个新的套接字 ,用于与客户端通信。
    ⑤处理连接失败:
    如果 返回的套接字小于 0,表示连接失败,代码记录一个 WARNING 级别的日志,包含错误信息,然后继续等待下一个连接请求。
    ⑥获取客户端信息:
    6.1. 获取客户端的端口号和IP地址,并进行字节序的转换,以便后续日志记录和任务处理。
    6.2. 记录 DEBUG 级别的日志,表示接受到客户端的连接请求,同时包含连接信息和套接字文件描述符。
    创建任务对象:
    在这里,代码创建了一个 Task对象 ,这个对象用于包装客户端的套接字、IP地址、端口号以及服务处理的回调函数 。
    ⑦将任务加入线程池:
    使用 tp_->push(t);将任务 添加到线程池中等待执行。然后不断循环

    🦖3.线程池ThreadPool介绍

    0004

    该代码实现了一个简单的线程池类,当创建线程池实例时,调用start函数之后会创建15个线程(预先设定),当有任务通过push接口进入任务队列时,线程会执行threadRountine()函数,首先该判断该线程任务队列里有没有任务在执行,如果有,那就进行条件等待 void waitForTask() { pthread_cond_wait(&cond_, &mutex_); } ,线程池里支持15个线程同时执行处理任务的函数,当任务数超过15时才会出现等待,实现了多线程并发提高了任务执行效率.

    🦕 4.任务类Task介绍

    0005

    ‘Task’ 类的主要作用是将任务与处理该任务的函数关联起来,使任务的处理变得可定制化。当线程池从任务队列中取出任务时,会调用 ‘Task’ 对象的 ‘operator()’ 函数,进而执行与任务关联的回调函数,完成任务的处理。这种方式允许灵活地定义和执行不同类型的任务。

    🐙5. 客户端Client介绍

    0006

    这是一个简单的客户端程序,用于与服务器建立连接并进行通信。
    首先要判断运行客户端的方式必须是 接收3个参数 运行程序+ip+端口
    然后绑定协议族等信息,把接收的参数封装起来,与远端的服务器发起链接请求,进行通信,当输入的内容为”quit”时退出客户端.

    🦑6.运行结果:

    0007

    🦐 7. 源码

    🦞7.1 serverTcp.cc

    #include "util.hpp"
    #include 
    #include 
    #include 
    #include"Threadpool.hpp"
    #include 
    #include"Task.hpp"
    
     //服务函数  ---->>>小写转换大写
     void transService(int sock,const std::string &clientip,uint16_t clientPort)
        {
            assert(sock >= 0);
            assert(!clientip.empty());
            assert(clientPort >= 1024);
    
            char inbuffer[BUFFER_SIZE];
            while (true)
            {
                 ssize_t s=read(sock,inbuffer,sizeof(inbuffer)-1); //给“\0”留一个位置
                 if(s>0)
                 {
                    //读取成功
                    inbuffer[s]='\0';
                    if(strcasecmp(inbuffer,"quit")==0)
                    {
                        //如果输入quit直接退出
                        logMessage(DEBUG, "client quit -- %s[%d]", clientip.c_str(), clientPort);
                        break;
                    }
                    logMessage(DEBUG,"trans before: %s[%d]>>>%s",clientip.c_str(),clientPort,inbuffer);
                    //下面进行大小写转换 
                      for(int i = 0; i < s; i++)
                    {
                        if(isalpha(inbuffer[i]) && islower(inbuffer[i])) 
                            inbuffer[i] = toupper(inbuffer[i]);
                    }
                    logMessage(DEBUG, "trans after: %s[%d]>>> %s", clientip.c_str(), clientPort, inbuffer);
    
                    //把转换后的写回套接字
                    write(sock, inbuffer, strlen(inbuffer));
                 
                 }
                  else if (s == 0)
                {
                    logMessage(DEBUG, "client quit -- %s[%d]", clientip.c_str(), clientPort);
                    break;
                }
                else
                {
                    logMessage(DEBUG, "%s[%d] - read: %s", clientip.c_str(), clientPort, strerror(errno));
                    break;
                }
          
            }
              // 只要走到这里,一定是client退出了,服务到此结束
            close(sock); // 如果一个进程对应的文件fd,打开了没有被归还,文件描述符泄漏!
            logMessage(DEBUG, "server close %d done", sock);
            
        }
        
    template<class T>
    class ServerTcp; // 申明一下ServerTcp
    template<class T>
    class ThreadData
    {
    public:
        uint16_t clientPort_;
        std::string clinetIp_;
        int sock_;
        ServerTcp <T>*this_;
    public:
            ThreadData(uint16_t port, std::string ip, int sock,  ServerTcp<T> *ts)
                : clientPort_(port), clinetIp_(ip), sock_(sock),this_(ts)
            {}
    };
    template<class T>
    class ServerTcp
    {
        public:
        //构造
        ServerTcp(uint16_t port,const std::string &ip=""):port_(port),ip_(ip),listenSock_(-1),tp_(nullptr)
        {}
        //析构
        ~ServerTcp()
        {}
        public:
        void init() //初始化函数
        {
            //1.创建套接字
            listenSock_=socket(PF_INET,SOCK_STREAM,0);   //PF_INET=IPV4 SOCK_STREAM=TCP 最后一个参数是协议 默认是0
            if (listenSock_ < 0) //如果socket创建成功会返回一个非负整数
            {
                //创建失败
                logMessage(FATAL, "socket: %s", strerror(errno));
                exit(SOCKET_ERR);
            }
            logMessage(DEBUG, "socket: %s, %d", strerror(errno), listenSock_);
            //2.bind绑定
            //2.1填充服务器信息
            struct sockaddr_in local; // 用户栈
            memset(&local, 0, sizeof local);
            local.sin_family = PF_INET;//ipv4
            local.sin_port = htons(port_);//htons 主机字节序转网络字节序
            ip_.empty()?(local.sin_addr.s_addr=INADDR_ANY):(inet_aton(ip_.c_str(),&local.sin_addr));
            //2.2本地socket信息 写入socket_t对应区域 
            if(bind(listenSock_,(const struct sockaddr*)&local,sizeof local)<0)
            {
                logMessage(FATAL,"bind:%s",strerror(errno));
                exit(BIND_ERR);
            }
            logMessage(DEBUG,"bind:%s,%d",strerror(errno),listenSock_);
            //3.监听套接字 
            if(listen(listenSock_,5)<0)
            {
                logMessage(FATAL,"listen:%s",strerror(errno));
                exit(LISTEN_ERR);
            }
            logMessage(DEBUG,"listen:%s,%d",strerror(errno),listenSock_);
            //加载线程池
    
            tp_=ThreadPool<Task>::getInstance();//获取单例
    
    
    
            //到这一步就运行起来等待客户端链接...
    
        }
    
        //多线程
       
        static void *threadRoutine(void *args)
        {
            pthread_detach(pthread_self()); //设置线程分离
            ThreadData<T> *td = static_cast<ThreadData<T>*>(args);
            td->this_->transService(td->sock_, td->clinetIp_, td->clientPort_);
            delete td;
            return nullptr;
        }
    
    
        void loop()
        {   
           //打开线程池 初始化
            tp_->start();
            logMessage(DEBUG, "thread pool start success, thread num: %d", tp_->threadNum());
          while(true)
          {
              struct sockaddr_in peer;
            socklen_t len=sizeof(peer);
            //4.获取链接
            int serviceSock=accept(listenSock_,(struct sockaddr* )&peer,&len);
            if(serviceSock<0)
            {
                //获取链接失败
                logMessage(WARINING,"Accept :%s[%d]",strerror(errno),serviceSock);
                continue;//获取失败继续获取
            }
    
            //4.1获取客户端基本信息
               uint16_t peerPort = ntohs(peer.sin_port);
                std::string peerIp = inet_ntoa(peer.sin_addr);
    
                logMessage(DEBUG, "accept: %s | %s[%d], socket fd: %d",
                           strerror(errno), peerIp.c_str(), peerPort, serviceSock);
                //5 提供服务, echo -> 小写 -> 大写
                //5.0 v0 版本 -- 单进程 -- 一旦进入transService,主执行流,就无法进行向后执行,只能提供完毕服务之后才能进行accept
                //transService(serviceSock, peerIp, peerPort);
    
    
                //多线版本
                // ThreadData *td = new ThreadData(peerPort, peerIp, serviceSock, this);
                // pthread_t tid;
                // pthread_create(&tid, nullptr, threadRoutine, (void*)td);
    
    
                //线程池
         
                Task t(serviceSock, peerIp, peerPort, transService);
            
            
    
                tp_->push(t);
               
    
          }
        }
    
       
    
        private:
            // sock
        int listenSock_;
        // port
        uint16_t port_;
        // ip
        std::string ip_;
            // 引入线程池
        ThreadPool<Task> *tp_;
    };
    
    static void Usage(std::string proc)
    {
        std::cerr << "Usage:\n\t" << proc << " port ip" << std::endl;
        std::cerr << "example:\n\t" << proc << " 8080 127.0.0.1\n" << std::endl;
    
    }
    
     
    // ./ServerTcp local_port local_ip
    int main(int argc, char *argv[])
    {
        if(argc != 2 && argc != 3 )
        {
            //只能提供两个或者三个参数
            Usage(argv[0]);
            exit(USAGE_ERR);
        }
        uint16_t port = atoi(argv[1]);
        std::string ip;
        if(argc == 3) ip = argv[2];
        ServerTcp<Task> svr(port, ip);
        svr.init();
        svr.loop();
        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

    🦀 7.2 ThreadPool.hpp

    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "Lock.hpp"
    using namespace std;
    
    int gThreadNum = 15; // 线程数
    template <class T>
    class ThreadPool
    {
    private:
        ThreadPool(int threadNum = gThreadNum) : threadNum_(threadNum), isStart_(false)
        {
            assert(threadNum_ > 0);
            pthread_mutex_init(&mutex_, nullptr);
            pthread_cond_init(&cond_, nullptr);
        }
        // 禁止掉拷贝构造和赋值构造
        ThreadPool(const ThreadPool<T> &) = delete;
        void operator=(const ThreadPool<T> &) = delete;
    
    public:
        static ThreadPool<T> *getInstance()
        {
            static Mutex mutex;
            if (nullptr == instance) // 检查是否已经存在单例对象 (第一次检查)
            {
                LockGuard LockGuard(&mutex); // 进入代码块 加锁 退出
                if (nullptr == instance)     // (第二次检查)
                {
                    instance = new ThreadPool<T>();
                }
                /*首先在没有锁的情况下检查一次instance是否为空,然后在加锁的情况下再进行一次检查
                    确保多线程情况之下,只有一个线程成功创建实例*/
            }
            return instance;
        }
    
        // 线程执行函数
        static void *threadRountine(void *args)
        {
            /*每个线程将在该函数中循环等待任务,获取任务,执行任务,然后再次等待*/
            pthread_detach(pthread_self()); // 线程分离  防止资源泄漏
            ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
            while (1)
            {
                tp->lockQueue();
                while (!tp->haveTask())
                {
                    tp->waitForTask();
                }
                // 拿到任务
                T t = tp->pop();
                tp->unlockQueue();
                t();
            }
        }
    
        void start()
        {
            assert(!isStart_);
            for (int i = 0; i < threadNum_; i++)
            {
                pthread_t temp;
                pthread_create(&temp, nullptr, threadRountine, this); // 创建线程
            }
            isStart_ = true; // 修改状态
        }
    
        void push(const T &in)
        {
            lockQueue();
            taskQueue_.push(in);
            choiceThreadForHandler();
            unlockQueue();
        }
    
        ~ThreadPool()
        {
            pthread_mutex_destroy(&mutex_);
            pthread_cond_destroy(&cond_);
        }
    
        int threadNum()
        {
            return threadNum_;
        }
    
    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 threadNum_;
        queue<T> taskQueue_;    // 任务队列
        pthread_mutex_t mutex_; // 锁
        pthread_cond_t cond_;   // 条件变量
        /*条件标志的作用是在一个或多个线程
        等待某个条件成立的情况下,阻塞自己,
        直到其他线程改变了共享数据并发出
        信号告诉等待的线程可以继续执行*/
    
        static ThreadPool<T> *instance; // 设置单例
        // const static int a = 100;
    };
    template <class T>
    ThreadPool<T> *ThreadPool<T>::instance = nullptr;
    
    • 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

    🐡7.3 Task.hpp

    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include "log.hpp"
    
    class Task
    {
    public:
        //等价于
        // typedef std::function callback_t;
        using callback_t = std::function<void (int, std::string, uint16_t)>;
    private:
        int sock_; // 给用户提供IO服务的sock
        uint16_t port_;  // client port
        std::string ip_; // client ip
        callback_t func_;  // 回调方法
    public:
        Task():sock_(-1), port_(-1)
        {}
        Task(int sock, std::string ip, uint16_t port, callback_t func)
        : sock_(sock), ip_(ip), port_(port), func_(func)
        {}
        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_);
        }
        ~Task()
        {}
    };
    
    • 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

    🐠7.4ClientTcp.cc

    #include "util.hpp"
    // 2. 需要bind吗??需要,但是不需要自己显示的bind! 不要自己bind!!!!
    // 3. 需要listen吗?不需要的!
    // 4. 需要accept吗?不需要的!
    
    volatile bool quit = false;
    
    static void Usage(std::string proc)
    {
        std::cerr << "Usage:\n\t" << proc << " serverIp serverPort" << std::endl;
        std::cerr << "Example:\n\t" << proc << " 127.0.0.1 8081\n"
                  << std::endl;
    }
    // ./clientTcp serverIp serverPort
    int main(int argc, char *argv[])
    {
        if (argc != 3)
        {
            Usage(argv[0]);
            exit(USAGE_ERR);
        }
        std::string serverIp = argv[1];
        uint16_t serverPort = atoi(argv[2]);
    
        // 1. 创建socket SOCK_STREAM
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            std::cerr << "socket: " << strerror(errno) << std::endl;
            exit(SOCKET_ERR);
        }
    
        // 2. connect,向服务器发起链接请求, 
        // 2.1 先填充需要连接的远端主机的基本信息
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(serverPort);
        inet_aton(serverIp.c_str(), &server.sin_addr);
        // 2.2 发起请求,connect 会自动帮我们进行bind!
        if (connect(sock, (const struct sockaddr *)&server, sizeof(server)) != 0)
        {
            std::cerr << "connect: " << strerror(errno) << std::endl;
            exit(CONN_ERR);
        }
        std::cout << "info : connect success: " << sock << std::endl;
    
        std::string message;
        while (!quit)
        {
            message.clear();
            std::cout << "请输入你的消息>>> ";
            std::getline(std::cin, message); // 结尾不会有\n
            if (strcasecmp(message.c_str(), "quit") == 0)
                quit = true;
    
            ssize_t s = write(sock, message.c_str(), message.size());
            if (s > 0)
            {
                message.resize(1024);
                ssize_t s = read(sock, (char *)(message.c_str()), 1024);
                if (s > 0)
                    message[s] = 0;
                std::cout << "Server Echo>>> " << message << std::endl;
            }
            else if (s <= 0)
            {
                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

    🐟 7.5 Lock.hpp

    本程序暂不涉及临界资源的访问 互斥锁的机制 可加可不加 我这里加上了 是为了学习和使用。

    #pragma once
    #include
    #include
    class Mutex
    {
        public:
        Mutex()
        {
            pthread_mutex_init(&lock_,nullptr);
        }
        ~Mutex()
        {
            pthread_mutex_destroy(&lock_);
        }
           void lock()
        {
            pthread_mutex_lock(&lock_);
        }
        void unlock()
        {
            pthread_mutex_unlock(&lock_);
        }
    
    private:
        pthread_mutex_t lock_;
    };
    
    
    class LockGuard
    {
    public:
        LockGuard(Mutex *mutex) : mutex_(mutex)
        {
            mutex_->lock();
            std::cout << "加锁成功..." << std::endl;
        }
    
        ~LockGuard()
        {
            mutex_->unlock();
            std::cout << "解锁成功...." << std::endl;
        }
    
    private:
        Mutex *mutex_;
    };
    
    
    • 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

    🐬7.7头文件util.hpp

    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "log.hpp"
    
    #define SOCKET_ERR 1
    #define BIND_ERR   2
    #define LISTEN_ERR 3
    #define USAGE_ERR  4
    #define CONN_ERR   5
    
    #define BUFFER_SIZE 1024
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    🐳7.6Makefile 文件

    .PHONY:all
    all:clientTcp serverTcp
    
    clientTcp: ClientTCP.cc
    	g++ -o $@ $^ -std=c++11
    serverTcp:ServerTCP.cc
    	g++ -o $@ $^ -std=c++11 -lpthread
    
    .PHONY:clean
    clean:
    	rm -f serverTcp clientTcp
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    00007

    🐋7.8 日志文件 log.hpp

    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define DEBUG 0
    #define NOTICE 1
    #define WARINING 2
    #define FATAL 3
    
    const char *log_level[]={"DEBUG", "NOTICE", "WARINING", "FATAL"};//四个提示等级
    
    // logMessage(DEBUG, "%d", 10);
    void logMessage(int level, const char *format, ...)//可变参数
    {
        assert(level >= DEBUG);
        assert(level <= FATAL);
    
        char *name = getenv("USER");
    
        char logInfo[1024];//存储日志数据
        va_list ap; // ap -> char*                    va_list是一种允许您操作变量参数的类型,
        va_start(ap, format);                        //va_start用于初始化 ava_list以指向第一个变量参数
    
        vsnprintf(logInfo, sizeof(logInfo)-1, format, ap);
    
        va_end(ap); // ap = NULL
    
    
        FILE *out = (level == FATAL) ? stderr:stdout;
    
        fprintf(out, "%s | %u | %s | %s\n", \
            log_level[level], \
            (unsigned int)time(nullptr),\
            name == nullptr ? "unknow":name,\
            logInfo);
    
      
    }
    
    • 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

    注:本程序是基于Linux系统下进行的,请在Linux环境下进行运行,本文为原创作品,各位转载请说明出处,创作不易,点赞支持技术大涨~~早日进大厂 ,关于程序有任何问题请在评论区留言…

    🦈 🐊 🐅 🐆 🦓 🦍 🦧 🦣 🐘 🦛 🦏 🐪 🐫 🦒 🦘 🐃 🐂 🐄 🐎 🐖 🐏 🐑 🦙 🐐 🦌 🐕 🐩 🦮 🐕‍🦺 🐈 🐈‍⬛

  • 相关阅读:
    MySQL视图详解
    动态规划 | 完全背包问题笔记 | 代码随想录
    【JavaWeb】案例:读取 WEB 工程下的资源文件、文件下载、点击切换验证码
    一文学会vim基本操作
    搭建自动化 Web 页面性能检测系统 —— 设计篇
    git rebase实战
    【一起学数据结构与算法】0基础学习集合Map和Set(包含面试题)
    Java8-接口的新增(默认方法和静态方法)
    Python不调包手绘圣诞树
    【三维目标检测】PointRCNN(二)
  • 原文地址:https://blog.csdn.net/weixin_62892290/article/details/132706528