目录
- 一、Comm.hpp(公共数据)
- 二、Log.hpp(日志)
- 三、InetAddr.hpp(管理sockaddr_in相关信息)
- 四、NoCopy.hpp(防拷贝)
- 五、Lockguard.hpp(自动管理锁)
- 六、Thread.hpp(封装线程)
- 七、ThreadPool.hpp(线程池)
- 八、dict.txt(配置文件、简单字典)
- 九、Translate.hpp(提供翻译服务)
- 十、Daemon.hpp(使进程变为守护进程)
- 十一、TcpServer.hpp(V1~V4版服务端封装)
- 十二、TcpServer.hpp(最终版,V5版服务端封装)
- 十三、Main.cpp(服务端)
- 十四、TcpClient.cpp(客户端)
- 结尾
一、Comm.hpp(公共数据)
#pragma once
enum {
Socket_err = 1,
Bind_err,
Accept_err,
Recvfrom_err
};
二、Log.hpp(日志)
#pragma once
#include <pthread.h>
#include <iostream>
#include <string>
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
using namespace std;
// 日志等级
enum
{
Debug = 0, // 调试
Info, // 正常
Warning, // 警告
Error, // 错误,但程序并未直接退出
Fatal // 程序直接挂掉
};
enum
{
Screen = 10, // 打印到显示器上
OneFile, // 打印到一个文件中
ClassFile // 按照日志等级打印到不同的文件中
};
string LevelToString(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 "Unknow";
}
}
const char* default_filename = "log.";
const int default_style = Screen;
const char* defaultdir = "log";
class Log
{
public:
Log()
: style(default_style), filename(default_filename)
{
// mkdir(defaultdir,0775);
pthread_mutex_init(&_log_mutex, nullptr);
}
void SwitchStyle(int sty)
{
style = sty;
}
void WriteLogToOneFile(const string& logname, const string& logmessage)
{
int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
if (fd == -1)
return;
pthread_mutex_lock(&_log_mutex);
write(fd, logmessage.c_str(), logmessage.size());
pthread_mutex_unlock(&_log_mutex);
close(fd);
}
void WriteLogToClassFile(const string& levelstr, const string& logmessage)
{
mkdir(defaultdir, 0775);
string name = defaultdir;
name += "/";
name += filename;
name += levelstr;
WriteLogToOneFile(name, logmessage);
}
void WriteLog(int level, const string& logmessage)
{
switch (style)
{
case Screen:
{
pthread_mutex_lock(&_log_mutex);
cout << logmessage;
pthread_mutex_unlock(&_log_mutex);
}
break;
case OneFile:
WriteLogToClassFile("All", logmessage);
break;
case ClassFile:
WriteLogToClassFile(LevelToString(level), logmessage);
break;
default:
break;
}
}
string GetTime()
{
time_t CurrentTime = time(nullptr);
struct tm* curtime = localtime(&CurrentTime);
char time[128];
// localtime 的年是从1900开始的,所以要加1900, 月是从0开始的所以加1
snprintf(time, sizeof(time), "%d-%d-%d %d:%d:%d",
curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday,
curtime->tm_hour, curtime->tm_min, curtime->tm_sec);
return time;
return "";
}
void LogMessage(int level, const char* format, ...)
{
char left[1024];
string Levelstr = LevelToString(level).c_str();
string Timestr = GetTime().c_str();
string Idstr = to_string(getpid());
snprintf(left, sizeof(left), "[%s][%s][%s] ",
Levelstr.c_str(), Timestr.c_str(), Idstr.c_str());
va_list args;
va_start(args, format);
char right[1024];
vsnprintf(right, sizeof(right), format, args);
string logmessage = left;
logmessage += right;
WriteLog(level, logmessage);
va_end(args);
}
~Log()
{
pthread_mutex_destroy(&_log_mutex);
};
private:
int style;
string filename;
pthread_mutex_t _log_mutex;
};
Log lg;
class Conf
{
public:
Conf()
{
lg.SwitchStyle(Screen);
}
~Conf()
{
}
};
Conf conf;
三、InetAddr.hpp(管理sockaddr_in相关信息)
#pragma once
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string>
class InetAddr
{
public:
InetAddr(struct sockaddr_in sock)
:_sock(sock)
{}
std::string Ip()
{
return inet_ntoa(_sock.sin_addr);
}
uint16_t Port()
{
return ntohs(_sock.sin_port);
}
std::string PrintDebug()
{
std::string info = "[";
info += Ip();
info += ":";
info += std::to_string(Port());
info += "]";
info += "# ";
return info;
}
~InetAddr()
{}
private:
struct sockaddr_in _sock;
};
四、NoCopy.hpp(防拷贝)
#pragma once
class Nocopy
{
public:
Nocopy() {}
~Nocopy() {}
Nocopy(const Nocopy&) = delete;
const Nocopy& operator=(const Nocopy&) = delete;
};
五、Lockguard.hpp(自动管理锁)
#pragma once
#include <iostream>
class Mutex
{
public:
Mutex(pthread_mutex_t* lock)
:pmutex(lock)
{}
void Lock()
{
pthread_mutex_lock(pmutex);
}
void Unlock()
{
pthread_mutex_unlock(pmutex);
}
~Mutex()
{}
public:
pthread_mutex_t* pmutex;
};
class LockGuard
{
public:
LockGuard(pthread_mutex_t* lock)
:mutex(lock)
{
mutex.Lock();
}
~LockGuard()
{
mutex.Unlock();
}
public:
Mutex mutex;
};
六、Thread.hpp(封装线程)
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
using namespace std;
// typedef function<void()> func_t;
template<class T>
using func_t = function<void(T&)>;
template<class T>
class Thread
{
public:
Thread(const string &threadname, func_t<T> func ,const T& data)
: _pid(0), _threadname(threadname), _func(func), isrunning(false) , _data(data)
{
}
// 线程需要执行的函数
static void *ThreadRoutine(void *arg)
{
Thread *pt = (Thread *)arg;
pt->_func(pt->_data);
return nullptr;
}
// 线程开创建并执行
bool Start()
{
int n = pthread_create(&_pid, nullptr, ThreadRoutine, this);
if (n == 0)
{
isrunning = true;
// cout << "is strat , is running : " << isrunning << endl;
return true;
}
else
{
return false;
}
}
// 线程等待
bool Join()
{
if(!isrunning) return false;
return pthread_join(_pid, nullptr);
}
bool IsRunning()
{
return isrunning;
}
string ThreadName()
{
return _threadname;
}
~Thread()
{
}
private:
pthread_t _pid;
string _threadname;
bool isrunning;
func_t<T> _func;
T _data;
};
七、ThreadPool.hpp(线程池)
#pragma once
#include <string>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Thread.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"
using namespace std;
const int default_threadnum = 3;
class ThreadDate
{
public:
ThreadDate(const string &name)
: threadname(name)
{
}
~ThreadDate()
{
}
public:
string threadname;
};
template <class T>
class ThreadPool
{
public:
static ThreadPool *GetInstance()
{
if (_psl == nullptr)
{
LockGuard lock(&_sig_lock);
if (_psl == nullptr)
{
lg.LogMessage(Info, "create singleton is success\n");
_psl = new ThreadPool<T>();
}
}
return _psl;
}
static void DelInstance()
{
if (_psl)
{
LockGuard lock(&_sig_lock);
if (_psl)
{
delete _psl;
_psl = nullptr;
lg.LogMessage(Info, "destroy singleton is success\n");
}
}
}
// 启动所有线程
bool Start()
{
for (auto &t : _threads)
{
t.Start();
lg.LogMessage(Info, "%s , is running...\n", t.ThreadName().c_str());
}
return true;
}
// 等待所有线程终止
void Join()
{
for (auto &t : _threads)
{
t.Join();
}
}
// 线程等待当前条件变量
void Thread_Wait(const ThreadDate &td)
{
pthread_cond_wait(&_cond, &_mutex);
lg.LogMessage(Debug, "no task , %s is sleeping...\n", td.threadname.c_str());
}
// 唤醒当前条件变量下的某个线程
void Thread_Wakeup()
{
pthread_cond_signal(&_cond);
}
// 添加任务
bool Push(T &in)
{
LockGuard lockguard(&_mutex);
_q.push(in);
Thread_Wakeup();
return true;
}
// 线程需要执行的回调函数
void ThreadRun(const ThreadDate &td)
{
while (1)
{
T t;
// 取任务
{
LockGuard lockguard(&_mutex);
while (_q.empty())
{
Thread_Wait(td);
lg.LogMessage(Debug, "haven task , %s is wakeup\n", td.threadname.c_str());
}
t = _q.front();
_q.pop();
}
// 执行任务
t();
// lg.LogMessage(Debug, "%s handler task %s done , result is %s\n",
// td.threadname.c_str(), t.PrintTask().c_str(), t.PrintResult().c_str());
}
}
private:
ThreadPool(int num = default_threadnum)
: _threadnum(num)
{
// 初始化锁和条件变量
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_cond, nullptr);
// 创建线程
for (int i = 0; i < _threadnum; i++)
{
string threadname = "thread-" + to_string(i + 1);
ThreadDate td(threadname);
// 由于Thread执行的是线程池的类内函数,而Thread调用的函数中并没有this指针
// 所以这里就使用bind函数,调整一下Thread调用函数的参数,使函数可以多接收一个参数
_threads.push_back(Thread<ThreadDate>(threadname, bind(&ThreadPool<T>::ThreadRun, this, placeholders::_1), td));
lg.LogMessage(Info, "%s is create\n", threadname.c_str());
}
}
// 销毁锁和条件变量
~ThreadPool()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
ThreadPool(const ThreadPool&) = delete;
const ThreadPool& operator=(const ThreadPool&) = delete;
private:
queue<T> _q;
vector<Thread<ThreadDate>> _threads;
int _threadnum;
static ThreadPool<T> *_psl;
static pthread_mutex_t _sig_lock;
pthread_mutex_t _mutex;
pthread_cond_t _cond;
};
template <class T>
ThreadPool<T> *ThreadPool<T>::_psl = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::_sig_lock = PTHREAD_MUTEX_INITIALIZER;
八、dict.txt(配置文件、简单字典)
apple 苹果
book 书
cat 猫
dog 狗
eat 吃
fly 飞
happy 快乐
jump 跳
kiss 吻
love 爱
moon 月亮
night 夜
pen 笔
red 红色
run 跑
sad 悲伤
sing 唱歌
star 星星
tree 树
water 水
big 大的
boy 男孩
car 汽车
dance 跳舞
fast 快的
girl 女孩
九、Translate.hpp(提供翻译服务)
#pragma once
#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <string.h>
#include <error.h>
#include "Log.hpp"
const char* defalutpath = "resourse/dict.txt";
const static std::string Sep = " ";
const static std::string unknown = "unknown";
class Translate
{
public:
Translate()
:_dict_path(defalutpath)
{
Loaddict();
Parse();
}
// 加载字典
void Loaddict()
{
std::ifstream in(_dict_path.c_str(),std::ifstream::in);
if(!in)
lg.LogMessage(Fatal,"ifstream error , errno : %d , error string : %s\n",errno,strerror(errno));
std::string line;
while(getline(in,line))
{
_lines.push_back(line);
}
}
// 执行翻译
std::string Execute(const std::string& word)
{
if(_dict.find(word) != _dict.end())
{
return _dict[word];
}
return "unknown";
}
// 将读取到的一行中英文单词互译拆分为:英文和中文
void Parse()
{
for(auto& line : _lines)
{
size_t pos = line.find(Sep);
if(pos == std::string::npos)
{
continue;
}
std::string word = line.substr(0,pos);
// 跳过空格
while(line[pos] == ' ')
{
pos++;
}
std::string chinese = line.substr(pos);
_dict.insert(std::make_pair(word,chinese));
}
}
// 用于测试
void Debug()
{
for(auto& line:_lines)
{
std::cout << line << std::endl;
}
for(auto& tmp:_dict)
{
std::cout << tmp.first << " : " << tmp.second << std::endl;
}
}
~Translate()
{}
private:
std::string _dict_path;
std::vector<std::string> _lines;
std::unordered_map<std::string,std::string> _dict;
};
十、Daemon.hpp(使进程变为守护进程)
#pragma once
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
const char* root = "/";
const char* dev_null = "/dev/null";
bool Daemon(bool nochdir, bool noclose)
{
// 1、忽略可能引起程序异常退出的信号 SIGCHLD SIGPIPE
signal(SIGCHLD,SIG_IGN);
signal(SIGPIPE,SIG_IGN);
// 2、创建子进程,让父进程退出,使得子进程不成为组长
pid_t pid = fork();
if(pid > 0) exit(0);
// 3、设置自己成为一个新的会画,setsid
setsid();
// 4、每一个进程都有自己的CWD(当前工作路径),是否将当前进程的CWD改为根目录
// 改为根目录以后,进程可以以绝对路径的方式找到操作系统中的文件
if(nochdir)
chdir(root);
// 5、变成守护进程以后,就不需要与用户的输入、输出和错误进行关联了
// 可以将它们全部关闭,但难免服务器中会有输入、输出和错误
// 向关闭的文件描述符中写入可能会导致进程退出
// 所以这里将它们关闭不是最优解,而是将它们重定向到/dev/null中
// 因为写入到/dev/null的数据会被直接丢弃,而从/dev/null读取信息,会默认读取到文件结尾
if(noclose)
{
int fd = open(dev_null,O_RDWR);
if(fd > 0)
{
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
close(fd);
}
}
else // 不推荐
{
close(0);
close(1);
close(2);
}
return true;
}
十一、TcpServer.hpp(V1~V4版服务端封装)
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <string>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <functional>
#include "Comm.hpp"
#include "Log.hpp"
#include "Nocopy.hpp"
#include "LockGuard.hpp"
#include "InetAddr.hpp"
#include "Thread.hpp"
#include "ThreadPool.hpp"
const static int default_backlog = 5;
using task_t = function<void(int,InetAddr&)>;
class TcpServer;
class ThreadDate
{
public:
ThreadDate(TcpServer* pts,int sockfd,const InetAddr& addr)
:_sockfd(sockfd),_pts(pts),_addr(addr)
{}
int Sockfd()
{
return _sockfd;
}
InetAddr& GetAddr()
{
return _addr;
}
TcpServer* GetPTcpserver()
{
return _pts;
}
~ThreadDate()
{}
private:
int _sockfd;
TcpServer* _pts;
InetAddr _addr;
};
class TcpServer : public Nocopy
{
public:
TcpServer(uint16_t port)
:_port(port),_isrunning(false)
{}
void Init()
{
// 创建套接字
_listensock = socket(AF_INET,SOCK_STREAM,0);
if(_listensock < 0)
{
lg.LogMessage(Fatal,"create socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
exit(Socket_err);
}
lg.LogMessage(Info , "create socket success , sockfd : %d\n",_listensock);
int opt = 1;
setsockopt(_listensock,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));
// 填充网络信息,并绑定
struct sockaddr_in ServerSockaddr;
ServerSockaddr.sin_family = AF_INET;
ServerSockaddr.sin_port = htons(_port);
ServerSockaddr.sin_addr.s_addr = INADDR_ANY;
socklen_t ServerLen = sizeof(ServerSockaddr);
int n = bind(_listensock , (struct sockaddr*)&ServerSockaddr,ServerLen);
if(n < 0)
{
lg.LogMessage(Fatal,"bind socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
exit(Bind_err);
}
lg.LogMessage(Info , "bind socket success , sockfd : %d\n",_listensock);
// 将listensock调节为监听模式
int m = listen(_listensock,default_backlog);
if(m < 0)
{
lg.LogMessage(Fatal,"listen socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
exit(Listen_err);
}
lg.LogMessage(Info , "listen socket success , sockfd : %d\n",_listensock);
}
// v1、v2、v3版本Service
// void Service(int sockfd)
// {
// char buffer[1024];
// while(true)
// {
// int n = read(sockfd,buffer,sizeof(buffer) - 1);
// if(n > 0)
// {
// buffer[n] = 0;
// std::cout << "Client Say# " << buffer << std::endl;
// std::string response = buffer;
// int m = write(sockfd,response.c_str(),response.size());
// }
// else if(n == 0)
// {
// lg.LogMessage(Warning,"client quit\n");
// break;
// }
// else
// {
// lg.LogMessage(Fatal,"read socket error , errno : %d , error string : %s\n",errno,strerror(errno));
// break;
// }
// }
// }
// v4版本Service
void Service(int sockfd,InetAddr& inetAddr)
{
char buffer[1024];
while(true)
{
int n = read(sockfd,buffer,sizeof(buffer) - 1);
if(n > 0)
{
buffer[n] = 0;
std::cout << inetAddr.PrintDebug() << buffer << std::endl;
std::string response = buffer;
int m = write(sockfd,response.c_str(),response.size());
}
else if(n == 0)
{
lg.LogMessage(Warning,"client quit\n");
break;
}
else
{
lg.LogMessage(Fatal,"read socket error , errno : %d , error string : %s\n",errno,strerror(errno));
break;
}
}
}
static void* HandlerRoutine(void* arg)
{
// 使主线程与新线程分离,使得主线程不需要等待新线程终止
pthread_detach(pthread_self());
ThreadDate* td = (ThreadDate*)arg;
td->GetPTcpserver()->Service(td->Sockfd(),td->GetAddr());
// 关闭文件描述符,delete ThreadDate
close(td->Sockfd());
delete td;
return nullptr;
}
void Start()
{
_isrunning = true;
while(_isrunning)
{
// 获取连接
struct sockaddr_in ClientSockaddr;
socklen_t ClientLen = sizeof(ClientSockaddr);
int sockfd = accept(_listensock,CONV(&ClientSockaddr),&ClientLen);
if(sockfd < 0)
{
lg.LogMessage(Warning,"accept socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
break;
}
lg.LogMessage(Info , "accept socket success , get a new sockfd : %d\n",sockfd);
// v3版本服务,忽略子进程退出时给父进程发送的信号
signal(SIGCHLD,SIG_IGN);
// 提供服务
// v1版本,单进程版本
// Service(sockfd);
// close(sockfd);
// v2版本,多进程版本 ,父进程负责接收连接,子进程(孙子进程)负责处理连接
// 这里父进程和子进程需要关闭自己不需要的文件描述符
// 防止多个客户端访问服务器时,导致父进程的文件描述符不够用的情况
// int fd = fork();
// if(fd < 0)
// {
// lg.LogMessage(Fatal,"fork child process fail , errno : %d , error string : %s\n",errno,strerror(errno));
// break;
// }
// else if(fd == 0) // 子进程
// {
// // 这里我们不想让父进程等待子进程退出
// // 我们创建一个孙子进程,子进程退出
// // 这样孙子进程变成了孤儿进程,被操作系统领养
// // 退出时,操作系统会回收孙子进程
// if(fork())
// exit(0);
// Service(sockfd);
// close(sockfd);
// close(_listensock);
// }
// else // 父进程
// {
// close(sockfd);
// }
// v3版本,多进程版本,忽略信号版本
// 忽略子进程退出时给父进程发送的信号,父进程就不需要等待子进程退出了
// int fd = fork();
// if(fd < 0)
// {
// lg.LogMessage(Fatal,"fork child process fail , errno : %d , error string : %s\n",errno,strerror(errno));
// break;
// }
// else if(fd == 0) // 子进程
// {
// Service(sockfd);
// close(sockfd);
// close(_listensock);
// }
// else // 父进程
// {
// close(sockfd);
// }
// v4版本,多线程版本
// pthread_t pth;
// ThreadDate* ptd = new ThreadDate(this,sockfd,InetAddr(ClientSockaddr));
// pthread_create(&pth,nullptr,HandlerRoutine,ptd);
}
}
~TcpServer()
{}
private:
uint16_t _port;
int _listensock;
bool _isrunning;
};
十二、TcpServer.hpp(最终版,V5版服务端封装)
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <string>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <functional>
#include <unordered_map>
#include "Comm.hpp"
#include "Log.hpp"
#include "Nocopy.hpp"
#include "LockGuard.hpp"
#include "InetAddr.hpp"
#include "Thread.hpp"
#include "ThreadPool.hpp"
#include "Translate.hpp"
const static int default_backlog = 5;
using callback_t = function<void(int,InetAddr&)>; // 回调函数
using task_t = function<void(void)>;
class TcpServer : public Nocopy
{
public:
TcpServer(uint16_t port)
:_port(port),_isrunning(false)
{}
void Init()
{
// 创建套接字
_listensock = socket(AF_INET,SOCK_STREAM,0);
if(_listensock < 0)
{
lg.LogMessage(Fatal,"create socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
exit(Socket_err);
}
lg.LogMessage(Info , "create socket success , sockfd : %d\n",_listensock);
int opt = 1;
setsockopt(_listensock,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));
// 填充网络信息,并绑定
struct sockaddr_in ServerSockaddr;
ServerSockaddr.sin_family = AF_INET;
ServerSockaddr.sin_port = htons(_port);
ServerSockaddr.sin_addr.s_addr = INADDR_ANY;
socklen_t ServerLen = sizeof(ServerSockaddr);
int n = bind(_listensock , (struct sockaddr*)&ServerSockaddr,ServerLen);
if(n < 0)
{
lg.LogMessage(Fatal,"bind socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
exit(Bind_err);
}
lg.LogMessage(Info , "bind socket success , sockfd : %d\n",_listensock);
// 将listensock调节为监听模式
int m = listen(_listensock,default_backlog);
if(m < 0)
{
lg.LogMessage(Fatal,"listen socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
exit(Listen_err);
}
lg.LogMessage(Info , "listen socket success , sockfd : %d\n",_listensock);
// 启动线程池
ThreadNS::ThreadPool<task_t>::GetInstance()->Start();
// 添加默认任务
RegisterFunc("defalutserver",bind(&TcpServer::DefalutServer,this,placeholders::_1,placeholders::_2));
}
// v4版本Service
void Service(int sockfd,InetAddr& inetAddr)
{
char buffer[1024];
while(true)
{
int n = read(sockfd,buffer,sizeof(buffer) - 1);
if(n > 0)
{
buffer[n] = 0;
std::cout << inetAddr.PrintDebug() << buffer << std::endl;
std::string response = buffer;
int m = write(sockfd,response.c_str(),response.size());
}
else if(n == 0)
{
lg.LogMessage(Warning,"client quit\n");
break;
}
else
{
lg.LogMessage(Fatal,"read socket error , errno : %d , error string : %s\n",errno,strerror(errno));
break;
}
}
}
// 效果:| ping | translate | transform |
// 获取任务列表
void DefalutServer(int sockfd,InetAddr& inetaddr)
{
(void)inetaddr; // 这里未使用inetaddr,强转防止警告
string task_list = "| ";
for(auto task : _task_list)
{
if(task.first != "defalutserver")
{
task_list += task.first;
task_list += " | ";
}
}
write(sockfd,task_list.c_str(),task_list.size());
}
// 读取客户端所需服务
string Read(int sockfd,InetAddr& inetaddr)
{
char buffer[1024];
int n = read(sockfd,buffer,sizeof(buffer) - 1);
if(n > 0)
{
buffer[n] = 0;
}
else if(n == 0)
{
lg.LogMessage(Warning,"client quit\n");
}
else
{
lg.LogMessage(Fatal,"read socket error , errno : %d , error string : %s\n",errno,strerror(errno));
}
return buffer;
}
// 任务转接
void Routine(int sockfd,InetAddr& inetaddr)
{
_task_list["defalutserver"](sockfd,inetaddr);
string type = Read(sockfd,inetaddr);
lg.LogMessage(Info,"%s select %s\n",inetaddr.PrintDebug().c_str(),type.c_str());
if(type == "ping")
{
_task_list[type](sockfd,inetaddr);
}
else if(type == "translate")
{
_task_list[type](sockfd,inetaddr);
}
else if(type == "transform")
{
_task_list[type](sockfd,inetaddr);
}
else
{}
close(sockfd);
}
// 注册任务
void RegisterFunc(const string& type , callback_t cb)
{
_task_list[type] = cb;
}
void Start()
{
_isrunning = true;
while(_isrunning)
{
// 获取连接
struct sockaddr_in ClientSockaddr;
socklen_t ClientLen = sizeof(ClientSockaddr);
int sockfd = accept(_listensock,CONV(&ClientSockaddr),&ClientLen);
if(sockfd < 0)
{
lg.LogMessage(Warning,"accept socket fail , errno : %d , error string : %s\n",errno,strerror(errno));
break;
}
lg.LogMessage(Info , "accept socket success , get a new sockfd : %d\n",sockfd);
// v5版本,线程池版本
// 其他四个版本在TcpServer copy.hpp中
// task_t task = bind(&TcpServer::Service,this,sockfd,InetAddr(ClientSockaddr));
// 将任务添加到线程池中
task_t task = bind(&TcpServer::Routine,this,sockfd,InetAddr(ClientSockaddr));
ThreadNS::ThreadPool<task_t>::GetInstance()->Push(task);
}
}
~TcpServer()
{}
private:
uint16_t _port;
int _listensock;
bool _isrunning;
// 任务列表
unordered_map<string,callback_t> _task_list;
};
十三、Main.cpp(服务端)
#include <iostream>
#include <string>
#include <memory>
#include <algorithm>
#include <cctype>
#include "TcpServer.hpp"
#include "InetAddr.hpp"
#include "Log.hpp"
#include "Translate.hpp"
#include "Daemon.hpp"
using namespace std;
Translate trans;
void Usage(const string& proc)
{
cout << proc << " localport\n" << endl;
}
// 未来我们不知道我们的服务器在任意一个时刻是否是健康的
// 所以我们可以让客户端定期的向服务器发送最小请求,若服务器有回应,则说明服务正常
// 这种机制我们称之为心跳机制
// ping服务,客户端发如何消息,服务端回复pong,表示服务器健康
void Ping(int sockfd,InetAddr& inetaddr)
{
lg.LogMessage(Info,"%s select ping\n",inetaddr.PrintDebug().c_str());
char buffer[1024];
int n = read(sockfd,buffer,sizeof(buffer) - 1);
if(n > 0)
{
string response = "Pong";
write(sockfd,response.c_str(),response.size());
}
else if(n == 0)
{
lg.LogMessage(Warning,"client quit\n");
}
else
{
lg.LogMessage(Fatal,"read socket error , errno : %d , error string : %s\n",errno,strerror(errno));
}
}
// 提供翻译服务,客户端发送一个单词,服务端返回单词的翻译
void Translate(int sockfd,InetAddr& inetaddr)
{
lg.LogMessage(Info,"%s select translate\n",inetaddr.PrintDebug().c_str());
char buffer[1024];
int n = read(sockfd,buffer,sizeof(buffer) - 1);
if(n > 0)
{
buffer[n] = 0;
string word = buffer;
string response = trans.Execute(word);
write(sockfd,response.c_str(),response.size());
}
else if(n == 0)
{
lg.LogMessage(Warning,"client quit\n");
}
else
{
lg.LogMessage(Fatal,"read socket error , errno : %d , error string : %s\n",errno,strerror(errno));
}
}
// 提供转大写服务,服务端将客户端发送的信息,全部转换为大写再发送给客户端
void TransForm(int sockfd,InetAddr& inetaddr)
{
lg.LogMessage(Info,"%s select transform\n",inetaddr.PrintDebug().c_str());
char buffer[1024];
int n = read(sockfd,buffer,sizeof(buffer) - 1);
if(n > 0)
{
buffer[n] = 0;
string messages = buffer;
string response(messages.size(),' ');
std::transform(messages.begin(),messages.end(),response.begin(),[](unsigned char c){return toupper(c);});
write(sockfd,response.c_str(),response.size());
}
else if(n == 0)
{
lg.LogMessage(Warning,"client quit\n");
}
else
{
lg.LogMessage(Fatal,"read socket error , errno : %d , error string : %s\n",errno,strerror(errno));
}
}
int main(int argc , char* argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
uint16_t ServerPort = stoi(argv[1]);
Daemon(true,true);
lg.SwitchStyle(OneFile);
unique_ptr<TcpServer> uq = make_unique<TcpServer>(ServerPort);
uq->RegisterFunc("ping",Ping);
uq->RegisterFunc("translate",Translate);
uq->RegisterFunc("transform",TransForm);
uq->Init();
uq->Start();
return 0;
}
十四、TcpClient.cpp(客户端)
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include "Comm.hpp"
using namespace std;
const static int Retry_Count = 5;
void Usage(const string &proc)
{
cout << proc << " serverip serverport\n" << endl;
}
// // v1~v4版本的客户端
// // v1~v4版本的服务器都是长服务,客户端发送什么消息,服务器添加部分消息后就直接转发服务
// bool VisitServer(const string &serverip, uint16_t serverport,int* count)
// {
// int n = 0 , m = 0;
// string buffer;
// char inbuffer[1024];
// bool ret = true;
// // 填充服务端信息
// struct sockaddr_in server;
// server.sin_family = AF_INET;
// server.sin_port = htons(serverport);
// inet_pton(AF_INET, serverip.c_str(), &server.sin_addr.s_addr);
// // 创建套接字
// int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// if (sockfd < 0)
// {
// cout << "create socket fail" << endl;
// ret = false;
// goto END;
// }
// // 建立连接
// if (connect(sockfd, CONV(&server), sizeof(server)) != 0)
// {
// cout << "connect fail" << endl;
// ret = false;
// goto END;
// }
// *count = 1;
// while(1)
// {
// // 向服务端发送信息
// cout << "Please Enter# ";
// getline(cin, buffer);
// n = write(sockfd, buffer.c_str(), buffer.size());
// if (n > 0)
// {
// m = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
// if (m > 0)
// {
// inbuffer[m] = 0;
// cout << "get a message : " << inbuffer << endl;
// }
// else
// {
// ret = false;
// goto END;
// }
// }
// else
// {
// ret = false;
// goto END;
// }
// }
// END:
// close(sockfd);
// return ret;
// }
// v5版本客户端
// 短服务,服务一次后就退出
bool VisitServer(const string &serverip, uint16_t serverport,int* count)
{
int n = 0 , m = 0;
string buffer;
char inbuffer[1024];
bool ret = true;
// 填充服务端信息
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
inet_pton(AF_INET, serverip.c_str(), &server.sin_addr.s_addr);
// 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
cout << "create socket fail" << endl;
ret = false;
goto END;
}
// 建立连接
if (connect(sockfd, CONV(&server), sizeof(server)) != 0)
{
cout << "connect fail" << endl;
ret = false;
goto END;
}
*count = 1;
// 获取服务列表
m = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
if (m > 0)
{
inbuffer[m] = 0;
cout << "server list : " << inbuffer << endl;
}
else
{
ret = false;
goto END;
}
// 选择服务
cout << "Please Select Server# ";
getline(cin, buffer);
if(buffer == "quit")
{
ret = true;
goto END;
}
n = write(sockfd, buffer.c_str(), buffer.size());
// 向服务端发送信息
cout << "Enter> ";
getline(cin, buffer);
n = write(sockfd, buffer.c_str(), buffer.size());
if (n > 0)
{
m = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
if (m > 0)
{
inbuffer[m] = 0;
cout << inbuffer << endl;
}
else
{
ret = false;
goto END;
}
}
else
{
ret = false;
goto END;
}
END:
close(sockfd);
return ret;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
string serverip = argv[1];
uint16_t serverport = stoi(argv[2]);
// 断线重连
int count = 1;
while(count <= Retry_Count)
{
if(VisitServer(serverip,serverport,&count))
{
break;
}
else
{
sleep(1);
cout << "server offline , retrying... , count : " << count << endl;
count++;
}
}
if(count >= Retry_Count)
cout << "server offline , retrying... , count : " << count << endl;
return 0;
}
结尾
如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹