简单的TcpServer(英译中)

目录

  • 一、TCP socket API 详解
    • 1.1 socket()
    • 1.2 bind()
    • 1.3 listen()
    • 1.4 accept()
    • 1.5 connect
  • 二、TcpServer(英译中)
    • 2.1 TcpServer.hpp
    • 2.2 TcpClient.cc
    • 2.3 Task.hpp
    • 2.4 Thread.hpp
    • 2.5 ThreadPool.hpp
    • 2.6 makefile
    • 2.7 Main.cc
    • 2.8 log.hpp
    • 2.9 Init.hpp
    • 2.10 dict.txt
    • 2.11 Daemon.hpp

一、TCP socket API 详解

实现一个简单的英译汉的功能。
下面介绍程序中用到的socket API,这些函数都在sys/socket.h中。

1.1 socket()

在这里插入图片描述

socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;
应用程序可以像读写文件一样用read/write在网络上收发数据;
如果socket()调用出错则返回-1;
对于IPv4, family参数指定为AF_INET;
对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议;
protocol参数的就不做介绍了,指定为0即可。

1.2 bind()

在这里插入图片描述
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用bind绑定一个固定的网络地址和端口号;

bind()成功返回0,失败返回-1。

bind()的作用是将参数sockfd和myaddr绑定在一起, 使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号;

前面讲过,struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度;

我们的程序中对myaddr参数是这样初始化的:
在这里插入图片描述

  1. 将整个结构体清零;
  2. 设置地址类型为AF_INET;
  3. 网络地址为INADDR_ANY, 这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址, 这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;
  4. 端口号为SERV_PORT, 我们定义为8080;

1.3 listen()

在这里插入图片描述
listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是5),listen()成功返回0,失败返回-1;

1.4 accept()

在这里插入图片描述
1、三次握手完成后, 服务器调用accept()接受连接;
2、如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;addr是一个传出参数,
accept()返回时传出客户端的地址和端口号;
3、如果给addr 参数传NULL,表示不关心客户端的地址;
4、addrlen参数是一个传入传出参数(value-result argument), 传入的是调用者提供的, 缓冲区addr的长度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);

1.5 connect

在这里插入图片描述
1、客户端需要调用connect()连接服务器;
2、connect和bind的参数形式一致, 区别在于bind的参数是自己的地址, 而connect的参数是对方的地址;
3、connect()成功返回0,出错返回-1;

二、TcpServer(英译中)

2.1 TcpServer.hpp

#pragma once

#include <iostream>
using namespace std;

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include <strings.h>
#include <functional>
#include <cstring>
#include <unordered_map>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"


const static int NUM = 1024;
const static string DEFAULT_IP = "0.0.0.0";
const static uint16_t DEFAULT_PORT = 8080;



using func_t = function<string(string)>;

enum
{
    SOCKET_ERROR = 3,
    BIND_ERROR,
    LISTEN_ERROR,
    ACCEPT_ERRNO,
    READ_ERROR
};

extern Log log;

// class ThreadData
// {
// public:
//     ThreadData(int sockfd, string clientip, uint16_t clientport, void *argv)
//         : _sockfd(sockfd), _clientip(clientip), _clientport(clientport), _argv(argv)
//     {
//     }

// public:
//     int _sockfd;
//     string _clientip;
//     uint16_t _clientport;
//     void *_argv;
// };

class TcpServer
{
public:
    TcpServer(func_t func, const uint16_t &port = DEFAULT_PORT, const string &ip = DEFAULT_IP)
        : _port(port), _ip(ip), _func(func)
    {
    }

    ~TcpServer()
    {
        if (_listen_sockfd > 0)
        {
            close(_listen_sockfd);
        }
    }

    void Init()
    {
        // 创建套接字
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            log(Fatal, "create _listen_socket failed,errno code:%d,error code string:%s", errno, strerror(errno));
            exit(SOCKET_ERROR);
        }
        log(Info, "create _listen_socket successed,_listen_sockfd:%d", _listen_sockfd);

        //设置端口复用
        int opt=1;
        setsockopt(_listen_sockfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));

        // 绑定
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        if (bind(_listen_sockfd, (struct sockaddr *)(&local), sizeof(local)) < 0)
        {
            log(Fatal, "bind failed,errno code:%d,error code string:%s", errno, strerror(errno));
            exit(BIND_ERROR);
        }
        log(Info, "bind successed...,_listen_socket:%d",_listen_sockfd);

        // 监听
        if (listen(_listen_sockfd, 5) < 0)
        {
            log(Fatal, "set _listen_sockfd failed,errno code:%d,error code string:%s", errno, strerror(errno));
            exit(LISTEN_ERROR);
        }
        log(Info, "set _listen_sockfd successed...,_listen_socket:%d",_listen_sockfd);
    }

    // static void* Service(void* argv)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData* td=static_cast<ThreadData*>(argv);

    //     char buffer[NUM];
    //     bzero(buffer, sizeof(buffer));
    //     while (true)
    //     {
    //         ssize_t s = read(td->_sockfd, buffer, sizeof(buffer) - 1);
    //         if (s > 0)
    //         {
    //             buffer[s] = '\0';
    //             cout << "client[ip:" << td->_clientip << " port:" << td->_clientport << "]# " << buffer << endl;

    //             // string message = _func(buffer);
    //             // write(sockfd, message.c_str(), message.size());
    //             write(td->_sockfd, buffer, sizeof(buffer));
    //             cout << "write done..." << endl;
    //         }
    //         else if (s == 0)
    //         {
    //             close(td->_sockfd);
    //             log(Info, "client exit... close sockfd:%d", td->_sockfd);
    //             break;
    //         }
    //         else
    //         {
    //             close(td->_sockfd);
    //             log(Fatal, "read failed,errno code:%d,error code string:%s,close sockfd:%d",
    //                 errno, strerror(errno), td->_sockfd);
    //             break;
    //         }
    //     }
    //     close(td->_sockfd);
    //     log(Info,"close sockfd:%d\n",td->_sockfd);

    // }

    // void Service(const int &sockfd, const string &clientip, const uint16_t &clientport)
    // {
    //     char buffer[NUM];
    //     bzero(buffer, sizeof(buffer));
    //     while (true)
    //     {
    //         ssize_t s = read(sockfd, buffer, sizeof(buffer) - 1);
    //         if (s > 0)
    //         {
    //             buffer[s] = '\0';
    //             //cout << "client[ip:" << clientip << " port:" << clientport << "]# " << buffer << endl;
    //             cout << "client[ip:" << clientip << " port:" << clientport << " sockfd:"<<sockfd<<"]# " << buffer << endl;

    //             // string message = _func(buffer);
    //             // write(sockfd, message.c_str(), message.size());

    //             // 这里不能写sizeof(buffer),不然可能会因为缓冲区问题导致client端不能正确打印
    //             write(sockfd, buffer, strlen(buffer));
    //             cout << "write done..." << endl;
    //         }
    //         else if (s == 0)
    //         {
    //             close(sockfd);
    //             log(Info, "client exit... ");
    //             break;
    //         }
    //         else
    //         {
    //             close(sockfd);
    //             log(Fatal, "read failed,errno code:%d,error code string:%s",
    //                 errno, strerror(errno));
    //             break;
    //         }
    //     }
    //     close(sockfd);
    //     log(Info, "close sockfd:%d", sockfd);
    // }

    // static void *Rontiue(void *argv)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData *td = static_cast<ThreadData *>(argv);
    //     TcpServer *p = static_cast<TcpServer *>(td->_argv);
    //     p->Service(td->_sockfd, td->_clientip, td->_clientport);
    //     delete td;
    // }

    void Run()
    {
        //守护进程化
        Daemon();

        //启动线程池
        ThreadPool<Task>::GetThreadPool()->Run();

        while (true)
        {
            // 获取连接
            struct sockaddr_in client;
            bzero(&client, sizeof(client));
            socklen_t len = sizeof(client);
            int sockfd = accept(_listen_sockfd, (struct sockaddr *)(&client), &len);
            if (sockfd < 0)
            {
                log(Warning, "accept sockfd failed,errno code:%d,error code string:%s", errno, strerror(errno));
                sleep(1);
                continue;
            }
            
            //不建议用inet_ntoa,因为inet_ntoa用的是一个静态的缓冲区,多次调用可能会导致覆盖问题
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(client));
            uint16_t clientport = ntohs(client.sin_port);
            log(Info, "accept sockfd successed,get a new link...[clientip:%s,clientport:%d],sockfd:%d", clientip, clientport, sockfd);

            //============================================================

            // // 单进程版
            // Service(sockfd, clientip, clientport);

            //=============================================================

            //=============================================================

            // // 多进程版
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     close(_listen_sockfd);
            //     // 子进程
            //     if (fork() > 0)
            //     {
            //         exit(0);
            //     }
            //     Service(sockfd, clientip, clientport);
            //     exit(5);
            // }
            // // 这里必须关闭,因为在上面的子进程已经拿到了文件描述符了,所以父进程
            // // 就要关闭这个文件描述符,其实父进程关闭的时候这个文件描述符并不会真正
            // // 地被释放,因为只是引用计数减一,只有子进程结束的时候释放该文件描述符
            // // 才会真正地被释放
            // close(sockfd);
            // // 这里不会被阻塞住,因为是孙子进程执行任务,儿子进程一下子就退出啦,即waitpid不会阻塞很久
            // waitpid(id, nullptr, 0);

            //=================================================================

            //=================================================================

            // // 多线程版
            // pthread_t tid;
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_create(&tid, nullptr, Rontiue, td);

            //==================================================================

            //线程池版
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetThreadPool()->Push(t);

            //===================================================================
        }
    }

private:
    int _listen_sockfd;    //监听套接字
    uint16_t _port;        //服务器端口号
    string _ip;            //服务器ip地址
    func_t _func;          //回调函数,服务器处理客户需求的方法可以自定义
};

2.2 TcpClient.cc


#include <iostream>
using namespace std;

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <pthread.h>
#include <cstring>
#include "log.hpp"

const static int NUM = 1024;

enum
{
    SOCKET_ERROR = 3,
    CONNECT_ERROR
};

void Usage(string argv)
{
    cout << "\n\t"
         << "Usage:" << argv << " ServerIp ServerPort" << endl
         << endl;
}

Log log;

// int main(int argc, char *argv[])
// {
//     if (argc != 3)
//     {
//         Usage(argv[0]);
//         exit(1);
//     }

//     string ServerIp = argv[1];
//     string str = argv[2];
//     uint16_t ServerPort = (uint16_t)stoi(str.c_str());

//     // 创建套接字
//     int sockfd = socket(AF_INET, SOCK_STREAM, 0);
//     if (sockfd < 0)
//     {
//         log(Fatal, "create socket failed,errno code:%d,error code string:%s", errno, strerror(errno));
//         exit(SOCKET_ERROR);
//     }
//     log(Info, "create socket successed,sockfd:%d", sockfd);

//     struct sockaddr_in server;
//     server.sin_family = AF_INET;
//     server.sin_addr.s_addr = inet_addr(ServerIp.c_str());
//     server.sin_port = htons(ServerPort);
//     socklen_t len = sizeof(server);

//     // 建立连接
//     if (connect(sockfd, (struct sockaddr *)&server, len) < 0)
//     {
//         log(Fatal, "connect failed,errno code:%d,error code string:%s", errno, strerror(errno));
//         exit(CONNECT_ERROR);
//     }
//     log(Info, "connect successed...");

//     string buffer;
//     while (true)
//     {
//         cout << "Please Enter# ";
//         getline(cin, buffer);

//         write(sockfd, buffer.c_str(), buffer.size());

//         //这里的message缓冲区的大小不能少于1024,否则会出错,具有原因有待查明,
//         //原因找到了,是服务端write的时候写了sizeof(buffer),导致缓冲区出现了问题
//         char message[1024];
//         bzero(message,sizeof(message));
//         ssize_t s = read(sockfd, message, sizeof(message)-1);
//         cout<<"read done..."<<endl;
//         if (s > 0)
//         {
//             message[s]='\0';
//             cout << "Server say# " << message << endl;
//         }
//         else if (s == 0)
//         {
//             log(Info, "server exit,close sockfd:%d", sockfd);
//             break;
//         }
//         else
//         {
//             log(Info, "read failed,close sockfd:%d", sockfd);
//             break;
//         }
//     }

//     close(sockfd);
// }


//带入了重连机制
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    string ServerIp = argv[1];
    string str = argv[2];
    uint16_t ServerPort = (uint16_t)stoi(str.c_str());

    //初始化服务器的结构体信息
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(ServerIp.c_str());
    server.sin_port = htons(ServerPort);
    socklen_t len = sizeof(server);

    while (true)
    {
        bool isreconnect = false;

        // 创建套接字
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            log(Fatal, "create socket failed,errno code:%d,error code string:%s", errno, strerror(errno));
        }
        log(Info, "create socket successed,sockfd:%d", sockfd);

        int cnt = 10;
        do
        {
            // 建立连接
            if (connect(sockfd, (struct sockaddr *)&server, len) < 0)
            {
                //如果连接失败,那么isreconnect就会被置为true,然后就进入重连状态
                cnt--;
                isreconnect = true;
                log(Warning, "connect failed,errcode:%d,errstring:%s,reconnect:%d", errno, strerror(errno), cnt);
                sleep(2);
            }
            else
            {
                //连接成功就跳出do while循环
                break;
            }

        } while (cnt && isreconnect);//最多重连cnt次,当cnt变为0的时候,就会自动跳出do while循环

        if (cnt == 0)
        {
            //重连失败
            log(Warning, "connect failed,errcode:%d,errstring:%s,reconnect fail...", errno, strerror(errno));
            break;
        }

        log(Info, "connect successed...");

        string buffer;
        // while (true)
        // {
        cout << "Please Enter# ";
        getline(cin, buffer);

        write(sockfd, buffer.c_str(), buffer.size());

        // 这里的message缓冲区的大小不能少于1024,否则会出错,具有原因有待查明,
        // 原因找到了,是服务端write的时候写了sizeof(buffer),导致缓冲区出现了问题
        char message[1024];
        bzero(message, sizeof(message));
        ssize_t s = read(sockfd, message, sizeof(message) - 1);
        // cout << "read done..." << endl;
        if (s > 0)
        {
            message[s] = '\0';
            cout << "Server say# " << message << endl;
        }
        else if (s == 0)
        {
            log(Info, "server exit,close sockfd:%d", sockfd);
            // break;
        }
        else
        {
            log(Info, "read failed,close sockfd:%d", sockfd);
            // break;
        }
        // }

        close(sockfd);
    }
}

由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。
注意:
客户端不是不允许调用bind(), 只是没有必要调用bind()固定一个端口号. 否则如果在同一台机器上启动多个客户端, 就会出现端口号被占用导致不能正确建立连接;
服务器也不是必须调用bind(), 但如果服务器不调用bind(), 内核会自动给服务器分配监听端口, 每次启动服务器时端口号都不一样, 客户端要连接服务器就会很麻烦了。

2.3 Task.hpp

#pragma once

#include <stdio.h>
#include "log.hpp"
#include "Init.hpp"
#include <cstring>
#include <strings.h>

Log log;

//定义一个全局变量,在main函数之前就定义好了,即初始化好了
Init dict;

class Task
{
public:
    Task(int sockfd, const std::string &clientip, const uint16_t &clientport)
        : _sockfd(sockfd), _clientip(clientip), _clientport(clientport)
    {
    }
    Task()
    {
    }
    void run(const string& threadName)
    {
        // // 测试代码
        // char buffer[4096];
        // ssize_t n = read(sockfd_, buffer, sizeof(buffer));
        // if (n > 0)
        // {
        //     buffer[n] = 0;
        //     std::cout << "client key# " << buffer << std::endl;
        //     std::string echo_string = buffer;

        //     // sleep(5);
        //     // // close(sockfd_);
        //     // lg(Warning, "close sockfd %d done", sockfd_);

        //     // sleep(2);
        //     n = write(sockfd_, echo_string.c_str(), echo_string.size()); // 100 fd 不存在
        //     if(n < 0)
        //     {
        //         lg(Warning, "write error, errno : %d, errstring: %s", errno, strerror(errno));
        //     }
        // }
        // else if (n == 0)
        // {
        //     lg(Info, "%s:%d quit, server close sockfd: %d", clientip_.c_str(), clientport_, sockfd_);
        // }
        // else
        // {
        //     lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd_, clientip_.c_str(), clientport_);
        // }
        // close(sockfd_);

        char buffer[4096];
        bzero(buffer, sizeof(buffer));

        ssize_t s = read(_sockfd, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            // buffer[s] = '\0';
            // // cout << "client[ip:" << clientip << " port:" << clientport << "]# " << buffer << endl;
            // cout <<threadName<< "收到一条信息:client[ip:" << _clientip << " port:" << _clientport << " sockfd:" << _sockfd << "]# " << buffer << endl;

            // // string message = _func(buffer);
            // // write(sockfd, message.c_str(), message.size());

            // // 这里不能写sizeof(buffer),不然可能会因为缓冲区问题导致client端不能正确打印
            // write(_sockfd, buffer, strlen(buffer));
            // cout << "write done..." << endl;


            cout <<threadName<< "收到一条信息:client[ip:" << _clientip << " port:" << _clientport << " sockfd:" << _sockfd << "]# " << buffer << endl;
            string resp=dict.translation(buffer);
            // 这里不能写sizeof(buffer),不然可能会因为缓冲区问题导致client端不能正确打印
            write(_sockfd, resp.c_str(), resp.size());
        }
        else if (s == 0)
        {
            close(_sockfd);
            log(Info, "client exit... ");
        }
        else
        {
            close(_sockfd);
            log(Fatal, "read failed,errno code:%d,error code string:%s",
                errno, strerror(errno));
        }

        close(_sockfd);
        log(Info, "close sockfd:%d", _sockfd);
    }
    void operator()(const string& threadName)
    {
        run(threadName);
    }
    ~Task()
    {
    }

private:
    int _sockfd;
    std::string _clientip;
    uint16_t _clientport;
};

2.4 Thread.hpp

#pragma once

#include <iostream>
using namespace std;
#include <functional>

typedef void *(*fun_t)(void *);

struct ThreadData
{
public:
    ThreadData(int num, void *args)
        : _args(args)
    {
        _name = "Thread-" + to_string(num);
    }

public:
    string _name;
    void *_args;
};

class Thread
{
public:
    Thread(int num, fun_t cb, void *args)
        : _cb(cb), _data(num, args)
    {
    }

    void Start()
    {
        pthread_create(&_tid, nullptr, _cb, &_data);
    }

private:
    pthread_t _tid;
    fun_t _cb;
    ThreadData _data;
};

2.5 ThreadPool.hpp

#pragma once

#include <iostream>
using namespace std;

#include <vector>
#include <queue>
#include <mutex>
#include <unistd.h>
#include "Thread.hpp"



// const static int default_num = 5;

// template <class T>
// class ThreadPool
// {
// private:
//     void Lock()
//     {
//         pthread_mutex_lock(&_mtx);
//     }
//     void UnLock()
//     {
//         pthread_mutex_unlock(&_mtx);
//     }
//     void WakeUp()
//     {
//         pthread_cond_signal(&_cond);
//     }
//     void ThreadSleep()
//     {
//         pthread_cond_wait(&_cond, &_mtx);
//     }
//     bool IsQueueEmpty()
//     {
//         return _q.empty();
//     }

// private:
//     ThreadPool(const int num = default_num)
//         : _num(num)
//     {
//         pthread_mutex_init(&_mtx, nullptr);
//         pthread_cond_init(&_cond, nullptr);
//     }

// public:
//     static ThreadPool<T>* GetInstance()
//     {
//         if(nullptr==_Inst)
//         {
//             pthread_mutex_lock(&_Inst_mtx);
//             if(nullptr==_Inst)
//             {
//                 _Inst=new ThreadPool<T>();
//             }
//             pthread_mutex_unlock(&_Inst_mtx);
//         }
//         return _Inst;
//     }

//     ~ThreadPool()
//     {
//         pthread_mutex_destroy(&_mtx);
//         pthread_cond_destroy(&_cond);
//     }

//     static void *Routine(void *args)
//     {
//         ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
//         pthread_detach(pthread_self());
//         while (true)
//         {
//             // cout<<"进入routine"<<endl;

//             //pop的锁必须在这里加,不能在pop函数中加,因为这里的ThreadSleep函数等待时需要先释放锁
//             //如果锁在pop函数中加的话,这里的ThreadSleep函数将无法释放锁,导致进程卡死
//             tp->Lock();
//             while (tp->IsQueueEmpty())
//             {
//                 tp->ThreadSleep();
//             }

//             usleep(1000);
//             T t=tp->Pop();
//             tp->UnLock();

//             //cout << "从任务队列中消费了一个数据:" << t << endl;
//             t();
//         }

//         return nullptr;
//     }

//     void Run()
//     {
//         for (int i = 0; i < _num; i++)
//         {
//             pthread_t tid;
//             // cout<<"Run"<<endl;
//             pthread_create(&tid, nullptr, Routine, this);
//             _threads.push_back(tid);
//         }
//     }

//     void Push(const T &in)
//     {
//         Lock();
//         _q.push(in);
//         WakeUp();
//         UnLock();
//     }

//     T Pop()
//     {
//         T out=_q.front();
//         _q.pop();
//         return out;
//     }

// private:
//     vector<pthread_t> _threads;
//     queue<T> _q;
//     int _num;
//     pthread_mutex_t _mtx;
//     pthread_cond_t _cond;
//     static ThreadPool<T>* _Inst;
//     static pthread_mutex_t _Inst_mtx;
// };

// template<class T>
// ThreadPool<T>* ThreadPool<T>::_Inst=nullptr;

// template<class T>
// pthread_mutex_t ThreadPool<T>::_Inst_mtx=PTHREAD_MUTEX_INITIALIZER;



const static int default_num = 5;

template <class T>
class ThreadPool
{
private:
    void Lock()
    {
        pthread_mutex_lock(&_mtx);
    }
    void UnLock()
    {
        pthread_mutex_unlock(&_mtx);
    }
    void WakeUp()
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond, &_mtx);
    }
    bool IsQueueEmpty()
    {
        return _q.empty();
    }

private:
    ThreadPool(const int num = default_num)
        : _num(num)
    {
        //构造函数中必须把所有的线程对象先创建好,否则程序会崩溃
        for(int i=0;i<_num;i++)
        {
            Thread thread(i+1,Routine,this);
            _threads.push_back(thread);
        }

        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

public:
    static ThreadPool<T>* GetThreadPool()
    {
        if(nullptr==_Inst)
        {
            pthread_mutex_lock(&_Inst_mtx);
            if(nullptr==_Inst)
            {
                _Inst=new ThreadPool<T>();
            }
            pthread_mutex_unlock(&_Inst_mtx);
        }
        return _Inst;
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }

    static void *Routine(void *args)
    {
        //ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        ThreadData* ptd=static_cast<ThreadData*>(args);
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(ptd->_args);
        pthread_detach(pthread_self());
        while (true)
        {
            //cout<<"进入routine"<<endl;

            //pop的锁必须在这里加,不能在pop函数中加,因为这里的ThreadSleep函数等待时需要先释放锁
            //如果锁在pop函数中加的话,这里的ThreadSleep函数将无法释放锁,导致进程卡死
            tp->Lock();
            while (tp->IsQueueEmpty())
            {
                tp->ThreadSleep();
            }

            //cout<<"进入routine"<<endl;


            usleep(1000);
            T t=tp->Pop();
            tp->UnLock();

            //cout << "从任务队列中消费了一个数据:" << t << endl;
            //cout<<ptd->_name<<":";
            t(ptd->_name);
        }

        return nullptr;
    }

    void Run()
    {
        for (int i = 0; i < _num; i++)
        {
            //pthread_t tid;
            // cout<<"Run"<<endl;
            //pthread_create(&tid, nullptr, Routine, this);
            _threads[i].Start();
        }
    }

    void Push(const T &in)
    {
        Lock();
        _q.push(in);
        WakeUp();
        UnLock();
    }

    T Pop()
    {
        T out=_q.front();
        _q.pop();
        return out;
    }

private:
    vector<Thread> _threads;
    queue<T> _q;
    int _num;
    pthread_mutex_t _mtx;
    pthread_cond_t _cond;
    static ThreadPool<T>* _Inst;
    static pthread_mutex_t _Inst_mtx;
};

template<class T>
ThreadPool<T>* ThreadPool<T>::_Inst=nullptr;

template<class T>
pthread_mutex_t ThreadPool<T>::_Inst_mtx=PTHREAD_MUTEX_INITIALIZER;

2.6 makefile

.PHONY:all
all:Client Server

Client:TcpClient.cc
	g++ -o $@ $^ -std=c++11 -lpthread

Server:Main.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f Client Server

2.7 Main.cc

#include "TcpServer.hpp"
#include <memory>

string func(string s)
{
    return s + " already handled\n";
}

void Usage(string argv)
{
    cout << "\n\t"
         << "Usage:" << argv << " ServerPort" << endl
         << endl;
}
int main(int argc, char* argv[]) 
{
    srand((unsigned int)time(nullptr));

    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }

    string str = argv[1];
    uint16_t ServerPort = (uint16_t)stoi(str.c_str());

    unique_ptr<TcpServer> svr(new TcpServer(func,ServerPort));
    //unique_ptr
    svr->Init();
    svr->Run();

    return 0;
}

2.8 log.hpp

#pragma once

#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <time.h>
#include <stdarg.h>

// 日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define OneFile 2
//向多个文件打印
#define Classfile 3
#define SIZE 1024

#define LogFile "log.txt"

class Log
{
public:
    Log()
    {
        printMethod = Screen;
        path = "./log/";
    }

    void Enable(int mothod)
    {
        printMethod = mothod;
    }

    string LevelToString(int level)
    {
        switch (level)
        {
        case Info:
        {
            return "Info";
        }
        case Debug:
        {
            return "Debug";
        }
        case Warning:
        {
            return "Warning";
        }
        case Error:
        {
            return "Error";
        }
        case Fatal:
        {
            return "Fatal";
        }
        default:
        {
            return "None";
        }
        }
    }

    void printlog(int level,const string& logtxt)
    {
        switch(printMethod)
        {
        case Screen:
        {
            cout<<logtxt<<endl;
            break;
        }
        case OneFile:
        {
            PrintOneFile(LogFile,logtxt);
            break;
        }
        case Classfile:
        {
            PrintClassfile(level,logtxt);
            break;
        }
        default:
        {
            break;
        }
        }
    }

    void PrintOneFile(const string& logname,const string& logtxt)
    {
        string _logname=path+logname;
        int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
        if(fd<0)
        {
            perror("open fail");
            return;
        }

        write(fd,logtxt.c_str(),logtxt.size());

        close(fd);

    }

    void PrintClassfile(int level,const string& logtxt)
    {
        string filename=LogFile;
        filename+='.';
        filename+=LevelToString(level);
        PrintOneFile(filename,logtxt);
    }

    void operator()(int level,const char* format,...)
    {
        time_t t=time(nullptr);
        struct tm* ctime=localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer,SIZE,"[%s][%d-%d-%d %d:%d:%d]",LevelToString(level).c_str(),
        ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,
        ctime->tm_hour,ctime->tm_min,ctime->tm_sec);

        va_list s;
        va_start(s,format);
        char rightbuffer[SIZE]={0};
        vsnprintf(rightbuffer,SIZE,format,s);
        va_end(s);


        char logtxt[SIZE*2];
        snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);

        printlog(level,logtxt);
    }

    ~Log()
    {
    }

private:
    // 打印方法
    int printMethod;
    string path;
};

2.9 Init.hpp

#pragma once

#include <iostream>
using namespace std;

#include <unordered_map>
#include <fstream>
#include "log.hpp"

const string dictname = "./dict.txt";
const string sep = ":";

extern Log log;

//把字符串分成key和val
static bool Split(const string &line, string &part1, string &part2)
{
    size_t pos = line.find(sep);
    if (pos == string::npos)
    {
        return false;
    }
    part1 = line.substr(0, pos);
    part2 = line.substr(pos + 2);
    return true;
}

class Init
{
public:
    Init()
    {
        //创建对象就会自动打开对应的文件
        ifstream in(dictname);
        if (!in.is_open())
        {
            log(Fatal, "open the dict failed...");
            exit(1);
        }
        log(Info, "open the dict successed...");

        string line;
        while (getline(in, line))
        {
            string part1, part2;
            if (Split(line, part1, part2))
            {
                //把所有的英文单词及其中文都映射到哈希表中
                _dict[part1] = part2;
            }
            else
            {
                log(Warning, "format error: %s", line.c_str());
            }
        }
        log(Info,"load dict success...");

        //关闭文件
        in.close();
    }

    string translation(const string& key)
    {
        auto it=_dict.find(key);
        if(it!=_dict.end())
        {
            return it->second;
        }
        else
        {
            return "Unknown this word";
        }
    }

private:
    unordered_map<string, string> _dict;
};

2.10 dict.txt

word: 单词
sentence: 句子
paragraph: 段落
book: 书
chapter: 章节
page: 页
article: 文章
dictionary: 字典
thesaurus: 同义词词典
encyclopedia: 百科全书
novel: 小说
short story: 短篇小说
poem: 诗
play: 剧本
film: 电影
song: 歌曲
painting: 画作
sculpture: 雕塑
computer: 电脑
phone: 手机
TV: 电视
radio: 收音机
car: 汽车
bus: 公交车
train: 火车
plane: 飞机
ship: 船
sun: 太阳
moon: 月亮
planet: 行星
star: 星星
sky: 天空
cloud: 云彩
rainbow: 彩虹
snow: 雪
wind: 风
fire: 火
water: 水
ice: 冰
mountain: 山峰
hill: 小山丘
forest: 森林
tree: 树木
flower: 花儿
grass: 草地
bird: 鸟类
cat: 猫科动物
dog: 狗科动物
fish: 鱼类动物
human being: 人类
happiness: 幸福
sadness: 悲伤
love: 爱意
fear: 恐惧感
joy: 喜悦感
anger: 愤怒感
surprise: 惊讶感
disgust: 厌恶感
guilt: 内疚感
shame: 自卑感
pride: 自尊感
hope: 希望感
disappointment: 失望感
excitement: 兴奋感
boredom: 厌倦感
trust: 信任感
surprise: 惊讶感
gratitude: 感激感
friendship: 友谊感
family love: 亲情爱意
romantic love: 浪漫爱情
school: 学校教育场所
workplace: 工作场所地
market place: 市场场所地

2.11 Daemon.hpp

#pragma once


#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//这是系统提供的一个类似于垃圾桶的文件,当我们向这
//个文件写入的时候,该文件就会把内容自动地丢弃
const std::string nullfile="/dev/null";

//守护进程化
void Daemon(const std::string& cmd="")
{
    //1、忽略其它异常信号
    signal(SIGPIPE,SIG_IGN);
    signal(SIGCHLD,SIG_IGN);
    signal(SIGSTOP,SIG_IGN);

    //2、先创建子进程,子进程再将自己变成独立的会话
    if(fork()>0)
    {
        exit(0);
    }
    setsid();//将自己变成独立的会话

    //3、更改当前调用进程的工作目录
    if(!cmd.empty())
    {
        chdir(cmd.c_str());
    }

    //4、标准输入,标准输出,标准错误重定向到 /dev/null
    int fd=open(nullfile.c_str(),O_RDWR);
    if(fd>0)
    {
        dup2(fd,0);
        dup2(fd,1);
        dup2(fd,2);
    }

}

以上就是简单的用于中英文翻译的TCP服务器的代码实现,你学会了吗?如果感觉到有所收获,那就点点赞,点点关注呗,后期还会持续更新网络编程的相关知识哦,我们下期见!!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue - 快速入门(一)

阅读文章可以收获&#xff1a; 1. 明白什么是vue 2. 如何创建一个vue实例 3. vue中的插值表达式如何使用 4. 如何安装vue的开发者工具 Vue 概念 什么是vue&#xff1f; Vue 是一个用于 构建用户界面 的 渐进式 框架 框架优点&#xff1a;大大提升开发效率 (70%↑) 缺点…

Spring Boot3,启动时间缩短 10 倍!

前面松哥写了一篇文章和大家聊了 Spring6 中引入的新玩意 AOT&#xff08;见Spring Boot3 新玩法&#xff0c;AOT 优化&#xff01;&#xff09;。 文章发出来之后&#xff0c;有小伙伴问松哥有没有做性能比较&#xff0c;老实说&#xff0c;这个给落下了&#xff0c;所以今天…

2024最新版MySQL安装使用指南

2024最新版MySQL安装使用指南 Installation and Usage Guide to the Latest Oracle MySQL in 2024 By JacksonML 1. MySQL简介 MySQL是世界上最受欢迎的开源数据库之一。MySQL属于Oracle&#xff08;甲骨文&#xff09;公司的产品&#xff0c;其具有强大的功能&#xff0c;但…

Vue-53、Vue技术vuex使用

vuex 是什么 1、概念 专门在Vue 中实现集中式状态&#xff08;数据&#xff09;管理的一个Vue 插件&#xff0c;对vue 应用中多个组件的共享状态进行集中式的 管理&#xff08;读/写&#xff09;&#xff0c;也是一种组件间通信的方式&#xff0c;且适用于任意组件间通信。2、…

《动手学深度学习(PyTorch版)》笔记7.5

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

rclone基础命令解析及实战

rclone命令解析及实战 1 rclone介绍&#xff1a;远程同步工具 rclone是一个开源的远程数据同步工具&#xff0c;由Golang编写&#xff0c;旨在在不同平台的文件系统和多种类型的对象存储产品之间提供数据同步功能。 它支持超过 40 种不同的云存储服务&#xff0c;包括 Amazon S…

常用Hallmark及KEGG、GO基因查询

文献&#xff1a;The Molecular Signatures Database (MSigDB) hallmark gene set collection - PMC (nih.gov) GSEA | MSigDB | Browse Human Gene Sets (gsea-msigdb.org)通过msigdb数据库可以查看各个Hallmark、KEGG、GO具体包含的基因细节。 Hallmark nameProcess categor…

C# Socket通信从入门到精通(21)——Tcp客户端判断与服务器断开连接的三种方法以及C#代码实现

前言 我们开发的tcp客户端程序在连接服务器以后,经常会遇到服务器已经关闭但是作为客户端的我们不知道,这时候应该应该有一个机制我们可以实时监测客户端和服务器已经断开连接,如果已经断开了连接,我们应该及时报警提示用户客户端和服务器已经断开连接,本文介绍三种可以监…

DAY12之滑动窗口最大值

今天内容有点超乎我的能力 直接放卡哥的讲解了 239. 滑动窗口最大值 - 力扣&#xff08;LeetCode&#xff09; 先看超时的暴力解法 class Solution { public:vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int>result; for(int …

新手养猫怎么挑选宠物空气净化器?猫用空气净化器测评推荐!

对于养猫的朋友来说&#xff0c;猫咪掉毛绝对是一个令人头痛的问题。猫毛和皮屑在室内飘散&#xff0c;不仅遍布各个角落&#xff0c;而且清理起来也相当费劲。尤其是那些顽固的猫毛&#xff0c;更是令人烦恼。更糟糕的是&#xff0c;这些毛发可能引起人体过敏反应&#xff0c;…

6.s081 学习实验记录(五)traps

文章目录 一、RISC-V assembly简介问题 二、Backtrace简介注意实验代码实验结果 三、Alarm简介注意实验代码实验结果 一、RISC-V assembly 简介 git checkout traps&#xff0c;切换到traps分支user/call.c 文件在我们输入 make fs.img 之后会被汇编为 call.asm 文件&#xf…

libev-ev_timer定时器的理解

1.相关说明 本文主要自己对于libev的ev_timer定时器的代码流程梳理&#xff0c;主要有ev_timer结构体定义变量的初始化&#xff0c;定时器变量的参数设置&#xff0c;定时器变量的使用 2.相关代码流程 下面是图片 3.相关实现代码 main.c #include <stdio.h> #include…

流浪动物救助|基于Springboot的流浪动物救助平台设计与实现(源码+数据库+文档)

流浪动物救助平台目录 目录 基于Springboot的流浪动物救助平台设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、动物信息管理 3、商品评论管理 4、公告信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设…

使用html2canvas截图踩坑总结

年底的移动端H5需求中&#xff0c;再次用到了html2canvas这个插件&#xff0c;这个插件主要是用来对网页进行截图&#xff0c;在项目需求中&#xff0c;有个交互的点&#xff0c;就是通过用户操作&#xff0c;将页面的内容截图保存下来&#xff0c;方便用户传播扩散。 H5说明&…

【初读论文】

这里写目录标题 万字长文解析深度学习中的术语面向小白的深度学习论文术语&#xff08;持续更新&#xff09;deepsolo不懂的知识pipelinebaselineRoI(Region of Interest)分类问题中的正例负例指示函数&#xff08;indicator function&#xff09;模型性能评估指标&#xff08;…

nginx+flask+Gunicorn反代理服务拿不到真实IP的解决

背景 本人在宝塔linux环境,要部署flask的简单后端并且用Ngnix反代理,用Gunicorn框架部署。(o(╥﹏╥)o中间磕磕绊绊总算部署上去了,需要了解Gunicorn怎么部署的朋友,评论区留言,我加补一篇介绍)。但是但是,我发现 其 accesslog日志里竟然是 127.0.0.1。这怎么能…

模拟钉钉官网动画

实现思路&#xff1a;利用粘性定位sticky&#xff0c;以及滚动事件实现。首先我们应该设置滚动动画开始位置和结束位置 &#xff0c;然后根据位置计算透明度或者transform&#xff0c;scale的值。 首先根据上述图线计算属性值&#xff0c;代码如下&#xff1a; function creat…

Python基础知识:Python模块

所谓模块(Module)&#xff0c;就是一种以“.py”为命名后缀的Python 文件&#xff0c;里面包含着很多集成的函数&#xff0c;可以很方便的被其他程序和脚本导入并使用。 如果模块理解为一辆汽车&#xff0c;我们使用汽车可以完成驾驶等工作&#xff0c;那么代码就是一个个细小…

Linux内存管理:(十二)Linux 5.0内核新增的反碎片优化

文章说明&#xff1a; Linux内核版本&#xff1a;5.0 架构&#xff1a;ARM64 参考资料及图片来源&#xff1a;《奔跑吧Linux内核》 Linux 5.0内核源码注释仓库地址&#xff1a; zhangzihengya/LinuxSourceCode_v5.0_study (github.com) 外碎片化发生时&#xff0c;页面分配…

day21网页布局

文章目录 块元素和行内元素列表标签表格标签媒体元素页面结构分析iframe内联框架 块元素和行内元素 块元素&#xff1a;无论内容多少&#xff0c;该元素独占一行。(p标签、h1~h6…) 行内元素&#xff1a;内容撑开宽度&#xff0c;左右都是行内元素的可以排在一行。&#xff08…