做网站产品图片素材,网站开发详细设计模板,没有照片怎么做网站,做网站收广告费文章目录 前言一、自定义协议传结构体对象 序列化和反序列化什么是序列化#xff1f;反序列化 二、计算器服务端#xff08;线程池版本#xff09;1.main.cc2.Socket.hpp3.protocol.hpp4.Calculator.hpp5.serverCal.hpp6.threadPool.hpp7.Task.hpp8. log.hpp 客户端Windows客… 文章目录 前言一、自定义协议传结构体对象 序列化和反序列化什么是序列化反序列化 二、计算器服务端线程池版本1.main.cc2.Socket.hpp3.protocol.hpp4.Calculator.hpp5.serverCal.hpp6.threadPool.hpp7.Task.hpp8. log.hpp 客户端Windows客户端运行 前言
我们已经学会了Tcp、Udp网络传输协议并且之前我们也实现了简易的聊天室和翻译器。
我们知道传输层是OS系统里就给我们写好的 应用层才是我们自己需要去编写的现在我们对应用层来进行一些初步的了解。 一、自定义协议
在之前我们使用Tcp和Udp服务进行网络通信我们一直都是以字符串的形式互相发送消息。
那么我们就只能发送字符串吗 当然不是Udp是基于数据流进行网络通信Udp是基于字节流进行网络通信虽然我们对字节流和数据流并没有一个特别清晰的认识但是我们可以知道的是我们其实是可以传各种各样的类型进行通行的。
这里就提出一种方案
传结构体对象
从我们之前写的代码来看只要我们在发送数据和接收数据时制定一个协议每次只读写一个结构体对象的大小那么我们就可以将该结构体进行通信。 而实际上底层的很多库也确实是这么做的但是这并不代表这种方式不存在明显弊端。
最大的弊端就是你能保证在不同环境之下同样的结构体类型在不同主机、不同环境下的大小是一样的吗。 结构体的大小涉及到许多比如说结构体字节对齐32位和64位系统下内置类型大小可能不同…
所以我们并不推崇这种方案。
而实际上我们其实更推崇传字符串的方式。
序列化和反序列化
什么是序列化
在现实生活中我们介绍自己有些人习惯先说自己的名字有些人习惯先说自己来自于哪一个城市。 如果你需要去统计大量的人的信息最好就是先列一个表格然后让他们严格按照表格上的个人信息顺序去介绍自己这就是序列化。
再比如说我们今天要写一个网络版本的计算器我可以写成11也可以写成一加一再也可以写写成1 1中间带几个空格。 那么这样的话我们服务器接受到的数据就是形形色色的不利于我们去解析。
于是我们就制定一个协议你必须要写成1 1的形式否则就是违反协议
反序列化
反序列化很简单就比如说我们收到了一段数据它是一个字符串形式的1 1我们就需要将该字符串进行解析反序列化成int x 1, char op ‘’ , int y 1. 这也是一种反序列化。
二、计算器服务端线程池版本
1.main.cc
#include serverCal.hppvoid Usage(const char *mes)
{std::cout Usage: mes port[8080-9000] std::endl;
}const std::string default_ip 0.0.0.0;enum{Usage_Err 1
};int main(int argc, char *argv[])
{if (argc ! 2){Usage(./serverCal);exit(Usage_Err);}ServerCal sc;sc.Init(AF_INET, default_ip, atoi(argv[1]));sc.Run();return 0;
}2.Socket.hpp
对套接字进行封装
#pragma once#include iostream
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include log.hpp
enum
{Socket_Err 1,Bind_Err,Listen_Err
};extern Log lg;const int backlog 10;class Socket
{
public:Socket(): _sockfd(-1){}int Getfd(){return _sockfd;}void Init(){int socket_fd socket(AF_INET, SOCK_STREAM, 0);if (socket_fd 0){lg(Fatal, Socket Create Failed...);exit(Socket_Err);}lg(Info, Socket Create Succeeded...);_sockfd socket_fd;}void Bind(const int sinfamily, const std::string ip, const uint16_t port){memset(_sockaddr, 0, sizeof _sockaddr);switch (sinfamily){case AF_INET:_sockaddr.sin_family AF_INET;break;case AF_INET6:_sockaddr.sin_family AF_INET6;break;}_sockaddr.sin_port htons(port);inet_aton(ip.c_str(), (_sockaddr.sin_addr));int n bind(_sockfd, (const struct sockaddr *)_sockaddr, sizeof _sockaddr);if (n 0){lg(Fatal, Bind Failed...);exit(Bind_Err);}lg(Info, Bind Succeeded..., port: %d, port);}void Listen(){int n listen(_sockfd, backlog);if (n 0){lg(Fatal, Listen Failed...);exit(Listen_Err);}lg(Info, Listen Succeeded...);}int Accept(struct sockaddr_in *clientsock, socklen_t *len){int fd accept(_sockfd, (sockaddr *)clientsock, len);if (fd 0){lg(Warning, Accept Failed...);return -1;}lg(Info, Accept Succeeded..., Get A new Link, fd: %d, fd);return fd;}int Connect(const std::string ip, const std::string port){struct sockaddr_in serversock;serversock.sin_port htons(atoi(port.c_str()));serversock.sin_family AF_INET;inet_aton(ip.c_str(), serversock.sin_addr);int n connect(_sockfd, (const struct sockaddr *)serversock, sizeof serversock);if (n 0){lg(Warning, Accept Failed...);return n;}lg(Info, Connect Succeeded...);return n;}~Socket(){close(_sockfd);}private:int _sockfd;struct sockaddr_in _sockaddr;
};3.protocol.hpp
序列化和反序列化的协议制定
#pragma once
#include iostream
#include string
#include log.hppextern Log lg;
const char blank_space_sep ;
const char protocol_sep \n;enum Code
{Div_Zero_Err 1,Mod_Zeor_Err,Operatorr_Err
};class Request
{
public:Request() {} // 提供一个无参构造Request(int x, int y, char op): _x(x), _y(y), _operator(op) {}bool serialize(std::string *out_str){// 协议规定 字符串格式应序列化为len\n_x _y\nstd::string main_body std::to_string(_x);main_body blank_space_sep;main_body _operator;main_body blank_space_sep;main_body std::to_string(_y);*out_str std::to_string(main_body.size());*out_str protocol_sep;*out_str main_body;*out_str protocol_sep;return true;}bool deserialize(std::string in_str){// 协议规定 in_str的格式应为len\n_x _y\n...size_t pos in_str.find(protocol_sep);if (pos std::string::npos){// 说明没找到\nlg(Warning, Message Format Error..., No Found The First Second \\n);return false;} std::string sl in_str.substr(0, pos);int len std::stoi(sl); // 如果这里的sl不是一串数字stoi就会抛异常 BUGint total_len sl.size() 1 len 1;if (in_str.size() total_len){lg(Warning, Message Format Error..., Lenth Error);return false;}if (in_str[total_len - 1] ! \n){lg(Warning, Message Format Error..., No Found The Second \\n);return false;}std::string main_body in_str.substr(pos 1, len);// main_body_x _yint left main_body.find(blank_space_sep);if (left std::string::npos){// 说明没找到 lg(Warning, Message Format Error..., No Found The First );return false;}int right main_body.rfind(blank_space_sep);if (left right){// 说明只有一个 lg(Warning, Message Format Error...,No Found The Second );return false;}_x std::stoi(main_body.substr(0, left)); // 如果这里的sl不是一串数字stoi就会抛异常 BUG_y std::stoi(main_body.substr(right 1)); // 如果这里的sl不是一串数字stoi就会抛异常 BUG_operator main_body[left 1];in_str.erase(0, total_len);return true;}void print(){std::cout _x _operator _y std::endl;}~Request() {}public:int _x;int _y;char _operator;
};class Respond
{
public:Respond() {} // 提供一个无参构造Respond(int result, int code): _result(result), _code(code) {}bool serialize(std::string *out_str){// 协议规定 字符串格式应序列化为_result _code*out_str std::to_string(_result);*out_str blank_space_sep;*out_str std::to_string(_code);return true;}bool deserialize(const std::string in_str){// 协议规定 in_str的格式应为_result _codesize_t pos in_str.find(blank_space_sep);if (pos std::string::npos){// 没找到字符 lg(Warning, Result Message Error...);return false;}_result std::stoi(in_str.substr(0, pos));_code std::stoi(in_str.substr(pos 1));return true;}void print(){std::cout _result _code std::endl;}~Respond() {}public:int _result;int _code -1; // 表示结果可信度 0表示可信
};4.Calculator.hpp
计算器功能接口函数
#pragma once
#includeprotocol.hppclass Calculator{public:Calculator() {}Respond calculate(const Request rq){Respond rs;switch (rq._operator){case :rs._result rq._x rq._y;break;case -:rs._result rq._x - rq._y;break;case *:rs._result rq._x * rq._y;break;case /:if(rq._y 0){lg(Warning,Found Div Zero Error...);rs._code Div_Zero_Err;return rs;}rs._result rq._x / rq._y;break;case %:if(rq._y 0){lg(Warning,Found Mod Zero Error...);rs._code Mod_Zeor_Err;return rs;}rs._result rq._x - rq._y;break;default:lg(Warning,Found Operator Error...);rs._code Operatorr_Err;return rs;}rs._code 0;return rs;}
}; 5.serverCal.hpp
代码如下示例
#pragma once#include Socket.hpp
#include protocol.hpp
#include threadPool.hpp
#include Task.hpp
class ServerCal
{
public:ServerCal(){}void Init(const int sinfamily, const std::string ip, const uint16_t port){_listensock.Init();_listensock.Bind(sinfamily, ip, port);_listensock.Listen();}void Run(){ThreadPoolTask *tp ThreadPoolTask::GetInstance();tp-Start();struct sockaddr_in client;while (true){memset(client, 0, sizeof client);socklen_t len;int socketfd _listensock.Accept(client, len);if (socketfd 0)continue;tp-Push(socketfd);}}private:Socket _listensock;
};6.threadPool.hpp
很熟悉的线程池封装
#pragma once#include iostream
#include vector
#include string
#include queue
#include pthread.h
#include unistd.hstruct ThreadInfo
{pthread_t tid;std::string name;
};static const int defalutnum 10;template class T
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(mutex_);}void Unlock(){pthread_mutex_unlock(mutex_);}void Wakeup(){pthread_cond_signal(cond_);}void ThreadSleep(){pthread_cond_wait(cond_, mutex_);}bool IsQueueEmpty(){return tasks_.empty();}std::string GetThreadName(const pthread_t tid){for (const auto ti : threads_){if (ti.tid tid)return ti.name;}return None;}public:static void *HandlerTask(void *args){ThreadPoolT *tp static_castThreadPoolT *(args);std::string name tp-GetThreadName(pthread_self());while (true){tp-Lock();while (tp-IsQueueEmpty()){tp-ThreadSleep();}T t tp-Pop();tp-Unlock();t();}}void Start(){int num threads_.size();for (int i 0; i num; i){threads_[i].name thread- std::to_string(i 1);pthread_create((threads_[i].tid), nullptr, HandlerTask, this);}}T Pop(){T t tasks_.front();tasks_.pop();return t;}void Push(const T t){Lock();tasks_.push(t);Wakeup();Unlock();}static ThreadPoolT *GetInstance(){if (nullptr tp_) // ???{pthread_mutex_lock(lock_);if (nullptr tp_){// std::cout log: singleton create done first! std::endl;tp_ new ThreadPoolT();}pthread_mutex_unlock(lock_);}return tp_;}private:ThreadPool(int num defalutnum) : threads_(num){pthread_mutex_init(mutex_, nullptr);pthread_cond_init(cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(mutex_);pthread_cond_destroy(cond_);}ThreadPool(const ThreadPoolT ) delete;const ThreadPoolT operator(const ThreadPoolT ) delete; // abc
private:std::vectorThreadInfo threads_;std::queueT tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPoolT *tp_;static pthread_mutex_t lock_;
};template class T
ThreadPoolT *ThreadPoolT::tp_ nullptr;template class T
pthread_mutex_t ThreadPoolT::lock_ PTHREAD_MUTEX_INITIALIZER; 7.Task.hpp
派发给线程池的任务
#pragma once
#include Socket.hpp
#include protocol.hpp
#include Calculator.hpp
class Task
{
public:Task(int socket_fd): _socket_fd(socket_fd){}void run(){char in_buffer[1024];Calculator cal;std::string message ;while (true){memset(in_buffer, 0, sizeof in_buffer);//std:: cout 开始等待读取... std::endl;int n read(_socket_fd, (void *)in_buffer, sizeof in_buffer - 1);//std::cout n strerror(errno) std::endl;//std::cout 读取到的有效字符为 n std::endl;if (n 0){lg(Warning, Connection closed by foreign host, socketfd[%d] closed..., _socket_fd);break;}else if (n 0){lg(Warning, Read Error, socketfd[%d]..., _socket_fd);break;}in_buffer[n] 0;message in_buffer;//std::cout 报文大小: message.size() ,报文内容: message std::endl;Request rq;if(!rq.deserialize(message)) continue;Respond rs cal.calculate(rq);std::string res;rs.serialize(res);printf(%d %c %d %d\n,rq._x,rq._operator,rq._y,rs._result);write(_socket_fd, res.c_str(), res.size());}}void operator()(){run();close(_socket_fd);}~Task(){}private:int _socket_fd;
};8. log.hpp
输出日志消息
#pragma once#include iostream
#include time.h
#include stdarg.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include stdlib.h#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile log.txtclass Log
{
public:Log(){printMethod Screen;path ./log/;}void Enable(int method){printMethod method;}std::string levelToString(int level){switch (level){case Info:return Info;case Debug:return Debug;case Warning:return Warning;case Error:return Error;case Fatal:return Fatal;default:return None;}}void printLog(int level, const std::string logtxt){switch (printMethod){case Screen:std::cout logtxt std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string logname, const std::string logtxt){std::string _logname path logname;int fd open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // log.txtif (fd 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string logtxt){std::string filename LogFile;filename .;filename levelToString(level); // log.txt.Debug/Warning/FatalprintOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t time(nullptr);struct tm *ctime localtime(t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(),ctime-tm_year 1900, ctime-tm_mon 1, ctime-tm_mday,ctime-tm_hour, ctime-tm_min, ctime-tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式默认部分自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), %s %s, leftbuffer, rightbuffer);// printf(%s, logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};Log lg; 客户端
#include Socket.hpp
#include protocol.hppvoid Usage(const char *mes)
{std::cout Usage: mes ip[xxx.xxx.xxx.xxx] port[8080-9000] std::endl;
}int main(int argc, char *argv[])
{if (argc ! 3){Usage(./clientCal);}Socket local;local.Init();int n local.Connect(argv[1], argv[2]);if (n 0){return 1;}int localfd local.Getfd();std::cout 简易计算器, 目前仅支持\ - * / %\运算符 std::endl;std::cout 数字和运算符请用空格或回车隔开 std::endl;Request rq;Respond rs;std::string message;char buffer[1024];while (true){memset(buffer, 0, sizeof buffer);std::cout 请输入您的算式 ;std::cin rq._x rq._operator rq._y;rq.serialize(message);write(localfd, message.c_str(), message.size());// 开始等待结果n read(localfd, buffer, sizeof buffer - 1);if (n 0){lg(Warning, Connection closed by foreign host, socketfd[%d] closed...,localfd);break;}else if (n 0){lg(Warning, Read Error, socketfd[%d]..., localfd);break;}buffer[n] 0;std::string res buffer;rs.deserialize(res);if(rs._code ! 0){switch(rs._code){case 1:std::cout 出现除0错误 std::endl;break;case 2:std::cout 出现模0错误 std::endl;break;case 3:std::cout 使用了除 - * / % 以外的运算符 std::endl;break;default:std::cout 发生未知错误 std::endl;break;}continue;}printf(%d %c %d %d\n,rq._x,rq._operator,rq._y,rs._result);}return 0;
}Windows客户端
#includeiostream
#includestring
#includeWinSock2.h
#includeWindows.h
#includefunctional
#includestdlib.h
#includeprotocol.hpp#pragma comment(lib, ws2_32.lib)
#pragma warning(disable:4996)
#pragma execution_character_set(utf-8)const int server_port 8889;
const std::string server_ip 43.143.58.29;int main()
{//初始化网络环境WSADATA wsd;WSAStartup(MAKEWORD(2, 2), wsd);system(chcp 65001);//申请套接字SOCKET socket_fd socket(AF_INET, SOCK_STREAM, 0);if (socket_fd SOCKET_ERROR){perror(Socket Error);exit(1);}//创建并初始化Server端sockaddr_in结构体struct sockaddr_in server;memset(server, 0, sizeof server);server.sin_family AF_INET;server.sin_addr.s_addr inet_addr(server_ip.c_str());server.sin_port htons(server_port);//开始连接服务器int n connect(socket_fd, (const struct sockaddr*)server, sizeof server);if (n 0){//连接失败std::cout Connect Failed std::endl;return 1;}std::cout 简易计算器, 目前仅支持\ - * / %\运算符 std::endl;std::cout 数字和运算符请用空格或回车隔开 std::endl;Request rq;Respond rs;std::string message;char buffer[1024];while (true){memset(buffer, 0, sizeof buffer);std::cout 请输入您的算式 ;std::cin rq._x rq._operator rq._y;rq.serialize(message);send(socket_fd, message.c_str(), (int)message.size(),0);// 开始等待结果n recv(socket_fd, buffer, sizeof buffer - 1,0);if (n 0){lg(Warning, Connection closed by foreign host, socketfd[%d] closed..., socket_fd);break;}else if (n 0){lg(Warning, Read Error, socketfd[%d]..., socket_fd);break;}buffer[n] 0;std::string res buffer;rs.deserialize(res);if (rs._code ! 0){switch (rs._code){case 1:std::cout 出现除0错误 std::endl;break;case 2:std::cout 出现模0错误 std::endl;break;case 3:std::cout 使用了除 - * / % 以外的运算符 std::endl;break;default:std::cout 发生未知错误 std::endl;break;}continue;}printf(%d %c %d %d\n, rq._x, rq._operator, rq._y, rs._result);}//清理环境closesocket(socket_fd);WSACleanup();return 0;
}运行