深入了解linux网络—— TCP网络通信(下)

前言

学习了TCP通信相关的接口使用,实现了基本的通信,现在来基于TCP通信实现翻译功能、远程SHELL

翻译功能

对于翻译功能,实现起来还是非常容易的,直接复用之前实现好的Dict类;

TcpServer中新增一个成员变量_func,表示未来信息的处理方法。

这样在server接收到信息时,只需回调_func即可。

cpp 复制代码
//tcpserver.hpp
using func_t = std::function<std::string(std::string)>;
class TcpServer
{
    void Server(int rwfd, InetAddr &addr)
    {
        while (true)
        {
            char english[256];
            int rn = read(rwfd, english, sizeof(english) - 1);
            if (rn < 0)
            {
                // read出错
                LOG(Level::ERROR) << "read error";
                break;
            }
            else if (rn == 0)
            {
                // write端退出
                LOG(Level::INFO) << "writer is exit";
                break;
            }
            // 读取成功
            english[rn] = '\0';
            std::string chinese = _func(english);
            std::cout << addr.ToString() << " : " << english << " -> " << chinese << std::endl;
            int wn = write(rwfd, chinese.c_str(), chinese.size());
            if (wn < 0)
            {
                LOG(Level::ERROR) << "write error";
                break;
            }
        }
    }
public:
    TcpServer(uint16_t port, func_t func) : _sockfd(-1), _port(port), _func(func)
    {
    }
    ~TcpServer() {}
    void Init()
    {
        // 1. socket
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(Level::FATAL) << "socket error";
            exit(1);
        }
        LOG(Level::DEBUG) << "socket success";
        // 2. bind
        InetAddr addr(_port);
        int b = bind(_sockfd, addr.GetInetAddr(), addr.GetLen());
        if (b < 0)
        {
            LOG(Level::FATAL) << "bind error";
            exit(2);
        }
        LOG(Level::DEBUG) << "bind success";
        int l = listen(_sockfd, 5);
        if (l < 0)
        {
            LOG(Level::FATAL) << "listen error";
            exit(3);
        }
        LOG(Level::DEBUG) << "listen success";
    }
    class ThreadData
    {
    public:
        ThreadData(int fd, TcpServer *tsvr, InetAddr addr)
            : _fd(fd), _tsvr(tsvr), _addr(addr)
        {
        }
        int _fd;
        TcpServer *_tsvr;
        InetAddr _addr;
    };
    static void *Routinue(void *argv)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(argv);
        td->_tsvr->Server(td->_fd, td->_addr);
        return nullptr;
    }
    void Start()
    {
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            bzero(&peer, len);
            int rwfd = accept(_sockfd, (struct sockaddr *)&peer, &len);
            if (rwfd < 0)
            {
                LOG(Level::FATAL) << "accept error";
                exit(4);
            }
            LOG(Level::DEBUG) << "accept success";
            // 多线程
            pthread_t tid;

            ThreadData *td = new ThreadData(rwfd, this, peer);
            pthread_create(&tid, nullptr, Routinue, td);
        }
    
private:
    int _sockfd;
    uint16_t _port;
    func_t _func;
};

这样在tcpserver启动时,先加载字典,然后创建TcpServer对象,启动服务端即可。

cpp 复制代码
//tcpserver.cc
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cout << "usage : " << argv[0] << " port" << std::endl;
        exit(1);
    }
    uint16_t port = std::stoi(argv[1]);
    Dict d;
    d.Load();
    TcpServer tsvr(port, [&d](std::string english) -> std::string
                   { return d.Translate(english); });
    tsvr.Init();
    tsvr.Start();
    return 0;
}

远程shell

这里简单实现远程shell,实现原理很简单:

简单设计一个InetShell类,其中存储着可以远程执行的命令(防止恶意操作)

再由InetShell通过一个方式去执行远端命令;

然后通过回调,在server接受到信息后,回调InetShell中的执行命令的方法;

最后,将执行的结果,通过返回值传给serverserver再将返回值发送给远端。

1. 设计InetShell

首先,在InetShell中要保存可以执行的命令(防止恶意操作),这里就直接使用unordered_set来存储。

cpp 复制代码
class InetShell
{
    InetShell()
    {
        _whitelist.insert("ls");
        _whitelist.insert("ll");
        _whitelist.insert("pwd");
        _whitelist.insert("who");
        _whitelist.insert("whoami");
        _whitelist.insert("touch test.txt");
        _whitelist.insert("mkdir test");
    }

private:
    std::unordered_set<std::string> _whitelist;
};

2. 执行命令

InetShell中,要实现一个执行命令的方法Execute

在执行该命令之前,就要先判断当前命令是否安全(是否在_whitelist中);

在确认安全后,就要执行命令:

这里执行命令,要命令行解析、进行程序替换等等;

这里就不做这些操作了,可以直接使用popenpopen:将传递进来的字符当中命令行信息,进行命令行解析,执行命令;最后执行结果以文件的形式返回一个FILE*类型的指针)

所以,这里在确认命令安全之后,就可以直接调用popen,将命令行信息传递进去,以读方式打开(r)。

然后就可以使用C语言文件读取的方法将执行结果读取出来(fgets);

将读取的内容拼接成字符串;读取完毕之后关闭文件即可。

cpp 复制代码
    std::string Execute(const std::string &com)
    {
        if (IsWhite(com))
            return "unsafe";
        FILE *fp = popen(com.c_str(), "r");
        if (fp == NULL)
            return std::string();
        char buff[1024];
        std::string result;
        while (fgets(buff, sizeof(buff), fp))
        {
            result += buff;
        }
        pclose(fp);
        return result;
    }

这样,在server中通过回调,执行该方法,获取执行结果,然后再将结果发送给远端。

这里就通过简单的服务(翻译、远程shell),来熟悉TCP通信。

补充

这里Tcp通信,所实现的TcpServerUDP通信所实现的UdpServer都是不希望被拷贝的;我们可以通过删除拷贝构造和拷贝赋值来保证不被拷贝;

但是这里就实现一个nocopy类,该类删除了拷贝构造和拷贝赋值,TcpServer继承nocopy类,从而TcpServer也就不能被拷贝了。

cpp 复制代码
class nocopy
{
public:
    nocopy() {}
    ~nocopy() {}
    nocopy(const nocopy &) = delete;
    const nocopy &operator=(const nocopy &) = delete;
};

此外,这里当程序出现问题,socket失败、bind失败调用exit退出,退出码都使用的是数字;这里就可以设计一个枚举类型,将退出码一个个列举出来,方便查错。

cpp 复制代码
enum ERR
{
    OK = 0,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
    ACCEPT_ERR,
    CONNECT_ERR,
    FORK_ERR
};

到这里,本篇文章内容就结束了,感谢支持

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
网络研究院3 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展