【Linux】基于tcp的简单服务器程序

目录

服务端

成员变量

成员函数

工作流程

客户端

头文件和命名空间

信号处理函数

使用说明和重试机制

访问服务器的函数

主函数

总结和注意事项

所用到的其他类

线程池

线程类

翻译业务类

禁止拷贝类

守护锁类

网络地址转换类

日志类

守护进程类


服务端

这个TcpServer类是一个简单的TCP服务器实现,用于监听指定端口上的连接请求,并处理客户端的连接和数据交互。

成员变量

  1. _port: 服务器监听的端口号,默认为8080。

  2. _listensock: 监听套接字的文件描述符。

  3. _isrunning: 表示服务器是否正在运行的布尔变量。

  4. funcs: 一个无序映射,用于存储注册的服务函数。键是服务名称,值是对应的服务处理函数。

成员函数

  1. 构造函数 (TcpServer): 接收监听套接字和端口号作为参数,但都有默认值,因此可以无参构造。构造函数仅初始化成员变量。

  2. Init(): 初始化服务器,包括创建套接字、设置套接字选项、绑定到指定端口、开始监听等步骤。如果在任何步骤中发生错误,程序会记录错误并退出。

  3. Start(): 启动服务器的主循环,接受连接请求。对于每个接受的连接,都会创建一个新的任务并将其添加到线程池中执行。

  4. Read(int sockfd): 从给定的套接字读取数据。如果读取成功,返回读取到的字符串;如果读取失败或连接已关闭,记录相应的日志。

  5. Routine(int sockfd, InetAddr addr): 处理来自客户端的连接。首先调用默认服务,然后读取客户端发送的数据类型,并根据数据类型调用相应的服务函数。

  6. DefaultService(int sockfd, InetAddr &addr): 默认服务函数,向客户端发送可用的服务列表。

  7. Service(int sockfd, InetAddr addr): 一个示例服务函数,接收来自客户端的消息并将其回显。

  8. RegisterFunc(const std::string &name, callback_t func): 允许用户注册自定义的服务函数。这些函数可以根据从客户端接收到的数据类型被调用。

  9. 析构函数 (~TcpServer): 目前析构函数为空,但在实际应用中,通常会在这里关闭套接字和释放资源。

工作流程

  1. 服务器通过Init()函数进行初始化,包括套接字的创建、绑定和监听。

  2. 调用Start()函数开始接受连接。对于每个接受的连接,都会创建一个新的任务来处理它。

  3. 在任务中,首先调用DefaultService向客户端发送可用的服务列表。

  4. 然后读取客户端发送的数据类型,并根据该类型调用相应的服务函数。

  5. 服务函数处理完客户端的请求后,关闭与客户端的连接。

这个类提供了一个基本的TCP服务器框架,用户可以通过注册自定义的服务函数来扩展其功能。注意,这个实现是同步的,每个连接都会占用一个线程来处理,因此在高并发环境下可能需要更高效的IO模型,如异步IO或多线程/多进程模型。

#pragma once

#include "ErrInfo.hpp"
#include "Log.hpp"
#include "NoCopy.hpp"
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <netinet/in.h>
#include <signal.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <unordered_map>

#include "Thread.hpp"
#include "ThreadPool.hpp"
#include"InetAddr.hpp"
#include"LockGuard.hpp"
#include"Translate.hpp"


static const int default_backlog = 5;
static const int DefaultListenSocket = -1;
static const uint16_t DefaultPort = 8080;

using task_t = std::function<void()>;
using callback_t = std::function<void(int, InetAddr&)>;

class TcpServer : public NoCopy
{
public:
  TcpServer(int listensocket = DefaultListenSocket, uint16_t port = DefaultPort)
      : _port(port),
        _listensock(listensocket),
        _isrunning(false)
  {
  }

  void Init()
  {
    // 创建套接字
    _listensock = socket(AF_INET, SOCK_STREAM, 0);
    if (_listensock < 0)
    {
      lg.LogMessage(Fatal, "创建套接字失败 错误码:%d  错误信息:%s \n", errno, strerror(errno));
      exit(Socket_Err);
    }
    lg.LogMessage(Info, "创建套接字成功!套接字:%d\n", _listensock);

    // 减少bind错误
    int opt = 1;
    setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

    // 填充信息
    sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_port = htons(_port);
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);

    // 进行绑定
    int n = bind(_listensock, (sockaddr *)&local, sizeof(local));
    if (n != 0)
    {
      lg.LogMessage(Fatal, "绑定失败! 错误码:%d, 错误信息:%s\n", errno, strerror(errno));
      exit(Bind_Err);
    }
    lg.LogMessage(Info, "绑定成功!\n");

    // 设置监听状态
    n = listen(_listensock, default_backlog);
    if (n != 0)
    {
      lg.LogMessage(Fatal, "监听失败!错误码:%d,错误信息:%s\n", errno, strerror(errno));
      exit(Listen_Err);
    }
    lg.LogMessage(Info, "监听成功!\n");

    ThreadPool<task_t>::GetInstance()->Start();
    funcs.insert(std::make_pair("defaultService", std::bind(&TcpServer::DefaultService, this, std::placeholders::_1, std::placeholders::_2)));
  }
  void Start()
  {
    _isrunning = true;
    signal(SIGCHLD, SIG_IGN);

    while (_isrunning)
    {
      sockaddr_in peer;
      memset(&peer, 0, sizeof(peer));
      socklen_t len = sizeof(peer);
      int sockfd = accept(_listensock, (sockaddr *)&peer, &len);
      if (sockfd < 0)
      {
        lg.LogMessage(Warning, "接受套接字失败, 错误码: %d, 错误信息: %s\n", errno, strerror(errno));
        continue;
      }
      lg.LogMessage(Debug, "接受套接字成功,新的套接字为: %d\n", sockfd);

      // 处理信息
      task_t t = std::bind(&TcpServer::Routine, this, sockfd, InetAddr(peer));
      ThreadPool<task_t>::GetInstance()->Push(t);
    }
  }

  std::string Read(int sockfd)
  {
    char type[1024];
    ssize_t n = read(sockfd, type, sizeof(type) - 1);
    if (n > 0)
    {
      type[n] = 0;
    }
    else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
    {
      lg.LogMessage(Info, "client quit...\n");
    }
    else
    {
      lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
    }
    return type;
  }

  void Routine(int sockfd, InetAddr addr)
  {
    funcs["defaultService"](sockfd, addr);
    std::string type = Read(sockfd);
    lg.LogMessage(Debug, "%s select %s\n", addr.PrintDebug().c_str(), type.c_str());

    if (type == "ping")
      funcs[type](sockfd, addr);
    else if (type == "translate")
      funcs[type](sockfd, addr);
    else if (type == "transform")
      funcs[type](sockfd, addr);
    else
    {
    }

    close(sockfd);
  }
  void DefaultService(const int sockfd, const InetAddr &addr)
  {
    (void)addr;
    std::string service_list = " |";
    for (auto func : funcs)
    {
      service_list += func.first;
      service_list += "|";
    }

    write(sockfd, service_list.c_str(), service_list.size());
  }

  void Service(int sockfd, InetAddr addr)
  {
    char buffer[1024];

    // 一直进行IO
    while (true)
    {
      ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
      if (n > 0)
      {
        buffer[n] = 0;
        std::cout << addr.PrintDebug() << "# " << buffer << std::endl;

        std::string echo_string = "server echo# ";
        echo_string += buffer;
        write(sockfd, echo_string.c_str(), echo_string.size());
      }
      else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
      {
        lg.LogMessage(Info, "client quit...\n");
        break;
      }
      else
      {
        lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
        break;
      }
    }
  }
  void RegisterFunc(const std::string &name, callback_t func)
  {
    funcs[name] = func;
  }
  ~TcpServer()
  {
  }

private:
  uint16_t _port;
  int _listensock;
  bool _isrunning;

  std::unordered_map<std::string, callback_t> funcs;
};

客户端

这个代码实现了一个简单的TCP客户端,用于连接到一个TCP服务器,获取服务器提供的服务列表,发送用户选择的服务,然后发送和接收消息。下面是对这个客户端类的详细介绍:

头文件和命名空间

代码首先包含了一系列必要的头文件,这些头文件提供了网络编程、字符串处理、输入输出等功能。然后使用了std命名空间,以简化代码中的标准库类型和函数的调用。

信号处理函数

handler函数是一个信号处理函数,用于处理接收到的信号。在这个例子中,它仅仅打印出接收到的信号编号,并退出程序。但这个函数在main函数中被设置为忽略SIGPIPE信号,这是为了防止在写已关闭的socket时程序异常退出。

使用说明和重试机制

Usage函数提供了程序的使用说明。如果用户没有提供正确的命令行参数,这个函数会被调用,显示如何使用这个程序。

程序中定义了一个重试计数Retry_Count,设置为5。这意味着如果连接服务器失败,客户端会尝试重新连接最多5次。

访问服务器的函数

visitServer函数是客户端的核心功能。它尝试连接到指定的服务器,并交换数据。这个函数的主要步骤如下:

  1. 创建Socket:使用socket函数创建一个TCP socket。

  2. 连接到服务器:使用服务器的IP地址和端口号连接到服务器。如果连接失败,会打印错误信息并返回false

  3. 读取服务列表:从服务器读取提供的服务列表,并显示给用户。

  4. 用户交互:提示用户选择一个服务,并将选择发送给服务器。然后等待用户输入一条消息,并将其发送给服务器。

  5. 接收服务器响应:读取并显示服务器的响应。如果服务器的响应是“quit”,则函数返回true,表示交互结束。

  6. 错误处理和资源清理:在函数结束时,关闭socket。

主函数

main函数是程序的入口点。它首先检查命令行参数的数量,确保用户提供了服务器的IP地址和端口号。然后,它进入一个循环,尝试连接到服务器。如果连接失败,会等待一秒钟后重试,直到达到最大重试次数。如果成功连接到服务器并完成了交互,循环会提前结束。

总结和注意事项

这个客户端程序是一个简单的TCP客户端示例,用于连接到TCP服务器并进行基本的文本交互。它展示了如何使用socket API进行网络通信,包括创建socket、连接到服务器、发送和接收数据等。

需要注意的是,这个程序没有进行复杂的错误处理或安全性检查。在实际应用中,应该添加更多的错误处理和安全性措施,例如检查输入的有效性、使用加密通信等。此外,这个程序使用了阻塞式的socket API,这意味着在读写数据时程序会停止执行直到操作完成。在需要高性能或并发处理的应用中,可能需要使用非阻塞式的socket API或异步I/O库。

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include "ErrInfo.hpp"

using namespace std;

void handler(int signo)
{
    std::cout << "signo: " << signo << std::endl;
    exit(0);
}

#define Retry_Count 5

void Usage(const std::string &process)
{
    std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}

bool visitServer(std::string &serverip, uint16_t &serverport, int *cnt)
{
    // 1. 创建socket
    string inbuffer;
    char service_list[1024];
    ssize_t m = 0;
    ssize_t n = 0;
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        cerr << "socket error" << endl;
        return false;
    }
    bool ret = true;

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串ip->4字节IP 2. 网络序列

    n = connect(sockfd, (sockaddr*)&server, sizeof(server)); 
    if (n < 0)
    {
        cerr << "connect error" << endl;
        ret = false;
        goto END;
    }
    *cnt = 0;

    m = read(sockfd, service_list, sizeof(service_list) - 1);
    if (m > 0)
    {
        service_list[m] = 0;
        cout << "服务器提供的服务列表是: " << service_list << endl;
    }

    cout << "请你选择服务#  ";
    getline(cin, inbuffer);
    write(sockfd, inbuffer.c_str(), inbuffer.size());

    cout << "Enter> ";
    getline(cin, inbuffer);
    if (inbuffer == "quit")
        return true;


    n = write(sockfd, inbuffer.c_str(), inbuffer.size());
    if (n > 0)
    {
        char buffer[1024];
        m = read(sockfd, buffer, sizeof(buffer) - 1);
        if (m > 0)
        {
            buffer[m] = 0;
            cout << buffer << endl;
        }
        else if (m == 0)
        {
            return true;
        }
        else
        {
            ret = false;
            goto END;
        }
    }
    else
    {
        std::cout << "hello write Error" << std::endl;
        ret = false;
        goto END;
    }

END:
    close(sockfd);
    return ret;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);

    signal(SIGPIPE, SIG_IGN);

    int cnt = 1;
    while (cnt <= Retry_Count)
    {
        bool result = visitServer(serverip, serverport, &cnt);
        if (result)
        {
            break;
        }
        else
        {
            sleep(1);
            std::cout << "server offline, retrying..., count : " << cnt << std::endl;
            cnt++;
        }
    }

    if (cnt >= Retry_Count)
    {
    std:
        cout << "server offline" << std::endl;
    }
    return 0;
}

所用到的其他类

线程池

#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"

static const int defaultnum = 10;

class ThreadData
{
public:
    ThreadData(const std::string &name) : threadname(name)
    {
    }
    ~ThreadData()
    {
    }

public:
    std::string threadname;
};

template <class T>
class ThreadPool
{
private:
    ThreadPool(int thread_num = defaultnum) : _thread_num(thread_num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        // 构建指定个数的线程
        for (int i = 0; i < _thread_num; i++)
        {
            // 待优化
            std::string threadname = "thread-";
            threadname += std::to_string(i + 1);

            ThreadData td(threadname);

            // Thread<ThreadData> t(threadname,
            //                      std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1), td);
            // _threads.push_back(t);
            _threads.emplace_back(threadname,
                                  std::bind(&ThreadPool<T>::ThreadRun, this,
                                            std::placeholders::_1),
                                  td);
            lg.LogMessage(Info, "%s is created...\n", threadname.c_str());
        }
    }
    ThreadPool(const ThreadPool<T> &tp) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T>) = delete;

public:
    // 有线程安全问题的
    static ThreadPool<T> *GetInstance()
    {
        if (instance == nullptr)
        {
            LockGuard lockguard(&sig_lock);
            if (instance == nullptr)
            {
                lg.LogMessage(Info, "创建单例成功...\n");
                instance = new ThreadPool<T>();
            }
        }

        return instance;
    }
    bool Start()
    {
        // 启动
        for (auto &thread : _threads)
        {
            thread.Start();
            lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());
        }

        return true;
    }
    void ThreadWait(const ThreadData &td)
    {
        lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadRun(ThreadData &td)
    {
        while (true)
        {
            // checkSelf()
            // checkSelf();
            // 取任务
            T t;
            {
                LockGuard lockguard(&_mutex);
                while (_q.empty())
                {
                    ThreadWait(td);
                    lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());
                }
                t = _q.front();
                _q.pop();
            }
            // 处理任务
            t();
            // lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",
            //               td.threadname, t.PrintTask().c_str(), t.PrintResult().c_str());
        }
    }
    void Push(T &in)
    {
        LockGuard lockguard(&_mutex);
        _q.push(in);
        ThreadWakeup();
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    // for debug
    void Wait()
    {
        for (auto &thread : _threads)
        {
            thread.Join();
        }
    }

private:
    std::queue<T> _q;
    std::vector<Thread<ThreadData>> _threads;
    int _thread_num;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *instance;
    static pthread_mutex_t sig_lock;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;

线程类

#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>

// 设计方的视角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;

template<class T>
class Thread
{
public:
    Thread(const std::string &threadname, func_t<T> func, T &data)
    :_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data)
    {}

    static void *ThreadRoutine(void *args) // 类内方法,
    {
        // (void)args; // 仅仅是为了防止编译器有告警
        Thread *ts = static_cast<Thread *>(args);

        ts->_func(ts->_data);

        return nullptr;
    }

    bool Start()
    {
        int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);
        if(n == 0) 
        {
            _isrunning = true;
            return true;
        }
        else return false;
    }
    bool Join()
    {
        if(!_isrunning) return true;
        int n = pthread_join(_tid, nullptr);
        if(n == 0)
        {
            _isrunning = false;
            return true;
        }
        return false;
    }
    std::string ThreadName()
    {
        return _threadname;
    }
    bool IsRunning()
    {
        return _isrunning;
    }
    ~Thread()
    {}
private:
    pthread_t _tid;
    std::string _threadname;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

翻译业务类

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include "Log.hpp"

const std::string unknown = "unknown";
const std::string mydict = "./recource/dict.txt";
const std::string sep = " ";



class Translate
{
public:
    Translate(std::string dict_path = mydict) : _dict_path(dict_path)
    {
        LoadDict();
        Parse();
    }
    void LoadDict()
    {
        std::ifstream in(_dict_path);
        std::string line;
        while(std::getline(in, line))
        {
            lines.push_back(line);
        }
        in.close();
        lg.LogMessage(Debug, "Load dict txt success, path: %s\n", _dict_path.c_str());
    }
    void Parse()
    {
        for(auto &line : lines)
        {
            auto pos = line.find(sep); 
            if(pos == std::string::npos) continue;
            else
            {
                std::string word = line.substr(0, pos);
                std::string chinese = line.substr(pos+sep.size());
                _dict.insert(std::make_pair(word, chinese));
            }
        }
        lg.LogMessage(Debug, "Parse dict txt success, path: %s\n", _dict_path.c_str());
    }
    void debug()
    {
        // for(auto &line : lines)std::cout << line << std::endl;

        for(auto &elem : _dict)
        {
            std::cout << elem.first << " : " << elem.second << std::endl;
        }
    }
    std::string Excute(const std::string &word)
    {
        auto iter = _dict.find(word);
        if (iter == _dict.end())
            return unknown;
        else
            return _dict[word];
    }
    ~Translate()
    {
    }

private:
    std::string _dict_path;
    std::unordered_map<std::string, std::string> _dict;
    std::vector<std::string> lines;
};

禁止拷贝类

#pragma once

#include<iostream>
class NoCopy
{
  public:
  NoCopy()
  {}
  NoCopy(const NoCopy&) = delete;
  NoCopy& operator=(const NoCopy&) = delete;
  ~NoCopy()
  {}
};

守护锁类

#pragma once

#include <pthread.h>

// 不定义锁,默认认为外部会给我们传入锁对象
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock):_lock(lock)
    {}
    void Lock()
    {
        pthread_mutex_lock(_lock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(_lock);
    }
    ~Mutex()
    {}

private:
    pthread_mutex_t *_lock;
};

class LockGuard
{
public:
    LockGuard(pthread_mutex_t *lock): _mutex(lock)
    {
        _mutex.Lock();
    }
    ~LockGuard()
    {
        _mutex.Unlock();
    }
private:
    Mutex _mutex;
};

网络地址转换类

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

class InetAddr
{
public:
    InetAddr(struct sockaddr_in &addr):_addr(addr)
    {
        _port = ntohs(_addr.sin_port);
        // _ip = inet_ntoa(_addr.sin_addr); //char *inet_ntoa(struct in_addr in),返回的是字符串地址,那么字符串在哪里?
        char ipbuffer[64];
        inet_ntop(AF_INET, &addr.sin_addr, ipbuffer, sizeof(ipbuffer)); // 1. 网络转本机 2. 4字节ip,字符串风格的ip
        _ip = ipbuffer;
    }
    std::string Ip() {return _ip;}
    uint16_t Port() {return _port;};
    std::string PrintDebug()
    {
        std::string info = _ip;
        info += ":";
        info += std::to_string(_port);  // "127.0.0.1:4444"
        return info;
    }
    const  struct sockaddr_in& GetAddr()
    {
        return _addr;
    }
    bool operator == (const InetAddr&addr)
    {
        //other code
        return this->_ip == addr._ip && this->_port == addr._port;
    }
    ~InetAddr(){}
private:
    std::string _ip;
    uint16_t _port;
    struct sockaddr_in _addr;
};

日志类

#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

enum
{
  Debug = 0,
  Info,
  Warning,
  Error,
  Fatal
};

enum
{
  Screen = 10,
  OneFile,
  ClassFile
};

std::string LevelToString(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 "Unknown";
  }
}

const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";

class Log
{
public:
  Log() : style(defaultstyle), filename(default_filename)
  {
    mkdir(logdir.c_str(), 0775);
  }
  void Enable(int sty) //
  {
    style = sty;
  }
  std::string TimeStampExLocalTime()
  {
    time_t currtime = time(nullptr);
    struct tm *curr = localtime(&currtime);
    char time_buffer[128];
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
             curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,
             curr->tm_hour, curr->tm_min, curr->tm_sec);
    return time_buffer;
  }
  void WriteLogToOneFile(const std::string &logname, const std::string &message)
  {
    umask(0);
    int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
    if (fd < 0)
      return;
    write(fd, message.c_str(), message.size());
    close(fd);
  }
  void WriteLogToClassFile(const std::string &levelstr, const std::string &message)
  {
    std::string logname = logdir;
    logname += "/";
    logname += filename;
    logname += levelstr;
    WriteLogToOneFile(logname, message);
  }

  void WriteLog(const std::string &levelstr, const std::string &message)
  {
    switch (style)
    {
    case Screen:
      std::cout << message;
      break;
    case OneFile:
      WriteLogToClassFile("all", message);
      break;
    case ClassFile:
      WriteLogToClassFile(levelstr, message);
      break;
    default:
      break;
    }
  }
  void LogMessage(int level, const char *format, ...) // 类C的一个日志接口
  {
    char leftbuffer[1024];
    std::string levelstr = LevelToString(level);
    std::string currtime = TimeStampExLocalTime();
    std::string idstr = std::to_string(getpid());

    char rightbuffer[1024];
    va_list args; // char *, void *
    va_start(args, format);
    // args 指向了可变参数部分
    vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
    va_end(args); // args = nullptr;
    snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",
             levelstr.c_str(), currtime.c_str(), idstr.c_str());

    std::string loginfo = leftbuffer;
    loginfo += rightbuffer;
    WriteLog(levelstr, loginfo);
  }

  ~Log() {}

private:
  int style;
  std::string filename;
};

Log lg;

class Conf
{
public:
  Conf()
  {
    lg.Enable(ClassFile);
  }
  ~Conf()
  {
  }
};

Conf conf;

守护进程类

#pragma once

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

const char *root = "/";
const char *dev_null = "/dev/null";

void Daemon(bool ischdir, bool isclose)
{
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    if (fork() > 0)
        exit(0);

    setsid();

    if (ischdir)
        chdir(root);

    if (isclose)
    {
        close(0);
        close(1);
        close(2);
    }
    else
    {
        int fd = open(dev_null, O_RDWR);
        if (fd > 0)
        {
            dup2(fd, 0);
            dup2(fd, 1);
            dup2(fd, 2);
            close(fd);
        }
    }
}

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

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

相关文章

实验报告5-Spring MVC实现页面

实验报告5-SpringMVC实现页面 一、需求分析 使用Spring MVC框架&#xff0c;从视图、控制器和模型三方面实验动态页面。模拟实现用户登录&#xff0c;模拟的用户名密码以模型属性方式存放在Spring容器中&#xff0c;控制器相应用户请求并映射参数&#xff0c;页面收集用户数据或…

设计模式-01 设计模式单例模式

设计模式-01 设计模式单例模式 目录 设计模式-01 设计模式单例模式 1定义 2.内涵 3.使用示例 4.具体代码使用实践 5.注意事项 6.最佳实践 7.总结 1 定义 单例模式是一种设计模式&#xff0c;它确保一个类只能被实例化一次。它通过在类内部创建类的唯一实例并提供一个全…

uniapp + uView动态表单校验

项目需求&#xff1a;动态循环表单&#xff0c;并实现动态表单校验 页面&#xff1a; <u--form label-position"top" :model"tmForm" ref"tmForm" label-width"0px" :rulesrules><div v-for"(element, index) in tmForm…

(详细整理!!!!)Tensorflow与Keras、Python版本对应关系!!!

小伙伴们大家好&#xff0c;不知道大家有没有被tensorflow框架困扰过 今天我就给大家整理一下tensorflow和keras、python版本的对应关系 大家这些都可以在官网找到&#xff0c;下面我把官网的连接给大家放在这里&#xff1a;在 Windows 环境中从源代码构建 | TensorFlow (g…

搭建大型分布式服务(三十七)SpringBoot 整合多个kafka数据源-取消限定符

系列文章目录 文章目录 系列文章目录前言一、本文要点二、开发环境三、原项目四、修改项目五、测试一下五、小结 前言 本插件稳定运行上百个kafka项目&#xff0c;每天处理上亿级的数据的精简小插件&#xff0c;快速上手。 <dependency><groupId>io.github.vipjo…

基于 React 的图形验证码插件

react-captcha-code NPM 地址 &#xff1a; react-captcha-code - npm npm install react-captcha-code --save 如下我自己的封装&#xff1a; import Captcha from "react-captcha-code";type CaptchaType {captchaChange: (captchaInfo: string) > void;code…

前端发起网络请求的几种常见方式(XMLHttpRequest、FetchApi、jQueryAjax、Axios)

摘要 前端发起网络请求的几种常见方式包括&#xff1a; XMLHttpRequest (XHR)&#xff1a; 这是最传统和最常见的方式之一。它允许客户端与服务器进行异步通信。XHR API 提供了一个在后台发送 HTTP 请求和接收响应的机制&#xff0c;使得页面能够在不刷新的情况下更新部分内容…

Flutter笔记:Widgets Easier组件库(2)阴影盒子

Flutter笔记 Widgets Easier组件库&#xff08;2&#xff09;&#xff1a;阴影盒子 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…

Python中的动态数据可视化Bokeh库实战

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python 中的动态数据可视化Bokeh库实战 在数据科学和可视化领域&#xff0c;动态数据可视化…

windows下安装onlyoffice

文章目录 1、 安装ErLang2、 安装rabbitmq3、 安装postgresql4、 安装onlyoffice(社区版) 1、 安装ErLang 下载地址&#xff1a;https://erlang.org/download/otp_win64_24.2.exe opt_wind64_24.2.exe 直接运行&#xff0c;一步一步安装 2、 安装rabbitmq 下载地址&#xf…

【笔记】Simulink与Workbench交互+自定义m函数封装为Simulink模块

以如下三角函数为例&#xff0c;说明建模方法 ya*sin(b*2*pi*uc);0.总模型总代码 总模型 总代码&#xff1a; clc clear close allt_all10; a10; b1; c0;%pi/2; delta_t0.01; simOutsim(test240430); out_tsimOut.tout; out_y1simOut.yout{1}.Values; out_y2simOut.yout{2}.…

C++-10

1.C一个程序&#xff0c;实现两个类&#xff0c;分别存放输入的字符串中的数字和字母&#xff0c;并按各自的顺序排列&#xff0c; 类中实现-一个dump函数&#xff0c;调C用后输出类中当前存放的字符串结果。 例如&#xff0c;输入1u4y2a3d,输出:存放字母的类&#xff0c;输出a…

机器人正反向运动学(FK和IK)

绕第一个顶点可以沿Z轴转动&#xff0c;角度用alpha表示 绕第二个点沿X轴转动&#xff0c;角度为Beta 第三个点沿X轴转动&#xff0c;记作gama 这三个点构成姿态&#xff08;pose&#xff09; 我们记第一个点为P0&#xff0c;画出它的本地坐标系&#xff0c;和世界坐标系一样红…

无人机+三维建模:倾斜摄影技术详解

无人机倾斜摄影测量技术是一项高新技术&#xff0c;近年来在国际摄影测量领域得到了快速发展。这种技术通过从一个垂直和四个倾斜的五个不同视角同步采集影像&#xff0c;从而获取到丰富的建筑物顶面及侧视的高分辨率纹理。这种技术不仅能够真实地反映地物情况&#xff0c;还能…

设计模式 --6组合模式

文章目录 组合模式应用场景组合模式概念组合模式结构图透明方式和安全方式什么时候使用组合模式公司管理系统使用 组合模式来构架组合模式的好处 组合模式应用场景 整体和部分可以被一致性对待 比如人力资源部 财务部的管理功能可以复用于分公司的功能 可以引入一种 树状的结构…

【webrtc】MessageHandler 2: 基于线程的消息处理:以PeerConnectionClient为例

PeerConnectionClient 前一篇 nullaudiopoller 并么有场景线程,而是就是在当前线程直接执行的, PeerConnectionClient 作为一个独立的客户端,默认的是主线程。 PeerConnectionClient 同时维护客户端的信令状态,并且通过OnMessage实现MessageHandler 消息处理。 目前只处理一…

AI大模型日报#0430:疑似GPT4.5模型刷屏、上交实现「蛋白质功能定向进化」、微软紧急撤回WizardLM-2

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了今日要点以及每条资讯的摘要。 《AI大模型日报》今日要点&#xff1a; 在AI大模型领域&#xff0c;多项研究进展和行业应用动态引发关注。一夜之间&#x…

Gateway Predicate断言(谓词)

是什么 Spring Cloud Gateway匹配路由作为Spring WebFlux HandlerMapping基础设施的一部分。 Spring Cloud Gateway包含许多内置的路由谓词工厂。 所有这些谓词都匹配HTTP请求的不同属性。 您可以使用逻辑 and 语句来联合收割机组合多个路由谓词工厂。 Predicate就是为了实现一…

sum函数搭配group by /having的案例说明

记录一些常用的函数及用法 --查询份额大于1w的投资人信息。 聚合数据的筛选&#xff1a;当你需要基于聚合函数&#xff08;如 SUM(), AVG(), MAX(), MIN(), COUNT() 等&#xff09;的结果来过滤记录时&#xff0c;使用 HAVING 子句。 组合条件&#xff1a;HAVING 子句可以使用…

Nginx配置Https缺少SSL模块

1、Linux下Nginx配置https nginx下载和安装此处就忽略&#xff0c;可自行百度 1.1、配置https 打开nginx配置文件 vim /opt/app/nginx/conf/nginx.conf相关https配置 server {listen 443 ssl; #开放端口server_name echarts.net;#域名#redirect to https#ssl on; #旧版#ssl证…