Socket 编程 TCP
- TCP socket API 详解
- V1 - Echo Server
- V2 - Echo Server 多进程版本
- V3 - Echo Server 多线程版本
- V4 - Echo Server 线程池版本
- 多线程远程命令执行
- v5 引入线程池版本翻译
TCP socket API 详解
socket():
- socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描述符;
- 应用程序可以像读写文件一样用 read/write 在网络上收发数据; 如果 socket()调用出错则返回-1;
- 对于 IPv4, family 参数指定为 AF_INET;
- 对于 TCP 协议,type 参数指定为 SOCK_STREAM, 表示面向流的传输协议
- protocol 参数的介绍从略,指定为 0 即可。
- bind:
- 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用 bind 绑定一个固定的网络地址和端口号;
- bind()成功返回 0,失败返回-1。
- bind()的作用是将参数 sockfd 和 myaddr 绑定在一起, 使 sockfd 这个用于网络通讯的文件描述符监听 myaddr 所描述的地址和端口号;
- 前面讲过,struct sockaddr *是一个通用指针类型,myaddr 参数实际上可以接受多种协议的 sockaddr 结构体,而它们的长度各不相同,所以需要第三个参数 addrlen
指定结构体的长度;
我们的程序中对myaddr参数是这样初始化的:
- 将整个结构体清零;
- 设置地址类型为 AF_INET;
- 网络地址为 INADDR_ANY, 这个宏表示本地的任意 IP 地址,因为服务器可能有
多个网卡,每个网卡也可能绑定多个 IP 地址, 这样设置可以在所有的 IP 地址上监听, 直到与某个客户端建立了连接时才确定下来到底用哪个 IP 地址; - 端口号为 SERV_PORT, 我们定义为 9999;
listen:
- listen()声明 sockfd 处于监听状态, 并且最多允许有 backlog 个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是 5);
- listen()成功返回 0,失败返回-1;
accept:
connect:
- 客户端需要调用 connect()连接服务器;
- connect 和 bind 的参数形式一致, 区别在于 bind 的参数是自己的地址, 而connect 的参数是对方的地址;
- connect()成功返回 0,出错返回-1;
V1 - Echo Server
nocopy.hpp
class nocopy
{public:nocopy(){}~nocopy(){}nocopy operator=(const nocopy& n)=delete;nocopy(const nocopy&n) =delete;
};
TcpServer.hpp
#pragma once
#include "common.hpp"
#include"InetAddr.hpp"
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve:public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// 去调用我们的服务Service(sockfd, addr);}_isruning = false;}void Service(int sockfd, InetAddr &peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);//写回我们的消息write(sockfd,echo_string.c_str(),echo_string.size());}else if (n == 0){//相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};
Comm.hpp
#pragma once
#include<iostream>
#include<string.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<memory>
#include<functional>
//#include"InetAddr.hpp"
#include"log.hpp"
#define CONV(addr) ((struct sockaddr*)&addr)
enum ExitCode
{OK=0,SOCKET_ERR,BIND_ERR,LISTEN_ERR,USAGE_ERR,CONNECT_ERR};
//用来防止拷贝的
class nocopy
{public:nocopy(){}~nocopy(){}nocopy operator=(const nocopy& n)=delete;nocopy(const nocopy&n) =delete;
};
tcpclient.cc
#include "common.hpp"
#include"InetAddr.hpp"
void Usage(std::string proc)
{std::cerr << "Usage: " << proc << " server_ip server_port" << std::endl;
}// ./tcpclient server_ip server_port
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(USAGE_ERR);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);//1. 创建socket int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;exit(SOCKET_ERR);}// 2. 直接向目标服务器发起建立连接的请求InetAddr serveraddr(serverip, serverport);int n = connect(sockfd, serveraddr.NetAddrPtr(), serveraddr.NetAddrLen());if (n < 0){std::cerr << "connect error" << std::endl;exit(CONNECT_ERR);}// 3. echo clientwhile (true){std::string line;std::cout << "Please Enter@ ";std::getline(std::cin, line);write(sockfd, line.c_str(), line.size());char buffer[1024];ssize_t size = read(sockfd, buffer, sizeof(buffer) - 1);if (size > 0){buffer[size] = 0;std::cout<< buffer << std::endl;}}close(sockfd);return 0;
}
V2 - Echo Server 多进程版本
修改的只有tcpserve.hpp的Run函数
#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();//v1// 去调用我们的服务// Service(sockfd, addr);//v2pid_t pid = fork();if (pid > 0){// 父进程close(sockfd);int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程(void)n;}else if (pid == 0){// 子进程close(_listensockfd);if(fork()>0) exit(OK);//孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收Service(sockfd,addr);exit(OK);}else{close(sockfd);LOG(LogLevel::FATAL) << "fork error";exit(FORK_ERR);}}_isruning = false;}void Service(int sockfd, InetAddr &peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};
V3 - Echo Server 多线程版本
void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1// 去调用我们的服务// Service(sockfd, addr);// v2// pid_t pid = fork();// if (pid > 0)// {// // 父进程// close(sockfd);// int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程// (void)n;// }// else if (pid == 0)// {// // 子进程// close(_listensockfd);// if(fork()>0) exit(OK);// //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收// Service(sockfd,addr);// exit(OK);// }// else// {// close(sockfd);// LOG(LogLevel::FATAL) << "fork error";// exit(FORK_ERR);// }// v3多线程版本pthread_t pid;ThreadData *ptr = new ThreadData(sockfd, addr, this);int n = pthread_create(&pid, nullptr, Rountinue, ptr);}_isruning = false;}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1// 去调用我们的服务// Service(sockfd, addr);// v2// pid_t pid = fork();// if (pid > 0)// {// // 父进程// close(sockfd);// int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程// (void)n;// }// else if (pid == 0)// {// // 子进程// close(_listensockfd);// if(fork()>0) exit(OK);// //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收// Service(sockfd,addr);// exit(OK);// }// else// {// close(sockfd);// LOG(LogLevel::FATAL) << "fork error";// exit(FORK_ERR);// }// v3多线程版本pthread_t pid;ThreadData *ptr = new ThreadData(sockfd, addr, this);int n = pthread_create(&pid, nullptr, Rountinue, ptr);}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}
V4 - Echo Server 线程池版本
#pragma once
#include "Mutex.hpp"
#include "cond.hpp"
#include "log.hpp"
#include "pthread.hpp"
#include <vector>
#include <queue>
namespace PthreadPoolModule
{using namespace MutexMoudle;using namespace CondModule;using namespace LogModule;using namespace PthreadModlue;template <typename T>class PthreadPool{private:void WakeUpAllThread(){LockGuard lockguard(_mutex);if (_sleepnum)_cond.Broadcast();LOG(LogLevel::INFO) << "唤醒所有的休眠线程";}void WakeUpOne(){_cond.Signal();LOG(LogLevel::INFO) << "唤醒一个休眠线程";}PthreadPool(int num = 17): _num(num),_isrunning(false),_sleepnum(0){for (int i = 0; i < _num; i++){_threads.emplace_back([this](){ HandlerTask(); });}}public:// 创建单例static PthreadPool<T> *GetInstance(){// 加锁防止多消费者if (_ins == nullptr){LockGuard lock(_insmutex);LOG(LogLevel::DEBUG) << "获取单例....";if (_ins == nullptr){LOG(LogLevel::DEBUG) << "首次使用单例, 创建之....";_ins = new PthreadPool<T>();_ins->Start();}}return _ins;}void Start(){if (_isrunning)return;_isrunning = true;LOG(LogLevel::INFO) << "线程池开始启动···";for (auto &ch : _threads){if (ch.Start()){LOG(LogLevel::INFO) << "线程 " << ch.Name() << " 启动成功";}else{LOG(LogLevel::ERROR) << "线程 " << ch.Name() << " 启动失败";}}}void Stop(){if (!_isrunning)return;_isrunning = false;// 进程停止// 可能有的在休眠// 有的在处理任务// 我们要把休眠的全部唤醒// 并且把任务队列的任务全部执行完// 才可以退出线程池// 唤醒所有的线层WakeUpAllThread();}void Join(){for (auto &ch : _threads){ch.Join();}}bool Enqueue(const T &in){if (_isrunning){LockGuard lock(_mutex);{_taskq.push(in);//if (_threads.size() == _sleepnum)if(_sleepnum>0)WakeUpOne();return true;}}return false;}// 该函数结束就会进行线程就运行完毕了void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));LOG(LogLevel::INFO) << name << " 处理任务...";// 消费者// 多个线程就是消费者while (true){T task;{LockGuard lock(_mutex);while (_taskq.empty() && _isrunning){_sleepnum++;_cond.Wait(_mutex);_sleepnum--;}if (!_isrunning && _taskq.empty())return;task = _taskq.front();_taskq.pop();}// 确保任务处理无阻塞,快速返回循环task(); // 任务执行在锁外}}private:std::vector<Pthread> _threads;int _num;Mutex _mutex;Cond _cond;std::queue<T> _taskq;bool _isrunning; // 用来标记线程退出int _sleepnum; // 睡眠的线程数static PthreadPool<T> *_ins; // 单例指针// 我们用的单例在开始时没有我们的锁因此我们这把锁要定义为静态的static Mutex _insmutex;};// 静态成员的初始化template <typename T>PthreadPool<T> *PthreadPool<T>::_ins = nullptr;template <typename T>MutexMoudle::Mutex PthreadPool<T>::_insmutex;
}
#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
#include"PthreadPool.hpp"
#include "log.hpp"
using task_t = std::function<void()>;
using namespace PthreadPoolModule;
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1// 去调用我们的服务// Service(sockfd, addr);// v2// pid_t pid = fork();// if (pid > 0)// {// // 父进程// close(sockfd);// int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程// (void)n;// }// else if (pid == 0)// {// // 子进程// close(_listensockfd);// if(fork()>0) exit(OK);// //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收// Service(sockfd,addr);// exit(OK);// }// else// {// close(sockfd);// LOG(LogLevel::FATAL) << "fork error";// exit(FORK_ERR);// }// v3多线程版本// pthread_t pid;// ThreadData *ptr = new ThreadData(sockfd, addr, this);// int n = pthread_create(&pid, nullptr, Rountinue, ptr);//v4多线程版本适合短服务PthreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,addr](){this->Service(sockfd,addr);});}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}void Service(int sockfd, InetAddr peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};
多线程远程命令执行
tcpserve.hpp
#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
//#include"PthreadPool.hpp"
#include "log.hpp"
using task_t = std::function<void()>;
//using namespace PthreadPoolModule;
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1// 去调用我们的服务// Service(sockfd, addr);// v2// pid_t pid = fork();// if (pid > 0)// {// // 父进程// close(sockfd);// int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程// (void)n;// }// else if (pid == 0)// {// // 子进程// close(_listensockfd);// if(fork()>0) exit(OK);// //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收// Service(sockfd,addr);// exit(OK);// }// else// {// close(sockfd);// LOG(LogLevel::FATAL) << "fork error";// exit(FORK_ERR);// }//v3多线程版本pthread_t pid;ThreadData *ptr = new ThreadData(sockfd, addr, this);int n = pthread_create(&pid, nullptr, Rountinue, ptr);//v4多线程版本适合短服务// PthreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,addr](){// this->Service(sockfd,addr);// });}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}void Service(int sockfd, InetAddr peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};
tcpserve.cc
#include"tcpserve.hpp"
#include"log.hpp"
#include"command.hpp"
using namespace LogModule;std::string defaultfunc(std::string mes,InetAddr peer)
{return "sreve say@:"+mes;
}
void Usage(std::string proc)
{std::cerr << "Usage: " << proc << " port" << std::endl;
}
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);ENABLE_CONSOLE_LOG_STRATEGY();Command comd;std::unique_ptr<tcpserve> tsvr = std::make_unique<tcpserve>(port,[&comd](std::string cmd,InetAddr peer)->std::string{return comd.Excute(cmd,peer);});tsvr->Init();tsvr->Run();return 0;
}
command.hpp
#pragma once#include <iostream>
#include <string>
#include <set>
#include <unistd.h>
#include "InetAddr.hpp"
class Command
{
public:Command(){_WhiteListCommands.insert("ls");_WhiteListCommands.insert("pwd");_WhiteListCommands.insert("ls -l");_WhiteListCommands.insert("touch haha.txt");_WhiteListCommands.insert("who");_WhiteListCommands.insert("whoami");}std::string Excute(std::string mes, InetAddr who){if (!_WhiteListCommands.count(mes))return "None";FILE *fp = popen(mes.c_str(), "r");if (nullptr == fp){return std::string("你要执行的命令不存在: ") + mes;}std::string res;char buffer[1024];while (fgets(buffer, sizeof(buffer), fp)){res += buffer;}pclose(fp);std::string result = who.StringAddr() + "execute done, result is: \n" + res;return res;}~Command(){}private:std::set<std::string> _WhiteListCommands;
};
v5 引入线程池版本翻译
PthreadPool.hpp
#pragma once
#include "Mutex.hpp"
#include "cond.hpp"
#include "log.hpp"
#include "pthread.hpp"
#include <vector>
#include <queue>
namespace PthreadPoolModule
{using namespace MutexMoudle;using namespace CondModule;using namespace LogModule;using namespace PthreadModlue;template <typename T>class PthreadPool{private:void WakeUpAllThread(){LockGuard lockguard(_mutex);if (_sleepnum)_cond.Broadcast();LOG(LogLevel::INFO) << "唤醒所有的休眠线程";}void WakeUpOne(){_cond.Signal();LOG(LogLevel::INFO) << "唤醒一个休眠线程";}PthreadPool(int num = 5): _num(num),_isrunning(false),_sleepnum(0){for (int i = 0; i < _num; i++){_threads.emplace_back([this](){ HandlerTask(); });}}public:// 创建单例static PthreadPool<T> *GetInstance(){// 加锁防止多消费者if (_ins == nullptr){LockGuard lock(_insmutex);LOG(LogLevel::DEBUG) << "获取单例....";if (_ins == nullptr){LOG(LogLevel::DEBUG) << "首次使用单例, 创建之....";_ins = new PthreadPool<T>();_ins->Start();}}return _ins;}void Start(){if (_isrunning)return;_isrunning = true;LOG(LogLevel::INFO) << "线程池开始启动···";for (auto &ch : _threads){if (ch.Start()){LOG(LogLevel::INFO) << "线程 " << ch.Name() << " 启动成功";}else{LOG(LogLevel::ERROR) << "线程 " << ch.Name() << " 启动失败";}}}void Stop(){if (!_isrunning)return;_isrunning = false;// 进程停止// 可能有的在休眠// 有的在处理任务// 我们要把休眠的全部唤醒// 并且把任务队列的任务全部执行完// 才可以退出线程池// 唤醒所有的线层WakeUpAllThread();}void Join(){for (auto &ch : _threads){ch.Join();}}bool Enqueue(const T &in){if (_isrunning){LockGuard lock(_mutex);{_taskq.push(in);if(_sleepnum>0)WakeUpOne();return true;}}return false;}// 该函数结束就会进行线程就运行完毕了void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));LOG(LogLevel::INFO) << name << " 处理任务...";// 消费者// 多个线程就是消费者while (true){T task;{LockGuard lock(_mutex);while (_taskq.empty() && _isrunning){_sleepnum++;_cond.Wait(_mutex);_sleepnum--;}if (!_isrunning && _taskq.empty())return;task = _taskq.front();_taskq.pop();}// 确保任务处理无阻塞,快速返回循环task(); // 任务执行在锁外}}private:std::vector<Pthread> _threads;int _num;Mutex _mutex;Cond _cond;std::queue<T> _taskq;bool _isrunning; // 用来标记线程退出int _sleepnum; // 睡眠的线程数static PthreadPool<T> *_ins; // 单例指针// 我们用的单例在开始时没有我们的锁因此我们这把锁要定义为静态的static Mutex _insmutex;};// 静态成员的初始化template <typename T>PthreadPool<T> *PthreadPool<T>::_ins = nullptr;template <typename T>MutexMoudle::Mutex PthreadPool<T>::_insmutex;
}
tcpserve.hpp
#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
#include"PthreadPool.hpp"
#include "log.hpp"
using task_t = std::function<void()>;
using namespace PthreadPoolModule;
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1// 去调用我们的服务// Service(sockfd, addr);// v2// pid_t pid = fork();// if (pid > 0)// {// // 父进程// close(sockfd);// int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程// (void)n;// }// else if (pid == 0)// {// // 子进程// close(_listensockfd);// if(fork()>0) exit(OK);// //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收// Service(sockfd,addr);// exit(OK);// }// else// {// close(sockfd);// LOG(LogLevel::FATAL) << "fork error";// exit(FORK_ERR);// }//v3多线程版本// pthread_t pid;// ThreadData *ptr = new ThreadData(sockfd, addr, this);// int n = pthread_create(&pid, nullptr, Rountinue, ptr);//v4多线程版本适合短服务PthreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,addr](){this->Service(sockfd,addr);});}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}void Service(int sockfd, InetAddr peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};
tbcserve.cc
#include"tcpserve.hpp"
#include"log.hpp"
#include"command.hpp"
#include"dict.hpp"
using namespace LogModule;std::string defaultfunc(std::string mes,InetAddr peer)
{return "sreve say@:"+mes;
}
void Usage(std::string proc)
{std::cerr << "Usage: " << proc << " port" << std::endl;
}
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);ENABLE_CONSOLE_LOG_STRATEGY();//Command comd;Dict dit;dit.LoadDict();std::unique_ptr<tcpserve> tsvr = std::make_unique<tcpserve>(port,[&dit](std::string word,InetAddr peer)->std::string{return dit.Translate(word,peer);});tsvr->Init();tsvr->Run();return 0;
}
dict.txt dict.hpp
#pragma oce
#include <map>
#include <unordered_map>
#include <string>
#include <fstream>
#include "log.hpp"
#include "InetAddr.hpp"
using namespace LogModule;
const std::string defaultdictpath = "./dict.txt";
class Dict
{
public:Dict(std::string path = defaultdictpath){_path = path;}bool LoadDict(){// 进行读取文件std::ifstream in(_path);if (!in.is_open()){LOG(LogLevel::DEBUG) << "打开字典: " << _path << " 错误";return false;}std::string sep(": ");std::string tem;while (getline(in, tem)){int pos = tem.find(sep);if (pos == std::string::npos){LOG(LogLevel::WARNING) << "解析: " << tem << " 失败";continue;}std::string english = tem.substr(0, pos);std::string chinese = tem.substr(pos + sep.size());if (english.empty() || chinese.empty()){LOG(LogLevel::WARNING) << "没有有效内容: " << tem;continue;}//std::cout<<english<<std::endl<<chinese<<std::endl;_dict.insert(make_pair(english, chinese));LOG(LogLevel::DEBUG) << "加载: " << tem;}return true;}std::string Translate(const std::string &word, InetAddr &client){//std::cout<<word<<std::endl;auto iter = _dict.find(word);if (iter == _dict.end()){LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.Ip() << " : " << client.Port() << "]# " << word << "->None";return "None";}LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.Ip() << " : " << client.Port() << "]# " << word << "->" << iter->second;return iter->second;}~Dict(){}private:std::unordered_map<std::string, std::string> _dict;std::string _path;
};apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天
red: 红色
blue: 蓝色
green: 绿色
yellow: 黄色
mother: 母亲
father: 父亲
brother: 兄弟
sister: 姐妹
eye: 眼睛
hand: 手
foot: 脚
head: 头
rice: 米饭
bread: 面包
water: 水
milk: 牛奶
tree: 树
mountain: 山
river: 河流
sun: 太阳
day: 天
year: 年
month: 月
hour: 小时
rain: 雨
snow: 雪
wind: 风
cloud: 云
school: 学校
house: 房子
door: 门
window: 窗户
purple: 紫色
pink: 粉色
orange: 橙色
gray: 灰色
doctor: 医生
engineer: 工程师
artist: 艺术家
cook: 厨师
tiger: 老虎
lion: 狮子
elephant: 大象
rabbit: 兔子
math: 数学
science: 科学
history: 历史
music: 音乐
morning: 早晨
night: 夜晚
spring: 春天
autumn: 秋天
sing: 唱歌
dance: 跳舞
read: 阅读
write: 写作
chair: 椅子
table: 桌子
bed: 床
lamp: 台灯
moon: 月亮
star: 星星
computer: 电脑
phone: 手机
angry: 生气的
tired: 疲倦的
hungry: 饥饿的
excited: 兴奋的
bicycle: 自行车
train: 火车
plane: 飞机
ship: 轮船
white: 白色
black: 黑色
gold: 金色
silver: 银色
shirt: 衬衫
pants: 裤子
shoes: 鞋子
hat: 帽子
basketball: 篮球
football: 足球
swim: 游泳
climb: 爬山
nose: 鼻子
ear: 耳朵
mouth: 嘴巴
tooth: 牙齿
coffee: 咖啡
tea: 茶
juice: 果汁
cake: 蛋糕
classroom: 教室
library: 图书馆
playground: 操场
pencil: 铅笔
one: 一
two: 二
three: 三
four: 四
sunny: 晴朗的
cloudy: 多云的
rainy: 下雨的
storm: 风暴
grandfather: 祖父
grandmother: 祖母
uncle: 叔叔
aunt: 阿姨
keyboard: 键盘
mouse: 鼠标
camera: 相机
screen: 屏幕
forest: 森林
ocean: 海洋
desert: 沙漠
grass: 草地
slow: 慢的
fast: 快的
hot: 热的
cold: 冷的
soft: 柔软的
hard: 坚硬的
light: 轻的
heavy: 重的
city: 城市
village: 村庄
street: 街道
park: 公园
Monday: 星期一
Friday: 星期五
January: 一月
December: 十二月
left: 左边
right: 右边
up: 向上
down: 向下
open: 打开
close: 关闭
buy: 购买
sell: 出售
begin: 开始
finish: 结束
question: 问题
answer: 答案
friend: 朋友
enemy: 敌人
peace: 和平
war: 战争
success: 成功
failure: 失败
grape: 葡萄
tomato: 西红柿
potato: 土豆
onion: 洋葱
carrot: 胡萝卜
noodle: 面条
egg: 鸡蛋
beer: 啤酒
salt: 盐
sugar: 糖
finger: 手指
leg: 腿
heart: 心脏
blood: 血液
hospital: 医院
medicine: 药
pain: 疼痛
healthy: 健康的
sick: 生病的
exercise: 锻炼
sleep: 睡觉
breathe: 呼吸
second: 秒
minute: 分钟
today: 今天
yesterday: 昨天
tomorrow: 明天
Tuesday: 星期二
Wednesday: 星期三
Thursday: 星期四
Saturday: 星期六
Sunday: 星期日
February: 二月
sky: 天空
fog: 雾
lake: 湖
earth: 地球
fire: 火
ice: 冰
plant: 植物
room: 房间
wall: 墙
floor: 地板
roof: 屋顶
kitchen: 厨房
bedroom: 卧室
bathroom: 浴室
sofa: 沙发
clock: 钟
mirror: 镜子
cup: 杯子
plate: 盘子
spoon: 勺子
coat: 外套
dress: 连衣裙
skirt: 裙子
jacket: 夹克
socks: 袜子
gloves: 手套
scarf: 围巾
bag: 包
watch: 手表
ring: 戒指
necklace: 项链
button: 纽扣
pocket: 口袋
belt: 腰带
umbrella: 雨伞
glasses: 眼镜
motorcycle: 摩托车
taxi: 出租车
road: 道路
bridge: 桥
station: 车站
airport: 机场
ticket: 票
passport: 护照
map: 地图
luggage: 行李
hotel: 酒店
tourist: 游客
direction: 方向
speed: 速度
paper: 纸
desk: 课桌
blackboard: 黑板
homework: 作业
exam: 考试
language: 语言
art: 艺术
sport: 体育
job: 工作
office: 办公室
meeting: 会议
colleague: 同事
boss: 老板
salary: 工资
email: 电子邮件
document: 文件
family: 家人
stranger: 陌生人
party: 聚会
gift: 礼物
smile: 微笑
calm: 平静的
bored: 无聊的
thirsty: 口渴的
hope: 希望
fear: 恐惧
courage: 勇气
pride: 骄傲
shame: 羞愧
patience: 耐心
stress: 压力
walk: 走
sit: 坐
stand: 站
eat: 吃
drink: 喝
speak: 说话
listen: 听
see: 看见
push: 推
pull: 拉
throw: 扔
catch: 抓住
time: 时间
life: 生命
death: 死亡
freedom: 自由
justice: 正义
truth: 真相
lie: 谎言
beauty: 美丽
ugliness: 丑陋
rich: 富有的
poor: 贫穷的
clean: 干净的
dirty: 肮脏的
easy: 容易的
difficult: 困难的
China: 中国
America: 美国
Japan: 日本
France: 法国
Germany: 德国
country: 国家
culture: 文化
tradition: 传统
festival: 节日
food: 食物
song: 歌曲
movie: 电影
game: 游戏
money: 钱
price: 价格