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)!