【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;
- };
复制代码 (完)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |