深入解析:Socket编程TCP

目录

1.TCP⽹络编程

1.1 服务端初始化实现

1.2 服务端运行

1.3 客户端初始化

1.4 客户端运行

2. 服务端多进程运行版本

3. 服务端多线程运行版本

4. 服务端进程池运行版本

4.1 线程池封装代码

4.2 线程封装代码

4.3 互斥锁封装代码

4.4 条件变量封装代码

4.5 服务端进程池运行代码

5. 基于TCP⽹络编程实现字典翻译的功能

5.1 引入字典文件,类

5.2 代码实现

6. 基于TCP⽹络编程实现远程命令执⾏

6.1 命令类

6.2 代码实现


TCP ( Transmission Control Protocol 传输控制协议)

传输层协议
有连接(例如生活中双方打电话,此时首先双方确认对方是否听得见,此时就是在建立连接)
可靠传输(如果丢包,再次发送,维持可靠传输,需要做更多工作,复杂,占有资源多)
⾯向字节流(水流,连续的,如果要接水,自行分配如何接)

1.TCP⽹络编程

1.1 服务端初始化实现

这里TCP是有连接的,对服务端要进行监听,例如生活中去餐厅吃饭,此时就需要一个前台来持续等待新客户到来,此时前台就处于监听状态,而TCP是有连接的,因此此时需要把自身设为listen状态,等待别人连接,监听函数如下

说明:
使得TCP服务器能够监听来自客户端的连接请求,它为服务器端接收客户端连接做准备。
#include 
int listen(int sockfd, int backlog);
参数:
sockfd 是通过socket函数创建的套接字文件描述符。
backlog 参数指定了队列中最多可以容纳多少等待处理的连接
#include"Common.hpp"//错误码类
#include"Log.hpp"//日志类
#include"inerAddr.hpp"//地址转换类
class TcpServer:public NoCopy//禁止对服务器拷贝
{
    public:
        TcpServer(uint16_t port,bool isRun=true)
        :_port(port) ,
        _isRun(isRun)
        {}
        void Init()
        {
            //创建套接字
            int _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::ERROR) << "create socket failed";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::INFO) << "create socket success";
            //绑定地址
            inerAddr lock(_port);
            int n=bind(_sockfd, (struct sockaddr*)&lock.NetAddr(), sizeof(lock.NetAddr()));
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "bind socket failed";
                exit(BIND_ERR);
            }
            LOG(LogLevel::INFO) << "bind socket success";
            //监听
            int n=listen(_sockfd, 8);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen socket failed";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::INFO) << "listen socket success";
        }
        void Run()
        {
        }
        ~TcpServer()
        {}
    private:
        uint16_t _port;
        bool _isRun;
};
#include"inerAddr.hpp"
#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//获取IP地址和端口号类
class inerAddr
{
public:
    inerAddr(struct sockaddr_in &addr) : _addr(addr)
    {
        //网络转主机字节序
        _port = ntohs(_addr.sin_port);
        //_ip = inet_ntoa(_addr.sin_addr);
        char ipbuffer[64];
        inet_ntop(AF_INET, &_addr.sin_addr, ipbuffer, sizeof(ipbuffer));
        _ip = ipbuffer;
    }
    inerAddr(const std::string &ip, uint16_t port): _ip(ip), _port(port)
    {
        //主机字节序转网络字节序
        _addr.sin_port = htons(_port);
        //_addr.sin_addr.s_addr = inet_addr(_ip.c_str());
        inet_pton(AF_INET, _ip.c_str(), &_addr.sin_addr);
    }
    inerAddr(uint16_t port) :_port(port),_ip()
    {
        // 主机转网络
        memset(&_addr, 0, sizeof(_addr));
        _addr.sin_family = AF_INET;
        _addr.sin_addr.s_addr = INADDR_ANY;
        _addr.sin_port = htons(_port);
    }
    uint16_t getPort() const { return _port; }
    std::string getIP() const { return _ip; }
    const struct sockaddr_in &NetAddr() { return _addr; }
    ~inerAddr() {}
private:
    struct sockaddr_in _addr;
    std::string _ip;
    uint16_t _port;
};

1.2 服务端运行

对服务端初始化后,此时socket就可以进行建立连接,首先就要获取连接,函数如下

#include 
#include 
说明:accept()等待客户端连接,用来与s参数建立连接
int accept(int s, struct sockaddr * addr, int * addrlen);
参数:
s:需要建立连接的文件描述符
addr:输出型参数,保存发起连接请求的那个客户端的IP和端口
addrlen:输出型参数,表示第二个参数addr的大小
返回值:成功则返回新的socket 处理代码, 失败返回-1,。
注意:
accept获取的链接直接从内核获取的,建立连接的过程与accept无关
参数s只负责获取连接--》listensockfd,
accept返回的socket才是用来进行处理服务和数据的

对于TCP来说处理用户数据时,和文件读写的方式相同

        void Service(int sockfd, inerAddr &client)
        {
            //处理客户端请求
            char recvbuf[1024] = {0};
            while(true)
            {
                //接收数据
                ssize_t n=read(sockfd, recvbuf, sizeof(recvbuf)-1);
                if (n > 0)
                {
                    recvbuf[n] = '\0';
                    LOG(LogLevel::INFO) << "recv data from client: " << recvbuf;
                    //发送数据
                    std::string echo_string="echo#";
                    echo_string+=recvbuf;
                    write(sockfd, echo_string.c_str(), echo_string.size());
                }
                else if (n == 0)
                {
                    //客户端关闭连接
                    LOG(LogLevel::INFO) << "client close connection";
                    close(sockfd);
                    break;
                }
                else
                {
                    //出错
                    LOG(LogLevel::ERROR) << "recv data failed";
                    close(sockfd);
                    break;
                }
            }
        }
        void Run()
        {
            _isRun=true;
            while (_isRun)
            {
                //获取连接,等待客户端连接
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                //如果没有客户端连接,则阻塞等待
                int sockfd = accept(_listensockfd, (struct sockaddr*)&client_addr, &client_addr_len);
                if (sockfd < 0)
                {
                    LOG(LogLevel::ERROR) << "accept socket failed";
                    continue;
                }
                inerAddr client(client_addr);
                LOG(LogLevel::INFO) << "accept socket success, client ip: " << client.getIP() << ", port: " << client.getPort();
                //处理客户端请求
                Service(sockfd, client);//测试
            }
            _isRun=false;
        }

1.3 客户端初始化

对于TCP的客户端,创建套接字和UDP一样,同样也不需要显示bind,同时客户端也不需要监听和获取连接,客户端应直接向服务端发送连接请求

发送连接函数如下

说明:
向服务端发起连接请求
int connect(int sockcd, const struct sockaddr *addr, int addrlen);
参数:
sockcd:套接字
addr:用于指定所要连接的服务器的地址
addrlen:为addr变量的大小
返回值:
成功返回0,失败返回-1。
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        std::cout<<"Usage: "<

1.4 客户端运行

TCP客户端运行其实就和文件操作类似,调用系统的文件接口,进行使用

    while (true)
    {
        std::string msg;
        std::cout<<"Please input:#";
        std::getline(std::cin,msg);
        write(sockfd,msg.c_str(),msg.size());
        char buf[1024];
        int n=read(sockfd,buf,sizeof(buf)-1);
        if(n>0)
        {
            buf[n]='\0';
            std::cout<<"server echo#"<

2. 服务端多进程运行版本

对于服务端,不可能只接受一个客户端,因此需要创建多进程来进行多用户访问,实现如下

        void Run()
        {
            _isRun=true;
            while (_isRun)
            {
                //获取连接,等待客户端连接
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                //如果没有客户端连接,则阻塞等待
                int sockfd = accept(_listensockfd, (struct sockaddr*)&client_addr, &client_addr_len);
                if (sockfd < 0)
                {
                    LOG(LogLevel::ERROR) << "accept socket failed";
                    continue;
                }
                inerAddr client(client_addr);
                LOG(LogLevel::INFO) << "accept socket success, client ip: " << client.getIP() << ", port: " << client.getPort();
                //创建子进程
                pid_t id=fork();
                if (id < 0)
                {
                    LOG(LogLevel::ERROR) << "fork process failed";
                    exit(FORK_ERR);
                }
                else if (id == 0)
                {
                    //子进程处理客户端请求
                    //关闭监听套接字,这是由于子进程不需要监听套接字
                    //不关也可以但是会有风险
                    close(_listensockfd);
                    if(fork()>0)
                    {
                        exit(OK);//子进程退出
                    }
                    //此时孙子进程来处理,由于子进程退出,变成了孤儿进程,由系统回收
                    Service(sockfd, client);
                    exit(OK);
                }
                else
                {
                    //父进程继续等待子进程结束
                    close(sockfd);//关闭连接套接字
                    waitpid(id, nullptr, 0);//此时由于子进程退出,父进程不用在这里阻塞,继续监听
                }
            }
            _isRun=false;
        }

3. 服务端多线程运行版本

        class ThreadData//内部类,为了传参给任务函数
        {
            public:
                ThreadData(TcpServer *server, int sockfd, inerAddr& client)
                :server(server), sockfd(sockfd), client(client)
                {}
            public:
                TcpServer *server;//能够访问类外函数
                //下面是传参用的
                int sockfd;
                inerAddr client;
        };
        static void *Roetine(void*args)//任务函数
        {
            ThreadData *data=static_cast(args);
            data->server->Service(data->sockfd, data->client);
            delete data;
            return nullptr;
        }
        void Run()
        {
            _isRun=true;
            while (_isRun)
            {
                //获取连接,等待客户端连接
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                //如果没有客户端连接,则阻塞等待
                int sockfd = accept(_listensockfd, (struct sockaddr*)&client_addr, &client_addr_len);
                if (sockfd < 0)
                {
                    LOG(LogLevel::ERROR) << "accept socket failed";
                    continue;
                }
                inerAddr client(client_addr);
                LOG(LogLevel::INFO) << "accept socket success, client ip: " << client.getIP() << ", port: " << client.getPort();
                //创建线程
                ThreadData *data=new ThreadData(this, sockfd, client);
                pthread_t tid;
                pthread_create(&tid, nullptr, Roetine, data);
            }
            _isRun=false;
        }

4. 服务端进程池运行版本

对于用户要进行长服务: 多进程多线程比较合适,但是短服务:线程池就比较好了,首先如下是进程池封装代码及线程池相关的封装代码

4.1 线程池封装代码

#pragma once
#include 
#include "Mutex.hpp" //互斥锁
#include "code.hpp"  //线程
#include "Cond.hpp"  //条件变量
#include "Log.hpp"   //日志
#include 
#include 
#include
namespace ThreanPoolModuls
{
    using namespace chuxin;
    using namespace MutexMudule;
    using namespace LogModuls;
    static const int gnum = 5; // 线程池中线程数量
    template 
    class ThreadPool
    {
    private:
        void WakeUpAllThread()
        {
            // 加锁
            Condition lock(_mutex);
            // 通知所有线程
            if (_waitnum)
                _cond.Broadcast();
        }
        void WakeUpOne()
        {
            _cond.Cond_signal_wait();
        }
         void HandlerTask()
        {
            char name[128];
            pthread_getname_np(pthread_self(), name, sizeof(name)); // 获取线程名
            while (true)
            {
                T t; // 任务
                {
                    Condition lock(_mutex);              // 互斥锁
                    while (_task_.empty() && _isStarted) // 任务队列为空
                    {
                        _waitnum++; // 等待线程数量加1
                        _cond.Wait(_mutex); // 条件变量等待
                        _waitnum--; // 等待线程数量减1
                    }
                    if (!_isStarted && _task_.empty())
                    {
                        break;
                    }
                    t = _task_.front(); // 取出任务
                    _task_.pop();
                }
                t(); // 执行任务
            }
        }
         ThreadPool(int num = gnum, bool isStart = false, int waitnum = 0)
            : _num(num),
              _isStarted(isStart),
              _waitnum(waitnum)
        {
            for (int i = 0; i < num; i++)
            {
                _thread.emplace_back([this]()
                                     {
                                         HandlerTask(); // 处理任务
                                     });
            }
        }
    public:
        void Start()
        {
            if (_isStarted)
            {
                return;
            }
            _isStarted = true;
            for (auto &t : _thread)
            {
                t.Start();
            }
        }
        void Join() // 等待所有线程退出
        {
            for (auto &t : _thread)
            {
                t.Join();
            }
        }
        void Stop()
        {
            if (!_isStarted)
            {
                return;
            }
            _isStarted = false;
            // 首先唤醒所有线程,让它们退出,如果有任务在队列中,则处理完后再退出
            WakeUpAllThread();
        }
        bool Enqueue(const T &in) // 提供给外部线程调用,将任务放入队列
        {
            if (_isStarted)
            {
                Condition lock(_mutex);
                _task_.push(in);
                if (_waitnum == _thread.size())
                    WakeUpOne(); // 唤醒一个线程
                return true;
            }
            return false;
        }
    private:
        //对拷贝构造函数和赋值运算符进行删除
        ThreadPool(const ThreadPool &) = delete;
        ThreadPool &operator=(const ThreadPool &) = delete;
    public:
        static ThreadPool *GetInstance()
        {
            if (inc == nullptr)
            {
                Condition lockguard(_lock);
                if (inc == nullptr)
                {
                    inc = new ThreadPool();
                    inc->Start();
                }
            }
            return inc;
        }
    private:
        std::vector _thread; // 线程池
        int _num;                    // 线程数量
        std::queue _task_; // 任务队列
        Mutex _mutex;         // 互斥锁
       Cond _cond; // 条件变量
        bool _isStarted; // 线程池是否启动
        int _waitnum;    // 等待任务的线程数量
        static ThreadPool *inc; // 单例指针
        static Mutex _lock;
    };
    //静态变量类外初始化
    template 
    ThreadPool *ThreadPool::inc = nullptr;// 线程池实例
    template 
    Mutex ThreadPool::_lock;// 互斥锁
}

4.2 线程封装代码

#ifndef _THREAD_H_
#define _THREAD_H_
#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
namespace chuxin
{
    static uint32_t num = 1;
    class Thread
    {
        using func_t = std::function;
    private:
        void Running()
        {
            _running = true;
        }
        void Detached()
        {
            _detached = true;
        }
        static void *Routine(void *arg)
        {
            Thread *p = static_cast(arg);
            p->Running();
            if (p->_detached)
                p->Detach();
            pthread_setname_np(p->_tid, p->_name.c_str());
            p->_func();
            return nullptr;
        }
    public:
        Thread(func_t func)
            : _tid(0), _running(false), _detached(false), _func(func), _res(nullptr)
        {
            _name = "thread-" + std::to_string(num++);
        }
        ~Thread()
        {
        }
        void Detach()
        {
            if (_detached)
                return;
            if (_running)
                pthread_detach(_tid);
            Detached();
        }
        bool Start()
        {
            if (_running)
                return false;
            int n = pthread_create(&_tid, NULL, Routine, this); // 这里this
            if (n != 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        bool Stop()
        {
            if (_running)
            {
                int n = pthread_cancel(_tid);
                if (n != 0)
                {
                    return false;
                }
                else
                {
                    _running = false;
                    return true;
                }
            }
            return false;
        }
        void Join()
        {
            if (_detached)
            {
                return;
            }
            int n = pthread_join(_tid, &_res);
            if (n != 0)
            {
            }
            else
            {
            }
        }
         pthread_t Id()
        {
            return _tid;
        }
    private:
        pthread_t _tid;
        std::string _name;
        bool _running;
        bool _detached;
        void *_res;
        func_t _func;
    };
}
#endif

4.3 互斥锁封装代码

#pragma once
#include
#include
namespace MutexMudule
{
    class Mutex
    {
    public:
        Mutex()
        {
            pthread_mutex_init(&m_mutex, NULL);
        }
        void lock()
        {
            pthread_mutex_lock(&m_mutex);
        }
        void unlock()
        {
            pthread_mutex_unlock(&m_mutex);
        }
        pthread_mutex_t* getMutex()
        {
            return &m_mutex;
        }
        ~Mutex()
        {
            pthread_mutex_destroy(&m_mutex);
        }
        private:
            pthread_mutex_t m_mutex;
    };
    class Condition
    {
        public:
            Condition(Mutex& mutex)
            :m_mutex(mutex)
            {
                m_mutex.lock();
            }
            ~Condition()
            {
                m_mutex.unlock();
            }
        private:
         Mutex &m_mutex;
    };
}

4.4 条件变量封装代码

#include 
#include "Mutex.hpp" //互斥量的封装
namespace chuxin
{
    using namespace MutexMudule; //使用互斥量模块
    class Cond
    {
    public:
        Cond()
        {
            pthread_cond_init(&_cond, NULL);
        }
        void Cond_signal_wait()
        {
            pthread_cond_signal(&_cond);
        }
        void Broadcast()
        {
            pthread_cond_broadcast(&_cond);
        }
        void Wait(Mutex &mutex)
        {
            pthread_cond_wait(&_cond, mutex.getMutex());
        }
        ~Cond()
        {
            pthread_cond_destroy(&_cond);
        }
    private:
        pthread_cond_t _cond;
    };
}

4.5 服务端进程池运行代码

        void Run()
        {
            _isRun=true;
            while (_isRun)
            {
                //获取连接,等待客户端连接
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                //如果没有客户端连接,则阻塞等待
                int sockfd = accept(_listensockfd, (struct sockaddr*)&client_addr, &client_addr_len);
                if (sockfd < 0)
                {
                    LOG(LogLevel::ERROR) << "accept socket failed";
                    continue;
                }
                inerAddr client(client_addr);
                LOG(LogLevel::INFO) << "accept socket success, client ip: " << client.getIP() << ", port: " << client.getPort();
               //创建线程池,并把任务放入任务队列中
              // using task_t=std::function;
               ThreadPool::GetInstance()->Enqueue([this, sockfd, &client]() {
                    this->Service(sockfd, client);
                });
            }
            _isRun=false;
        }

5. 基于TCP⽹络编程实现字典翻译的功能

5.1 引入字典文件,类

#include 
#include
#include
#include"Log.hpp"//引入日志模块
#include"inerAddr.hpp"
using namespace LogModuls;
const std::string defaultdict = "./dict.txt";//默认字典路径
const std::string sep = ": ";//字典文件中每行的分隔符
class Dict {
    public:
        Dict(const std::string &path = defaultdict) : _dict_path(path)
        {}
        bool LoadDict()//加载字典
        {
            std::ifstream in(_dict_path);//打开字典文件
            if(!in.is_open())
            {
                LOG(LogLevel::ERROR) << "Failed to open dict file: " << _dict_path;
                return false;
            }
            std::string line;
            while(std::getline(in,line))
            {
                auto pos = line.find(sep);
                if(pos == std::string::npos)
                {
                    LOG(LogLevel::WARN) << "解析 " << line<<" 失败";
                    continue;
                }
                std::string english = line.substr(0, pos);
                std::string chinese = line.substr(pos + sep.size());
                if(english.empty() || chinese.empty())
                {
                    LOG(LogLevel::WARN) << "没有有效内容" << line;
                    continue;
                }
                _dict_map.insert(std::make_pair(english, chinese));
                LOG(LogLevel::INFO) << "加载 " << english << " -> " << chinese;
            }
            in.close();
            return true;
        }
        std::string Translate(const std::string &english,inerAddr &client) //翻译英文
        {
            auto it = _dict_map.find(english);
            if(it == _dict_map.end())
            {
                LOG(LogLevel::DEBUG) <<"["<< client.getIP() << ":" << client.getPort() <<"]" << " 没有找到 " ;
                return "None";
            }
            //顺便记录一下客户端的IP和端口
            LOG(LogLevel::DEBUG) <<"["<< client.getIP() << ":" << client.getPort() <<"]" << " 请求翻译 " << english << " -> " << it->second;
            return it->second;
        }
        ~Dict()
        {}
    private:
        std::string _dict_path;//字典的路径
        std::unordered_map _dict_map;//字典的map,映射对应的翻译
};

5.2 代码实现

server.hpp

#include"Common.hpp"//错误码类
#include"Log.hpp"//日志类
#include"inerAddr.hpp"//地址转换类
#include
#include
using namespace LogModuls;
using func_t = std::function;
class TcpServer:public NoCopy//禁止对服务器拷贝
{
    public:
        TcpServer(uint16_t port,bool isRun=true)
        :_port(port) ,
        _isRun(isRun)
        {}
        void Init()
        {
            //创建套接字
            _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensockfd < 0)
            {
                LOG(LogLevel::ERROR) << "create socket failed";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::INFO) << "create socket success";
            //绑定地址
            inerAddr lock(_port);
            int n=bind(_listensockfd, (struct sockaddr*)&lock.NetAddr(), sizeof(lock.NetAddr()));
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "bind socket failed";
                exit(BIND_ERR);
            }
            LOG(LogLevel::INFO) << "bind socket success";
            //监听
            int n=listen(_listensockfd, 8);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen socket failed";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::INFO) << "listen socket success";
        }
        void Service(int sockfd, inerAddr &client)
        {
            //处理客户端请求
            char recvbuf[1024] = {0};
            while(true)
            {
                //接收数据
                ssize_t n=read(sockfd, recvbuf, sizeof(recvbuf)-1);
                if (n > 0)
                {
                    recvbuf[n] = '\0';
                    LOG(LogLevel::INFO) << "recv data from client: " << recvbuf;
                    //发送数据
                    std::string echo_string = _func(recvbuf, client);
                    write(sockfd, echo_string.c_str(), echo_string.size());
                }
                else if (n == 0)
                {
                    //客户端关闭连接
                    LOG(LogLevel::INFO) << "client close connection";
                    close(sockfd);
                    break;
                }
                else
                {
                    //出错
                    LOG(LogLevel::ERROR) << "recv data failed";
                    close(sockfd);
                    break;
                }
            }
        }
        class ThreadData//为了传参个任务函数
        {
            public:
                ThreadData(TcpServer *server, int sockfd, inerAddr& client)
                :server(server), sockfd(sockfd), client(client)
                {}
            public:
                TcpServer *server;//能够访问类外函数
                //下面是传参用的
                int sockfd;
                inerAddr client;
        };
        static void *Roetine(void*args)//任务函数
        {
            ThreadData *data=static_cast(args);
            data->server->Service(data->sockfd, data->client);
            delete data;
            return nullptr;
        }
        void Run()
        {
            _isRun=true;
            while (_isRun)
            {
                //获取连接,等待客户端连接
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                //如果没有客户端连接,则阻塞等待
                int sockfd = accept(_listensockfd, (struct sockaddr*)&client_addr, &client_addr_len);
                if (sockfd < 0)
                {
                    LOG(LogLevel::ERROR) << "accept socket failed";
                    continue;
                }
                inerAddr client(client_addr);
                LOG(LogLevel::INFO) << "accept socket success, client ip: " << client.getIP() << ", port: " << client.getPort();
                //创建线程
                ThreadData *data=new ThreadData(this, sockfd, client);
                pthread_t tid;
                pthread_create(&tid, nullptr, Roetine, data);
            }
            _isRun=false;
        }
        ~TcpServer()
        {}
    private:
        uint16_t _port;
        int _listensockfd;//监听套接字
        bool _isRun;
        func_t _func; // 设置回调处理
};

server.cc

#include "TcpServer.hpp"
#include"Common.hpp"
#include"Dict.hpp"
void Usage(char* name)
{
    std::cerr<<"Usage: "<server=std::make_unique(port,[&dict](const std::string& msg, inerAddr&addr){
        //处理消息
        dict.Translate(msg,addr);
    });
    server->Init();
    server->Run();
    return 0;
}

6. 基于TCP⽹络编程实现远程命令执⾏

6.1 命令类

#pragma once
#include 
#include 
#include 
#include 
#include "Command.hpp"
#include "inerAddr.hpp"
#include "Log.hpp"
using namespace LogModuls;
class Command
{
public:
    Command()
    {
        //预设命令
        //注意不要加入rm -rf这类命令,不然客户可能会误删本机文件
        _Commands.insert("ls");
        _Commands.insert("pwd");
        _Commands.insert("ls -l");
        _Commands.insert("touch haha.txt");
        _Commands.insert("who");
        _Commands.insert("whoami");
    }
    std::string Execute(const std::string& cmd, inerAddr& addr)
    {
        if(_Commands.find(cmd) == _Commands.end())
        {
            LOG(LogLevel::ERROR) << "Command not found: " << cmd;
            return "Command not found: " + cmd;
        }
        std::string who=addr.getIP()+":"+std::to_string(addr.getPort());
        //执行命令
        FILE* fp=popen(cmd.c_str(),"r");
        if(fp==NULL)
        {
            LOG(LogLevel::ERROR) << "Execute command failed: " << cmd;
            return "Execute command failed: " + cmd;
        }
        char buffer[1024];
        std::string res;
        while(fgets(buffer,sizeof(buffer),fp)!=NULL)
        {
            res+=buffer;
        }
        pclose(fp);
        std::string result = who + "execute done, result is: \n" + res;
        LOG(LogLevel::DEBUG) << result;
        return result;
    }
    ~Command()
    {}
private:
    std::set _Commands; // 管理命令
};

6.2 代码实现

此处服务端的代码的实现和字典类似,只有把外部的执行任务换为执行命令的任务就行了

#include "TcpServer.hpp"
#include"Common.hpp"
#include"Command.hpp"
void Usage(char* name)
{
    std::cerr<<"Usage: "< server = std::make_unique(port,
                                                                  std::bind(&Command::Execute, &cmd, std::placeholders::_1, std::placeholders::_2));
    // std::unique_ptr server = std::make_unique(port, [&cmd](const std::string &command, InetAddr &addr)
    //                                                               { return cmd.Execute(command, addr); });
    server->Init();
    server->Run();
    return 0;
}

posted on 2025-10-14 14:57  slgkaifa  阅读(11)  评论(0)    收藏  举报

导航