自学内容网 自学内容网

Socket编程TCP

【Linux】TCP编程

实验:通过TCP通信—在客户端输入要执行的指令,接收执行结果,另服务端接收指令并执行,向客户端发送执行结果

//主函数
#include<iostream>
#include<string>
#include"log.hpp"
#include"command_excute.hpp"
#include"tcp_serve.hpp"


void Usage(std::string name)
{
   std::cout<<"usage:\n\t"<<name <<" local_port\n"<<std::endl;
}
 int main(int argc,char* argv[])
 {
   if(argc !=2)
   {
      Usage(argv[0]);
      return 1;
   }
   EnableScrean();//开启屏幕日志
   uint16_t port =std::stoi(argv[1]);//端口号
   Command cmd("./safe.txt");//创建Command对象
   func_t excute_t = bind(&Command::excute,&cmd,std::placeholders::_1);//将excute函数绑定到cmd对象,并指定一个占位符
   std::unique_ptr<tcp_serve> t_ptr = std::make_unique<tcp_serve>(port,excute_t);//创建智能指针指向tcp_serve类,并构造类对象
   t_ptr->init_serve();
   t_ptr->loop();
   return 0;
 }

//客户端
#include <functional>
#include <iostream>
#include <string.h>
#include <memory>
#include <cstdint>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"

void Usage(std::string name)
{
   std::cout << "usage:\n\t" << name << " serveip serveport\n"
             << std::endl;
}

int main(int argc, char *argv[])
{
   if (argc != 3)
   {
      Usage(argv[0]);
      exit(1);
   }
   std::string server_ip = argv[1];
   uint16_t server_prot = std::stoi(argv[2]);
   // 创建套接字
   int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
   if (sockfd < 0)
   {
      std::cerr << "socket error" << std::endl;
      exit(2);
   }
   // 设置本地信息
   struct sockaddr_in server;
   memset(&server, 0, sizeof(server));
   server.sin_family = AF_INET;
   server.sin_port = htons(server_prot);
   server.sin_addr.s_addr = inet_addr(server_ip.c_str());
   // 连接套接字sockfd
   int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
   if (n < 0)
   {
      std::cerr << "connect error" << std::endl;
      exit(3);
   }
   while (true)
   {
      std::cout << "please enter#:" << std::endl;
      std::string outstring;
      std::getline(std::cin, outstring);
      ssize_t s = send(sockfd, outstring.c_str(), outstring.size(), 0); // 发送信息
      if (s > 0)
      {
         // 发送成功
         char buffer[1024];
         ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 接受信息
         if (n > 0)
         {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
         }
         else
         {
            break;
         }
      }
      else
      {
         break;
      }
   }
   close(sockfd);
   return 0;
}
//服务端
#pragma once
#include <functional>
#include <iostream>
#include <string.h>
#include <memory>
#include <cstdint>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
// 错误码
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    USAGE_ERROR
};

static const int gbacklog = 16;
static const int defaultsock = -1;
using func_t = std::function<std::string(const std::string &)>; // 回调函数处理server接收的信息

class tcp_serve;

class thread_data
{
public:
    thread_data(int fd, InetAddr clientaddr, tcp_serve *s)
        : _sockfd(fd), _clientaddr(clientaddr), _self(s)

    {
    }

public:
    InetAddr _clientaddr;
    int _sockfd;
    tcp_serve *_self;
};
class tcp_serve
{
public:
    tcp_serve(uint16_t port, func_t func)
        : _port(port), _func(func), _isruning(false), _listensock(defaultsock)
    {
    }
    void init_serve()
    {
        // 1. 创建流式套接字
        _listensock = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            LOG(FATAL, "socket error\n");
            exit(SOCKET_ERROR);
        }
        LOG(DEBUG, "socket create seccess ,sockfd is : %d\n", _listensock);

        // 2. bind绑定本地协议地址,套接字将用于通信的本地 IP 地址和端口号
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        int n = ::bind(_listensock, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind error\n");
            exit(BIND_ERROR);
        }
        LOG(DEBUG, "bind success, sockfd is :%d\n", _listensock);
        n = ::listen(_listensock, gbacklog); // 将一个未连接的套接字转换为一个被动套接字,即监听套接字,它可以接受其他套接字的连接请求
        if (n < 0)
        {
            LOG(FATAL, "listen error\n");
            exit(LISTEN_ERROR);
        }
        LOG(DEBUG, "listen success, sockfd is : %d\n", _listensock);
    }
    void service(int sockfd, InetAddr client)
    {
        LOG(DEBUG, "get new link,info : %s: %d:%d\n", client.Ip(), client.Port(), sockfd);
        std::string clientaddr = "[" + client.Ip() + " : " + std::to_string(client.Port()) + "]#";
        while (true) // 死循环一直接收信息,处理信息,发送信息
        {
            char buffer[1024];
            ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 接收来自套接字sockfd中的信息
            if (n > 0)
            {
                buffer[n] == 0;
                std::cout << clientaddr << buffer << std::endl;

                std::string ret = _func(buffer); // 通过回调函数处理接收的信息

                send(sockfd, ret.c_str(), ret.size(), 0); // 发送处理接受信息的结果给sockfd
            }
            else if (n == 0)
            {
                LOG(INFO, "%s quit\n", clientaddr.c_str());
                break;
            }
            else
            {
                LOG(ERROR, "read error \n", clientaddr.c_str());
                break;
            }
        }
        ::close(sockfd);
    }
    static void *handler_sock(void *args)
    {
        std::cout << "2" << std::endl;

        // pthread_detach(pthread_self());
        // thread_data *ptr = static_cast<thread_data *>(args);
        // pthread_detach(pthread_self());
        thread_data *td = static_cast<thread_data *>(args);
        td->_self->service(td->_sockfd, td->_clientaddr);

        delete td;
        return nullptr;
    }
    void loop()
    {
        _isruning = true;
        while (_isruning)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(_listensock, (struct sockaddr *)&peer, &len);
            // 用于从监听套接字接受一个连接请求
            // 阻塞等待直到有一个连接请求到达监听套接_listensock
            // 当连接请求到达时,accept会创建一个新的套接字处理这个连接,并返回新套接字的文件描述符。
            // 新套接字用于后续与客户端的通信
            if (sockfd < 0)
            {
                LOG(WARNING, "accept error\n");
                continue;
            }
            pthread_t t;
            std::cout << "1" << std::endl;
            thread_data *td = new thread_data(sockfd, InetAddr(peer), this); // 创建指针td指向thread_data对象
            pthread_create(&t, nullptr, handler_sock, td);                   // 创建一个线程执行handler_sock,并将td'作为参数传递
        }
        _isruning = false;
    }

private:
    uint16_t _port;
    int _listensock;
    bool _isruning;
    func_t _func;
};
//执行指令封装
#include <iostream>
#include <set>
#include <cstdio>
#include <fstream>
#include "log.hpp"
const static std::string sign = " ";
class Command
{
private:
    void load_safe_command(const std::string &set) // 将安全指令集从磁盘中装入内存中
    {
        std::ifstream in(set); // 打开我文件set
        if (!in.is_open())
        {
            LOG(FATAL, "open file error");
            return;
        }
        std::string line;
        while (std::getline(in, line))
        {
            LOG(FATAL, "load command [%s] success!\n", line.c_str());
            _safe_cmd.insert(line);
        }
        in.close(); // 关闭文件
    }

public:
    Command(const std::string cond_path)
        : _cond_path(cond_path)
    {
        load_safe_command(_cond_path);
    }
    std::string get_head(const std::string &cmd) // ls -a -l 获取指令头部
    {
        if (cmd.empty())
            return std::string();
        auto index = cmd.find(sign);
        if (index == std::string::npos)
            return cmd;
        else
            return cmd.substr(0, index);
    }
    bool check_safe(const std::string &cmd) // 检查指令是否安全
    {
        std::string head = get_head(cmd); // 获取指令头部,如:ls -l -a 指令中 ls
        if (head.empty())
            return false;
        auto iter = _safe_cmd.find(head); // 在安全指令集中查找指令头部
        if (iter != _safe_cmd.end())
        {
            return true;
        }
        return false;
    }
    std::string excute(const std::string &cmd) // 回调函数,cmd :server端接收的指令,
    {
        std::string result;
        if (check_safe(cmd)) // 检查指令是否安全,即是否在安全指令集中
        {
            FILE *fp = popen(cmd.c_str(), "r"); // popen()函数执行cmd指令,创建一个管道,通过这个管道可以读取命令的输出,
            // 执行完后,可以使用fread,fgets从fp指定的管道中读取数据,最后不用这个管道了,用pclose(fp)关闭
            if (fp == nullptr)
            {
                return "failed";
            }
            char buffer[1024];
            while (fgets(buffer, sizeof(buffer), fp) != NULL) // 读取执行结果,写入result中
            {
                result += buffer;
            }
            pclose(fp);
        }
        else
        {
            result = "坏人!\n";
        }
        return result;
    }

private:
    std::set<std::string> _safe_cmd; // 安全指令集
    std::string _cond_path;          // 安全指令存放的路径
};
//ip port 信息封装
#pragma once
#include <iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
class InetAddr
{
public:
    InetAddr(const sockaddr_in& addr)
    :_addr(addr)
    {
        get_address(&_ip,&_port);
    }
    std::string Ip()
    {
        return _ip;
    }
    uint16_t Port()
    {
        return _port;
    }
    bool operator==(const InetAddr& addr)
    {
        if(_ip == addr._ip&&_port == addr._port)
        {
            return true;
        }
        else
        return false;
    }
    struct sockaddr_in addr()
    {
        return _addr;
    }
    ~InetAddr()
    {}
private:
    void get_address(std::string* ip,uint16_t* port)
    {
        *ip = inet_ntoa(_addr.sin_addr);
        *port = ntohs(_addr.sin_port);
    }
    struct sockaddr_in _addr;
    std::string _ip;
    uint16_t _port;
};
//日志封装
#pragma once
#include <iostream>
#include <stdarg.h>
#include <fstream>
#include<sys/types.h>
#include<unistd.h>
#include "LockGuard.hpp"
const static char *logname = "log.txt";//日志文件
bool g_save = false; 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
enum level//日志的等级
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};
void save_file(const std::string &logname, std::string &massage)//保存日志到文件中
{
    std::ofstream infile("logname", std::ios::app);
    if (!infile.is_open())
    {
        return;
    }
    infile << massage << std::endl;
    infile.close();
}
// 获取日志等级
std::string get_level_string(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 "None";
    }
}
// 获取时间字符串
std::string get_time_string()
{
    time_t cur_time = time(nullptr);
    if (cur_time == (time_t)-1)
    {
        printf("Failed to get the current time.\n");
        return "None";
    }
    struct tm *formate_time = localtime(&cur_time);
    if (formate_time == nullptr)
    {
        return "None";
    }
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d",
             formate_time->tm_year + 1900,
             formate_time->tm_mon + 1,
             formate_time->tm_mday,
             formate_time->tm_hour,
             formate_time->tm_min,
             formate_time->tm_sec);
    return buffer;
}
// 日志信息
void  Log_inf(std::string filename, int line, bool is_save, int level, const char *format, ...)
{
    std::string levelstr = get_level_string(level);
    std::string time = get_time_string();
    pid_t selfid = getpid();
    char buffer[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg);
    std::string massage = "[" + time + "]" + "[" + levelstr + "]" + "[" + std::to_string(selfid) + "]" + "[" + filename + "]" + "[" + std::to_string(line) + "]" + buffer;
    LockGuard lockguard(mutex); // RAII
    if (is_save)
    {
        // 保存到文件中
        save_file(logname, massage);
    }
    else
    {   //向屏幕中打印
        std::cout << massage;
    }
}

// 定义宏
#define LOG(level, format, ...)                                            \
    do                                                                     \
    {                                                                      \
        Log_inf(__FILE__, __LINE__, g_save, level, format, ##__VA_ARGS__); \
    } while (0)
#define Enablefile()   \
    do                 \
    {                  \
        g_save = true; \
    } while (0)
#define EnableScrean()  \
    do                  \
    {                   \
        g_save = false; \
    } while (0)

//互斥锁
#pragma once 
#include<iostream>
#include<pthread.h>
class LockGuard //互斥量RAII
{
    public:
    LockGuard(pthread_mutex_t& mutex)
    :_mutex(mutex)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_mutex_lock(&_mutex);
    }
    ~LockGuard()
    {
        pthread_mutex_unlock(&_mutex);
        pthread_mutex_destroy(&_mutex);
    }
    private:
    pthread_mutex_t& _mutex;
};

(完)


原文地址:https://blog.csdn.net/m0_69131255/article/details/144274629

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!