0


【在Linux世界中追寻伟大的One Piece】Socket编程TCP(续)

1 -> V2 -Echo Server多进程版本

通过每个请求,创建子进程的方式来支持多连接。

InetAddr.hpp

#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);
    }

    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;
};

TcpSever.hpp

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"

const static int default_backlog = 6; // TODO

class TcpServer : public nocopy
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }

    // 都是固定套路
    void Init()
    {
        // 1. 创建 socket, file fd, 本质是文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "create socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Fatal);
        }

        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
            SO_REUSEPORT, &opt, sizeof(opt));
        lg.LogMessage(Debug, "create socket success,
            sockfd: % d\n", _listensock);

        // 2. 填充本地网络信息并 bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 2.1 bind
        if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
        {
            lg.LogMessage(Fatal, "bind socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Bind_Err);
        }

        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
            _listensock);

        // 3. 设置 socket 为监听状态,tcp 特有的
        if (listen(_listensock, default_backlog) != 0)
        {
            lg.LogMessage(Fatal, "listen socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Listen_Err);
        }

        lg.LogMessage(Debug, "listen socket success,
            sockfd: % d\n", _listensock);
    }

    // Tcp 连接全双工通信的.
    void Service(int sockfd)
    {
        char buffer[1024];
        // 一直进行 IO
        while (true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << 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 ProcessConnection(int sockfd, struct sockaddr_in& peer)
    {
        // v2 多进程
        pid_t id = fork();
        if (id < 0)
        {
            close(sockfd);

            return;
        }
        else if (id == 0)
        {
            // child
            close(_listensock);
            if (fork() > 0)
                exit(0);
            InetAddr addr(peer); // 获取 client 地址信息
            lg.LogMessage(Info, "process connection: %s:%d\n",
                addr.Ip().c_str(), addr.Port());
            // 孙子进程,孤儿进程,被系统领养,正常处理
            Service(sockfd);
            close(sockfd);
            exit(0);
        }
        else
        {
            close(sockfd);
            pid_t rid = waitpid(id, nullptr, 0);
            if (rid == id)
            {
                // do nothing
            }
        }
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, CONV(&peer), &len);
            if (sockfd < 0)
            {
                lg.LogMessage(Warning, "accept socket error, errno
                    code: % d, error string : % s\n", errno, strerror(errno));
                    continue;
            }

            lg.LogMessage(Debug, "accept success, get n new
                sockfd: % d\n", sockfd);
                ProcessConnection(sockfd, peer);
        }
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock; // TODO
    bool _isrunning;
};

引入InetAddr.hpp,方便后面打印消息。

2 -> V3 -Echo Server多线程版本

Thread.hpp

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"

const static int default_backlog = 6; // TODO

class TcpServer : public nocopy
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }

    // 都是固定套路
    void Init()
    {
        // 1. 创建 socket, file fd, 本质是文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "create socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Fatal);
        }

        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
            SO_REUSEPORT, &opt, sizeof(opt));
        lg.LogMessage(Debug, "create socket success,
            sockfd: % d\n", _listensock);

        // 2. 填充本地网络信息并 bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 2.1 bind
        if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
        {
            lg.LogMessage(Fatal, "bind socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Bind_Err);
        }

        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
            _listensock);

        // 3. 设置 socket 为监听状态,tcp 特有的
        if (listen(_listensock, default_backlog) != 0)
        {
            lg.LogMessage(Fatal, "listen socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Listen_Err);
        }

        lg.LogMessage(Debug, "listen socket success,
            sockfd: % d\n", _listensock);
    }

    class ThreadData
    {
    public:
        ThreadData(int sockfd, struct sockaddr_in addr)
            : _sockfd(sockfd), _addr(addr)
        {}

        ~ThreadData()
        {}

    public:
        int _sockfd;
        InetAddr _addr;
    };

    // Tcp 连接全双工通信的.
    // 新增 static
    static void Service(ThreadData& td)
    {
        char buffer[1024];
        // 一直进行 IO
        while (true)
        {
            ssize_t n = read(td._sockfd, buffer, sizeof(buffer) -
                1);
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer <<
                    std::endl;

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

    static void* threadExcute(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        TcpServer::Service(*td);
        close(td->_sockfd);

        delete td;

        return nullptr;
    }

    void ProcessConnection(int sockfd, struct sockaddr_in& peer)
    {
        // v3 多线程
        InetAddr addr(peer);
        pthread_t tid;
        ThreadData* td = new ThreadData(sockfd, peer);
        pthread_create(&tid, nullptr, threadExcute, (void*)td);
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, CONV(&peer), &len);
            if (sockfd < 0)
            {
                lg.LogMessage(Warning, "accept socket error, errno
                    code: % d, error string : % s\n", errno, strerror(errno));
                    continue;
            }

            lg.LogMessage(Debug, "accept success, get n new
                sockfd: % d\n", sockfd);
                ProcessConnection(sockfd, peer);
        }
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock; // TODO
    bool _isrunning;
};

使用最原始的接口,使用内部ThreadData类。

3 -> V3-1 -多线程远程命令执行

Command.hpp

  • 命令类,用来执行命令,并获取结果。
  • 这里暂停,做一个多线程的小业务。
#pragma once

#include <iostream>
#include <string>
#include <set>
#include <unistd.h>

class Command
{
private:
public:
    Command() {}

    Command(int sockfd) : _sockfd(sockfd)
    {
        // 限定一下命令的个数和范围
        _safe_command.insert("ls");
        _safe_command.insert("pwd");
        _safe_command.insert("ls -l");
        _safe_command.insert("ll");
        _safe_command.insert("touch");
        _safe_command.insert("who");
        _safe_command.insert("whoami");
    }

    bool IsSafe(const std::string& command)
    {
        auto iter = _safe_command.find(command);
        if (iter == _safe_command.end()) 
            return false; // 要执行的命令不 set 中,不安全
        else 
            return true;
    }

    std::string Execute(const std::string& command)
    {
        if (!IsSafe(command)) 
            return "unsafe";

        FILE* fp = popen(command.c_str(), "r");
        if (fp == nullptr)
            return std::string();

        char buffer[1024];
        std::string result;
        while (fgets(buffer, sizeof(buffer), fp))
        {
            result += buffer;
        }

        pclose(fp);

        return result;
    }

    std::string RecvCommand()
    {
        char line[1024];
        ssize_t n = recv(_sockfd, line, sizeof(line) - 1, 0); 
        if (n > 0)
        {
            line[n] = 0;
            return line;
        }
        else
        {
            return std::string();
        }
    }

    void SendCommand(std::string result)
    {
        if (result.empty()) result = "done"; // 主要是有些命令没有结果,比如 touch
            send(_sockfd, result.c_str(), result.size(), 0);
    }

    ~Command()
    {
    }

private:
    std::set<std::string> _safe_command;
    int _sockfd;
    std::string _command;
};

TcpSever.hpp

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "Command.hpp" // 引入命令执行

const static int default_backlog = 6; // TODO

class TcpServer : public nocopy
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }

    // 都是固定套路
    void Init()
    {
        // 1. 创建 socket, file fd, 本质是文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "create socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Fatal);
        }

        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
            SO_REUSEPORT, &opt, sizeof(opt));
        lg.LogMessage(Debug, "create socket success,
            sockfd: % d\n", _listensock);

        // 2. 填充本地网络信息并 bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 2.1 bind
        if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
        {
            lg.LogMessage(Fatal, "bind socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Bind_Err);
        }

        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
            _listensock);

        // 3. 设置 socket 为监听状态,tcp 特有的
        if (listen(_listensock, default_backlog) != 0)
        {
            lg.LogMessage(Fatal, "listen socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Listen_Err);
        }

        lg.LogMessage(Debug, "listen socket success,
            sockfd: % d\n", _listensock);
    }

    class ThreadData
    {
    public:
        ThreadData(int sockfd, struct sockaddr_in addr)
            : _sockfd(sockfd), _addr(addr)
        {
        }

        ~ThreadData()
        {
        }

    public:
        int _sockfd;
        InetAddr _addr;
    };

    // Tcp 连接全双工通信的.
    // 新增 static
    static void Service(ThreadData& td)
    {
        char buffer[1024];
        // 一直进行 IO
        while (true)
        {
            Command command(td._sockfd);
            std::string commandstr = command.RecvCommand();
            if (commandstr.empty())
                return;

            std::string result = command.Execute(commandstr);
            command.SendCommand(result);
        }
    }

    static void* threadExcute(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        TcpServer::Service(*td);
        close(td->_sockfd);

        delete td;

        return nullptr;
    }

    void ProcessConnection(int sockfd, struct sockaddr_in& peer)
    {
        // v3 多线程
        InetAddr addr(peer);
        pthread_t tid;
        ThreadData* td = new ThreadData(sockfd, peer);
        pthread_create(&tid, nullptr, threadExcute, (void*)td);
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, CONV(&peer), &len);
            if (sockfd < 0)
            {
                lg.LogMessage(Warning, "accept socket error, errno
                    code: % d, error string : % s\n", errno, strerror(errno));
                    continue;
            }

            lg.LogMessage(Debug, "accept success, get n new
                sockfd: % d\n", sockfd);
                ProcessConnection(sockfd, peer);
        }
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock; // TODO
    bool _isrunning;
};

4 -> V4 -Echo Server线程池版本

引入系统部分的线程池,进行简单的业务处理。

TcpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

const static int default_backlog = 6; // TODO

class TcpServer : public nocopy
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }

    // 都是固定套路
    void Init()
    {
        // 1. 创建 socket, file fd, 本质是文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "create socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Fatal);
        }

        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
            SO_REUSEPORT, &opt, sizeof(opt));
        lg.LogMessage(Debug, "create socket success,
            sockfd: % d\n", _listensock);

        // 2. 填充本地网络信息并 bind
        // struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 2.1 bind
        if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
        {
            lg.LogMessage(Fatal, "bind socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Bind_Err);
        }

        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
            _listensock);

        // 3. 设置 socket 为监听状态,tcp 特有的
        if (listen(_listensock, default_backlog) != 0)
        {
            lg.LogMessage(Fatal, "listen socket error, errno
                code: % d, error string : % s\n", errno, strerror(errno));
                exit(Listen_Err);
        }

        lg.LogMessage(Debug, "listen socket success,
            sockfd: % d\n", _listensock);
    }

    // Tcp 连接全双工通信的.
    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 << "client say# " << 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[%s:%d] quit...\n",
                    addr.Ip().c_str(), addr.Port());
                break;
            }
            else
            {
                lg.LogMessage(Error, "read socket error, errno
                    code: % d, error string : % s\n", errno, strerror(errno));
                    break;
            }
        }
    }

    void ProcessConnection(int sockfd, struct sockaddr_in& peer)
    {
        using func_t = std::function<void()>;
        InetAddr addr(peer);
        func_t func = std::bind(&TcpServer::Service, this, sockfd,
            addr); // 这里不能 auto
        ThreadPool<func_t>::GetInstance()->Push(func);
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, CONV(&peer), &len);
            if (sockfd < 0)
            {
                lg.LogMessage(Warning, "accept socket error, errno
                    code: % d, error string : % s\n", errno, strerror(errno));
                    continue;
            }

            lg.LogMessage(Debug, "accept success, get n new
                sockfd: % d\n", sockfd);
                ProcessConnection(sockfd, peer);
        }

        _isrunning = false;
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock; // TODO
    bool _isrunning;
};

感谢各位大佬支持!!!

互三啦!!!

标签: linux 服务器 运维

本文转载自: https://blog.csdn.net/weixin_74809706/article/details/143464212
版权归原作者 枫叶丹4 所有, 如有侵权,请联系我们删除。

“【在Linux世界中追寻伟大的One Piece】Socket编程TCP(续)”的评论:

还没有评论