Socket编程TCP

诗林  金牌会员 | 2024-12-11 01:18:17 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 581|帖子 581|积分 1743

【Linux】TCP编程
实验:通过TCP通讯—在客户端输入要实验的指令,接收实验效果,另服务端接收指令并实验,向客户端发送实验效果
  1. //主函数
  2. #include<iostream>
  3. #include<string>
  4. #include"log.hpp"
  5. #include"command_excute.hpp"
  6. #include"tcp_serve.hpp"
  7. void Usage(std::string name)
  8. {
  9.    std::cout<<"usage:\n\t"<<name <<" local_port\n"<<std::endl;
  10. }
  11. int main(int argc,char* argv[])
  12. {
  13.    if(argc !=2)
  14.    {
  15.       Usage(argv[0]);
  16.       return 1;
  17.    }
  18.    EnableScrean();//开启屏幕日志
  19.    uint16_t port =std::stoi(argv[1]);//端口号
  20.    Command cmd("./safe.txt");//创建Command对象
  21.    func_t excute_t = bind(&Command::excute,&cmd,std::placeholders::_1);//将excute函数绑定到cmd对象,并指定一个占位符
  22.    std::unique_ptr<tcp_serve> t_ptr = std::make_unique<tcp_serve>(port,excute_t);//创建智能指针指向tcp_serve类,并构造类对象
  23.    t_ptr->init_serve();
  24.    t_ptr->loop();
  25.    return 0;
  26. }
复制代码
  1. //客户端
  2. #include <functional>
  3. #include <iostream>
  4. #include <string.h>
  5. #include <memory>
  6. #include <cstdint>
  7. #include <string>
  8. #include <unistd.h>
  9. #include <sys/types.h>
  10. #include <sys/socket.h>
  11. #include <netinet/in.h>
  12. #include <arpa/inet.h>
  13. #include "InetAddr.hpp"
  14. void Usage(std::string name)
  15. {
  16.    std::cout << "usage:\n\t" << name << " serveip serveport\n"
  17.              << std::endl;
  18. }
  19. int main(int argc, char *argv[])
  20. {
  21.    if (argc != 3)
  22.    {
  23.       Usage(argv[0]);
  24.       exit(1);
  25.    }
  26.    std::string server_ip = argv[1];
  27.    uint16_t server_prot = std::stoi(argv[2]);
  28.    // 创建套接字
  29.    int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
  30.    if (sockfd < 0)
  31.    {
  32.       std::cerr << "socket error" << std::endl;
  33.       exit(2);
  34.    }
  35.    // 设置本地信息
  36.    struct sockaddr_in server;
  37.    memset(&server, 0, sizeof(server));
  38.    server.sin_family = AF_INET;
  39.    server.sin_port = htons(server_prot);
  40.    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
  41.    // 连接套接字sockfd
  42.    int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
  43.    if (n < 0)
  44.    {
  45.       std::cerr << "connect error" << std::endl;
  46.       exit(3);
  47.    }
  48.    while (true)
  49.    {
  50.       std::cout << "please enter#:" << std::endl;
  51.       std::string outstring;
  52.       std::getline(std::cin, outstring);
  53.       ssize_t s = send(sockfd, outstring.c_str(), outstring.size(), 0); // 发送信息
  54.       if (s > 0)
  55.       {
  56.          // 发送成功
  57.          char buffer[1024];
  58.          ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 接受信息
  59.          if (n > 0)
  60.          {
  61.             buffer[n] = 0;
  62.             std::cout << buffer << std::endl;
  63.          }
  64.          else
  65.          {
  66.             break;
  67.          }
  68.       }
  69.       else
  70.       {
  71.          break;
  72.       }
  73.    }
  74.    close(sockfd);
  75.    return 0;
  76. }
复制代码
  1. //服务端
  2. #pragma once
  3. #include <functional>
  4. #include <iostream>
  5. #include <string.h>
  6. #include <memory>
  7. #include <cstdint>
  8. #include <string>
  9. #include <sys/types.h>
  10. #include <sys/socket.h>
  11. #include <netinet/in.h>
  12. #include <arpa/inet.h>
  13. #include "InetAddr.hpp"
  14. // 错误码
  15. enum
  16. {
  17.     SOCKET_ERROR = 1,
  18.     BIND_ERROR,
  19.     LISTEN_ERROR,
  20.     USAGE_ERROR
  21. };
  22. static const int gbacklog = 16;
  23. static const int defaultsock = -1;
  24. using func_t = std::function<std::string(const std::string &)>; // 回调函数处理server接收的信息
  25. class tcp_serve;
  26. class thread_data
  27. {
  28. public:
  29.     thread_data(int fd, InetAddr clientaddr, tcp_serve *s)
  30.         : _sockfd(fd), _clientaddr(clientaddr), _self(s)
  31.     {
  32.     }
  33. public:
  34.     InetAddr _clientaddr;
  35.     int _sockfd;
  36.     tcp_serve *_self;
  37. };
  38. class tcp_serve
  39. {
  40. public:
  41.     tcp_serve(uint16_t port, func_t func)
  42.         : _port(port), _func(func), _isruning(false), _listensock(defaultsock)
  43.     {
  44.     }
  45.     void init_serve()
  46.     {
  47.         // 1. 创建流式套接字
  48.         _listensock = ::socket(AF_INET, SOCK_STREAM, 0);
  49.         if (_listensock < 0)
  50.         {
  51.             LOG(FATAL, "socket error\n");
  52.             exit(SOCKET_ERROR);
  53.         }
  54.         LOG(DEBUG, "socket create seccess ,sockfd is : %d\n", _listensock);
  55.         // 2. bind绑定本地协议地址,套接字将用于通信的本地 IP 地址和端口号
  56.         struct sockaddr_in local;
  57.         memset(&local, 0, sizeof(local));
  58.         local.sin_family = AF_INET;
  59.         local.sin_port = htons(_port);
  60.         local.sin_addr.s_addr = INADDR_ANY;
  61.         int n = ::bind(_listensock, (struct sockaddr *)&local, sizeof(local));
  62.         if (n < 0)
  63.         {
  64.             LOG(FATAL, "bind error\n");
  65.             exit(BIND_ERROR);
  66.         }
  67.         LOG(DEBUG, "bind success, sockfd is :%d\n", _listensock);
  68.         n = ::listen(_listensock, gbacklog); // 将一个未连接的套接字转换为一个被动套接字,即监听套接字,它可以接受其他套接字的连接请求
  69.         if (n < 0)
  70.         {
  71.             LOG(FATAL, "listen error\n");
  72.             exit(LISTEN_ERROR);
  73.         }
  74.         LOG(DEBUG, "listen success, sockfd is : %d\n", _listensock);
  75.     }
  76.     void service(int sockfd, InetAddr client)
  77.     {
  78.         LOG(DEBUG, "get new link,info : %s: %d:%d\n", client.Ip(), client.Port(), sockfd);
  79.         std::string clientaddr = "[" + client.Ip() + " : " + std::to_string(client.Port()) + "]#";
  80.         while (true) // 死循环一直接收信息,处理信息,发送信息
  81.         {
  82.             char buffer[1024];
  83.             ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 接收来自套接字sockfd中的信息
  84.             if (n > 0)
  85.             {
  86.                 buffer[n] == 0;
  87.                 std::cout << clientaddr << buffer << std::endl;
  88.                 std::string ret = _func(buffer); // 通过回调函数处理接收的信息
  89.                 send(sockfd, ret.c_str(), ret.size(), 0); // 发送处理接受信息的结果给sockfd
  90.             }
  91.             else if (n == 0)
  92.             {
  93.                 LOG(INFO, "%s quit\n", clientaddr.c_str());
  94.                 break;
  95.             }
  96.             else
  97.             {
  98.                 LOG(ERROR, "read error \n", clientaddr.c_str());
  99.                 break;
  100.             }
  101.         }
  102.         ::close(sockfd);
  103.     }
  104.     static void *handler_sock(void *args)
  105.     {
  106.         std::cout << "2" << std::endl;
  107.         // pthread_detach(pthread_self());
  108.         // thread_data *ptr = static_cast<thread_data *>(args);
  109.         // pthread_detach(pthread_self());
  110.         thread_data *td = static_cast<thread_data *>(args);
  111.         td->_self->service(td->_sockfd, td->_clientaddr);
  112.         delete td;
  113.         return nullptr;
  114.     }
  115.     void loop()
  116.     {
  117.         _isruning = true;
  118.         while (_isruning)
  119.         {
  120.             struct sockaddr_in peer;
  121.             socklen_t len = sizeof(peer);
  122.             int sockfd = ::accept(_listensock, (struct sockaddr *)&peer, &len);
  123.             // 用于从监听套接字接受一个连接请求
  124.             // 阻塞等待直到有一个连接请求到达监听套接_listensock
  125.             // 当连接请求到达时,accept会创建一个新的套接字处理这个连接,并返回新套接字的文件描述符。
  126.             // 新套接字用于后续与客户端的通信
  127.             if (sockfd < 0)
  128.             {
  129.                 LOG(WARNING, "accept error\n");
  130.                 continue;
  131.             }
  132.             pthread_t t;
  133.             std::cout << "1" << std::endl;
  134.             thread_data *td = new thread_data(sockfd, InetAddr(peer), this); // 创建指针td指向thread_data对象
  135.             pthread_create(&t, nullptr, handler_sock, td);                   // 创建一个线程执行handler_sock,并将td'作为参数传递
  136.         }
  137.         _isruning = false;
  138.     }
  139. private:
  140.     uint16_t _port;
  141.     int _listensock;
  142.     bool _isruning;
  143.     func_t _func;
  144. };
复制代码
  1. //执行指令封装
  2. #include <iostream>
  3. #include <set>
  4. #include <cstdio>
  5. #include <fstream>
  6. #include "log.hpp"
  7. const static std::string sign = " ";
  8. class Command
  9. {
  10. private:
  11.     void load_safe_command(const std::string &set) // 将安全指令集从磁盘中装入内存中
  12.     {
  13.         std::ifstream in(set); // 打开我文件set
  14.         if (!in.is_open())
  15.         {
  16.             LOG(FATAL, "open file error");
  17.             return;
  18.         }
  19.         std::string line;
  20.         while (std::getline(in, line))
  21.         {
  22.             LOG(FATAL, "load command [%s] success!\n", line.c_str());
  23.             _safe_cmd.insert(line);
  24.         }
  25.         in.close(); // 关闭文件
  26.     }
  27. public:
  28.     Command(const std::string cond_path)
  29.         : _cond_path(cond_path)
  30.     {
  31.         load_safe_command(_cond_path);
  32.     }
  33.     std::string get_head(const std::string &cmd) // ls -a -l 获取指令头部
  34.     {
  35.         if (cmd.empty())
  36.             return std::string();
  37.         auto index = cmd.find(sign);
  38.         if (index == std::string::npos)
  39.             return cmd;
  40.         else
  41.             return cmd.substr(0, index);
  42.     }
  43.     bool check_safe(const std::string &cmd) // 检查指令是否安全
  44.     {
  45.         std::string head = get_head(cmd); // 获取指令头部,如:ls -l -a 指令中 ls
  46.         if (head.empty())
  47.             return false;
  48.         auto iter = _safe_cmd.find(head); // 在安全指令集中查找指令头部
  49.         if (iter != _safe_cmd.end())
  50.         {
  51.             return true;
  52.         }
  53.         return false;
  54.     }
  55.     std::string excute(const std::string &cmd) // 回调函数,cmd :server端接收的指令,
  56.     {
  57.         std::string result;
  58.         if (check_safe(cmd)) // 检查指令是否安全,即是否在安全指令集中
  59.         {
  60.             FILE *fp = popen(cmd.c_str(), "r"); // popen()函数执行cmd指令,创建一个管道,通过这个管道可以读取命令的输出,
  61.             // 执行完后,可以使用fread,fgets从fp指定的管道中读取数据,最后不用这个管道了,用pclose(fp)关闭
  62.             if (fp == nullptr)
  63.             {
  64.                 return "failed";
  65.             }
  66.             char buffer[1024];
  67.             while (fgets(buffer, sizeof(buffer), fp) != NULL) // 读取执行结果,写入result中
  68.             {
  69.                 result += buffer;
  70.             }
  71.             pclose(fp);
  72.         }
  73.         else
  74.         {
  75.             result = "坏人!\n";
  76.         }
  77.         return result;
  78.     }
  79. private:
  80.     std::set<std::string> _safe_cmd; // 安全指令集
  81.     std::string _cond_path;          // 安全指令存放的路径
  82. };
复制代码
  1. //ip port 信息封装
  2. #pragma once
  3. #include <iostream>
  4. #include<sys/types.h>
  5. #include<sys/socket.h>
  6. #include<arpa/inet.h>
  7. #include<netinet/in.h>
  8. class InetAddr
  9. {
  10. public:
  11.     InetAddr(const sockaddr_in& addr)
  12.     :_addr(addr)
  13.     {
  14.         get_address(&_ip,&_port);
  15.     }
  16.     std::string Ip()
  17.     {
  18.         return _ip;
  19.     }
  20.     uint16_t Port()
  21.     {
  22.         return _port;
  23.     }
  24.     bool operator==(const InetAddr& addr)
  25.     {
  26.         if(_ip == addr._ip&&_port == addr._port)
  27.         {
  28.             return true;
  29.         }
  30.         else
  31.         return false;
  32.     }
  33.     struct sockaddr_in addr()
  34.     {
  35.         return _addr;
  36.     }
  37.     ~InetAddr()
  38.     {}
  39. private:
  40.     void get_address(std::string* ip,uint16_t* port)
  41.     {
  42.         *ip = inet_ntoa(_addr.sin_addr);
  43.         *port = ntohs(_addr.sin_port);
  44.     }
  45.     struct sockaddr_in _addr;
  46.     std::string _ip;
  47.     uint16_t _port;
  48. };
复制代码
  1. //日志封装
  2. #pragma once
  3. #include <iostream>
  4. #include <stdarg.h>
  5. #include <fstream>
  6. #include<sys/types.h>
  7. #include<unistd.h>
  8. #include "LockGuard.hpp"
  9. const static char *logname = "log.txt";//日志文件
  10. bool g_save = false;
  11. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  12. enum level//日志的等级
  13. {
  14.     DEBUG = 0,
  15.     INFO,
  16.     WARNING,
  17.     ERROR,
  18.     FATAL
  19. };
  20. void save_file(const std::string &logname, std::string &massage)//保存日志到文件中
  21. {
  22.     std::ofstream infile("logname", std::ios::app);
  23.     if (!infile.is_open())
  24.     {
  25.         return;
  26.     }
  27.     infile << massage << std::endl;
  28.     infile.close();
  29. }
  30. // 获取日志等级
  31. std::string get_level_string(int level)
  32. {
  33.     switch (level)
  34.     {
  35.     case DEBUG:
  36.         return "DEBUG";
  37.     case INFO:
  38.         return "INFO";
  39.     case WARNING:
  40.         return "WARNING";
  41.     case ERROR:
  42.         return "ERROR";
  43.     case FATAL:
  44.         return "FATAL";
  45.     default:
  46.         return "None";
  47.     }
  48. }
  49. // 获取时间字符串
  50. std::string get_time_string()
  51. {
  52.     time_t cur_time = time(nullptr);
  53.     if (cur_time == (time_t)-1)
  54.     {
  55.         printf("Failed to get the current time.\n");
  56.         return "None";
  57.     }
  58.     struct tm *formate_time = localtime(&cur_time);
  59.     if (formate_time == nullptr)
  60.     {
  61.         return "None";
  62.     }
  63.     char buffer[1024];
  64.     snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d",
  65.              formate_time->tm_year + 1900,
  66.              formate_time->tm_mon + 1,
  67.              formate_time->tm_mday,
  68.              formate_time->tm_hour,
  69.              formate_time->tm_min,
  70.              formate_time->tm_sec);
  71.     return buffer;
  72. }
  73. // 日志信息
  74. void  Log_inf(std::string filename, int line, bool is_save, int level, const char *format, ...)
  75. {
  76.     std::string levelstr = get_level_string(level);
  77.     std::string time = get_time_string();
  78.     pid_t selfid = getpid();
  79.     char buffer[1024];
  80.     va_list arg;
  81.     va_start(arg, format);
  82.     vsnprintf(buffer, sizeof(buffer), format, arg);
  83.     va_end(arg);
  84.     std::string massage = "[" + time + "]" + "[" + levelstr + "]" + "[" + std::to_string(selfid) + "]" + "[" + filename + "]" + "[" + std::to_string(line) + "]" + buffer;
  85.     LockGuard lockguard(mutex); // RAII
  86.     if (is_save)
  87.     {
  88.         // 保存到文件中
  89.         save_file(logname, massage);
  90.     }
  91.     else
  92.     {   //向屏幕中打印
  93.         std::cout << massage;
  94.     }
  95. }
  96. // 定义宏
  97. #define LOG(level, format, ...)                                            \
  98.     do                                                                     \
  99.     {                                                                      \
  100.         Log_inf(__FILE__, __LINE__, g_save, level, format, ##__VA_ARGS__); \
  101.     } while (0)
  102. #define Enablefile()   \
  103.     do                 \
  104.     {                  \
  105.         g_save = true; \
  106.     } while (0)
  107. #define EnableScrean()  \
  108.     do                  \
  109.     {                   \
  110.         g_save = false; \
  111.     } while (0)
复制代码
  1. //互斥锁
  2. #pragma once
  3. #include<iostream>
  4. #include<pthread.h>
  5. class LockGuard //互斥量RAII
  6. {
  7.     public:
  8.     LockGuard(pthread_mutex_t& mutex)
  9.     :_mutex(mutex)
  10.     {
  11.         pthread_mutex_init(&_mutex,nullptr);
  12.         pthread_mutex_lock(&_mutex);
  13.     }
  14.     ~LockGuard()
  15.     {
  16.         pthread_mutex_unlock(&_mutex);
  17.         pthread_mutex_destroy(&_mutex);
  18.     }
  19.     private:
  20.     pthread_mutex_t& _mutex;
  21. };
复制代码
(完)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

诗林

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表