目录
1 -> V2 -Echo Server多进程版本
2 -> V3 -Echo Server多线程版本
3 -> V3-1 -多线程远程命令执行
4 -> V4 -Echo Server线程池版本
1 -> V2 -Echo Server多进程版本
通过每个请求,创建子进程的方式来支持多连接。
InetAddr.hpp
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
class InetAddr
{
public:
InetAddr(struct sockaddr_in& addr) :_addr(addr)
{
_port = ntohs(_addr.sin_port);
_ip = inet_ntoa(_addr.sin_addr);
}
std::string Ip()
{
return _ip;
}
uint16_t Port()
{
return _port;
};
std::string PrintDebug()
{
std::string info = _ip;
info += ":";
info += std::to_string(_port); // "127.0.0.1:4444"
return info;
}
const struct sockaddr_in& GetAddr()
{
return _addr;
}
bool operator == (const InetAddr & addr)
{
//other code
return this->_ip == addr._ip && this->_port == addr._port;
}
~InetAddr() {}
private:
std::string _ip;
uint16_t _port;
struct sockaddr_in _addr;
};
TcpSever.hpp
#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
const static int default_backlog = 6; // TODO
class TcpServer : public nocopy
{
public:
TcpServer(uint16_t port) : _port(port), _isrunning(false)
{
}
// 都是固定套路
void Init()
{
// 1. 创建 socket, file fd, 本质是文件
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
lg.LogMessage(Fatal, "create socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Fatal);
}
int opt = 1;
setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
SO_REUSEPORT, &opt, sizeof(opt));
lg.LogMessage(Debug, "create socket success,
sockfd: % d\n", _listensock);
// 2. 填充本地网络信息并 bind
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
// 2.1 bind
if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
{
lg.LogMessage(Fatal, "bind socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Bind_Err);
}
lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
_listensock);
// 3. 设置 socket 为监听状态,tcp 特有的
if (listen(_listensock, default_backlog) != 0)
{
lg.LogMessage(Fatal, "listen socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Listen_Err);
}
lg.LogMessage(Debug, "listen socket success,
sockfd: % d\n", _listensock);
}
// Tcp 连接全双工通信的.
void Service(int sockfd)
{
char buffer[1024];
// 一直进行 IO
while (true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
std::cout << "client say# " << buffer <<
std::endl;
std::string echo_string = "server echo# ";
echo_string += buffer;
write(sockfd, echo_string.c_str(),
echo_string.size());
}
else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!)
{
lg.LogMessage(Info, "client quit...\n");
break;
}
else
{
lg.LogMessage(Error, "read socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
break;
}
}
}
void ProcessConnection(int sockfd, struct sockaddr_in& peer)
{
// v2 多进程
pid_t id = fork();
if (id < 0)
{
close(sockfd);
return;
}
else if (id == 0)
{
// child
close(_listensock);
if (fork() > 0)
exit(0);
InetAddr addr(peer); // 获取 client 地址信息
lg.LogMessage(Info, "process connection: %s:%d\n",
addr.Ip().c_str(), addr.Port());
// 孙子进程,孤儿进程,被系统领养,正常处理
Service(sockfd);
close(sockfd);
exit(0);
}
else
{
close(sockfd);
pid_t rid = waitpid(id, nullptr, 0);
if (rid == id)
{
// do nothing
}
}
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
// 4. 获取连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensock, CONV(&peer), &len);
if (sockfd < 0)
{
lg.LogMessage(Warning, "accept socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
continue;
}
lg.LogMessage(Debug, "accept success, get n new
sockfd: % d\n", sockfd);
ProcessConnection(sockfd, peer);
}
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensock; // TODO
bool _isrunning;
};
引入InetAddr.hpp,方便后面打印消息。
2 -> V3 -Echo Server多线程版本
Thread.hpp
#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
const static int default_backlog = 6; // TODO
class TcpServer : public nocopy
{
public:
TcpServer(uint16_t port) : _port(port), _isrunning(false)
{
}
// 都是固定套路
void Init()
{
// 1. 创建 socket, file fd, 本质是文件
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
lg.LogMessage(Fatal, "create socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Fatal);
}
int opt = 1;
setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
SO_REUSEPORT, &opt, sizeof(opt));
lg.LogMessage(Debug, "create socket success,
sockfd: % d\n", _listensock);
// 2. 填充本地网络信息并 bind
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
// 2.1 bind
if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
{
lg.LogMessage(Fatal, "bind socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Bind_Err);
}
lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
_listensock);
// 3. 设置 socket 为监听状态,tcp 特有的
if (listen(_listensock, default_backlog) != 0)
{
lg.LogMessage(Fatal, "listen socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Listen_Err);
}
lg.LogMessage(Debug, "listen socket success,
sockfd: % d\n", _listensock);
}
class ThreadData
{
public:
ThreadData(int sockfd, struct sockaddr_in addr)
: _sockfd(sockfd), _addr(addr)
{}
~ThreadData()
{}
public:
int _sockfd;
InetAddr _addr;
};
// Tcp 连接全双工通信的.
// 新增 static
static void Service(ThreadData& td)
{
char buffer[1024];
// 一直进行 IO
while (true)
{
ssize_t n = read(td._sockfd, buffer, sizeof(buffer) -
1);
if (n > 0)
{
buffer[n] = 0;
std::cout << "client say# " << buffer <<
std::endl;
std::string echo_string = "server echo# ";
echo_string += buffer;
write(td._sockfd, echo_string.c_str(),
echo_string.size());
}
else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!)
{
lg.LogMessage(Info, "client[%s:%d] quit...\n",
td._addr.Ip().c_str(), td._addr.Port());
break;
}
else
{
lg.LogMessage(Error, "read socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
break;
}
}
}
static void* threadExcute(void* args)
{
pthread_detach(pthread_self());
ThreadData* td = static_cast<ThreadData*>(args);
TcpServer::Service(*td);
close(td->_sockfd);
delete td;
return nullptr;
}
void ProcessConnection(int sockfd, struct sockaddr_in& peer)
{
// v3 多线程
InetAddr addr(peer);
pthread_t tid;
ThreadData* td = new ThreadData(sockfd, peer);
pthread_create(&tid, nullptr, threadExcute, (void*)td);
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
// 4. 获取连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensock, CONV(&peer), &len);
if (sockfd < 0)
{
lg.LogMessage(Warning, "accept socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
continue;
}
lg.LogMessage(Debug, "accept success, get n new
sockfd: % d\n", sockfd);
ProcessConnection(sockfd, peer);
}
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensock; // TODO
bool _isrunning;
};
使用最原始的接口,使用内部ThreadData类。
3 -> V3-1 -多线程远程命令执行
Command.hpp
- 命令类,用来执行命令,并获取结果。
- 这里暂停,做一个多线程的小业务。
#pragma once
#include <iostream>
#include <string>
#include <set>
#include <unistd.h>
class Command
{
private:
public:
Command() {}
Command(int sockfd) : _sockfd(sockfd)
{
// 限定一下命令的个数和范围
_safe_command.insert("ls");
_safe_command.insert("pwd");
_safe_command.insert("ls -l");
_safe_command.insert("ll");
_safe_command.insert("touch");
_safe_command.insert("who");
_safe_command.insert("whoami");
}
bool IsSafe(const std::string& command)
{
auto iter = _safe_command.find(command);
if (iter == _safe_command.end())
return false; // 要执行的命令不 set 中,不安全
else
return true;
}
std::string Execute(const std::string& command)
{
if (!IsSafe(command))
return "unsafe";
FILE* fp = popen(command.c_str(), "r");
if (fp == nullptr)
return std::string();
char buffer[1024];
std::string result;
while (fgets(buffer, sizeof(buffer), fp))
{
result += buffer;
}
pclose(fp);
return result;
}
std::string RecvCommand()
{
char line[1024];
ssize_t n = recv(_sockfd, line, sizeof(line) - 1, 0);
if (n > 0)
{
line[n] = 0;
return line;
}
else
{
return std::string();
}
}
void SendCommand(std::string result)
{
if (result.empty()) result = "done"; // 主要是有些命令没有结果,比如 touch
send(_sockfd, result.c_str(), result.size(), 0);
}
~Command()
{
}
private:
std::set<std::string> _safe_command;
int _sockfd;
std::string _command;
};
TcpSever.hpp
#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "Command.hpp" // 引入命令执行
const static int default_backlog = 6; // TODO
class TcpServer : public nocopy
{
public:
TcpServer(uint16_t port) : _port(port), _isrunning(false)
{
}
// 都是固定套路
void Init()
{
// 1. 创建 socket, file fd, 本质是文件
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
lg.LogMessage(Fatal, "create socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Fatal);
}
int opt = 1;
setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
SO_REUSEPORT, &opt, sizeof(opt));
lg.LogMessage(Debug, "create socket success,
sockfd: % d\n", _listensock);
// 2. 填充本地网络信息并 bind
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
// 2.1 bind
if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
{
lg.LogMessage(Fatal, "bind socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Bind_Err);
}
lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
_listensock);
// 3. 设置 socket 为监听状态,tcp 特有的
if (listen(_listensock, default_backlog) != 0)
{
lg.LogMessage(Fatal, "listen socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Listen_Err);
}
lg.LogMessage(Debug, "listen socket success,
sockfd: % d\n", _listensock);
}
class ThreadData
{
public:
ThreadData(int sockfd, struct sockaddr_in addr)
: _sockfd(sockfd), _addr(addr)
{
}
~ThreadData()
{
}
public:
int _sockfd;
InetAddr _addr;
};
// Tcp 连接全双工通信的.
// 新增 static
static void Service(ThreadData& td)
{
char buffer[1024];
// 一直进行 IO
while (true)
{
Command command(td._sockfd);
std::string commandstr = command.RecvCommand();
if (commandstr.empty())
return;
std::string result = command.Execute(commandstr);
command.SendCommand(result);
}
}
static void* threadExcute(void* args)
{
pthread_detach(pthread_self());
ThreadData* td = static_cast<ThreadData*>(args);
TcpServer::Service(*td);
close(td->_sockfd);
delete td;
return nullptr;
}
void ProcessConnection(int sockfd, struct sockaddr_in& peer)
{
// v3 多线程
InetAddr addr(peer);
pthread_t tid;
ThreadData* td = new ThreadData(sockfd, peer);
pthread_create(&tid, nullptr, threadExcute, (void*)td);
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
// 4. 获取连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensock, CONV(&peer), &len);
if (sockfd < 0)
{
lg.LogMessage(Warning, "accept socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
continue;
}
lg.LogMessage(Debug, "accept success, get n new
sockfd: % d\n", sockfd);
ProcessConnection(sockfd, peer);
}
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensock; // TODO
bool _isrunning;
};
4 -> V4 -Echo Server线程池版本
引入系统部分的线程池,进行简单的业务处理。
TcpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
const static int default_backlog = 6; // TODO
class TcpServer : public nocopy
{
public:
TcpServer(uint16_t port) : _port(port), _isrunning(false)
{
}
// 都是固定套路
void Init()
{
// 1. 创建 socket, file fd, 本质是文件
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
lg.LogMessage(Fatal, "create socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Fatal);
}
int opt = 1;
setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |
SO_REUSEPORT, &opt, sizeof(opt));
lg.LogMessage(Debug, "create socket success,
sockfd: % d\n", _listensock);
// 2. 填充本地网络信息并 bind
// struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
// 2.1 bind
if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
{
lg.LogMessage(Fatal, "bind socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Bind_Err);
}
lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",
_listensock);
// 3. 设置 socket 为监听状态,tcp 特有的
if (listen(_listensock, default_backlog) != 0)
{
lg.LogMessage(Fatal, "listen socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
exit(Listen_Err);
}
lg.LogMessage(Debug, "listen socket success,
sockfd: % d\n", _listensock);
}
// Tcp 连接全双工通信的.
void Service(int sockfd, InetAddr addr)
{
char buffer[1024];
// 一直进行 IO
while (true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
std::cout << "client say# " << buffer <<
std::endl;
std::string echo_string = "server echo# ";
echo_string += buffer;
write(sockfd, echo_string.c_str(),
echo_string.size());
}
else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!)
{
lg.LogMessage(Info, "client[%s:%d] quit...\n",
addr.Ip().c_str(), addr.Port());
break;
}
else
{
lg.LogMessage(Error, "read socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
break;
}
}
}
void ProcessConnection(int sockfd, struct sockaddr_in& peer)
{
using func_t = std::function<void()>;
InetAddr addr(peer);
func_t func = std::bind(&TcpServer::Service, this, sockfd,
addr); // 这里不能 auto
ThreadPool<func_t>::GetInstance()->Push(func);
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
// 4. 获取连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensock, CONV(&peer), &len);
if (sockfd < 0)
{
lg.LogMessage(Warning, "accept socket error, errno
code: % d, error string : % s\n", errno, strerror(errno));
continue;
}
lg.LogMessage(Debug, "accept success, get n new
sockfd: % d\n", sockfd);
ProcessConnection(sockfd, peer);
}
_isrunning = false;
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensock; // TODO
bool _isrunning;
};
感谢各位大佬支持!!!
互三啦!!!