1 序列化和反序列化
write和read实质是拷贝函数
1.1序列化和反序列化的概述:
2网络版计算器
2.1代码实现
先把日志拷贝过来
2.1.1必须先要有网络功能
先把 TcpServer.hpp编写号
- #pragma once
- #include
-
- #include "Socket.hpp"
- #include "./logs/ljwlog.h"
-
- class TcpServer
- {
- public:
- TcpServer()
- {}
- bool InitServer()
- {}
- void Start()
- {}
-
- ~TcpServer()
- {}
- private:
- uint16_t port;
-
- };
2.1.2 把套接字接口封装一下方便使用
- #pragma once
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- using namespace std;
-
- class Sock
- {
- public:
- Sock()
- {}
- ~Sock()
- {}
- public:
- void Socket()//创建套接字的接口
- {}
- void bind()//绑定的接口
- {}
- void Listen()//监听状态的接口
- {}
- int Accept()//获取连接的接口
- {}
- int Connect()//方便两个客户端和服务器都能使用这个Sock的这个公共方法
- {}
-
- private:
- int sockfd_;
- };
2.1.3 TcpServer.hpp接口的补充
- #pragma once
- #include
-
- #include "Socket.hpp"
- #include "./logs/ljwlog.h"
-
- class TcpServer
- {
- public:
- TcpServer()
- {}
- bool InitServer()
- {
- //先创建套接字,再绑定,设置监听状态
- listensock_.Socket();
- listensock_.bind();
- listensock_.Listen();
- }
- void Start()
- {
- while(true)
- {
- int sockfd = listensock_.Accept();
- }
- }
-
- ~TcpServer()
- {}
- private:
- uint16_t port_;
- Sock listensock_; //叫listen套接字,Listen完后有真正的网络文件描述符
- };
-
-
2.1.4 Sock.hpp接口的补充
- #pragma once
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include "./logs/ljwlog.h"
-
- using namespace std;
-
- enum{
- SocketErr = 2,
- BindErr,
- ListenErr
- };
- const int backlog = 10;
- class Sock
- {
- public:
- Sock()
- {}
- ~Sock()
- {}
- public:
- void Socket()//创建套接字的接口
- {
- sockfd_ = socket(AF_INET, SOCK_STREAM, 0);//流式套接字 第二个参数是协议类型
- if(sockfd_ < 0)
- {
- FATAL("Socket errno,error:%d,errstring:%s", errno, strerror(errno));
- exit(SocketErr);
- }
- }
- void Bind(uint16_t port)//绑定的接口
- {
- 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;//ip默认0.0.0.0
- if(bind(sockfd_, (struct sockaddr*)&local, sizeof(local)) < 0)
- {
- FATAL("Bind errno,error:%d,errstring:%s", errno, strerror(errno));
- exit(BindErr);
- }
- }
- void Listen()//监听状态的接口
- {
- if(listen(sockfd_, backlog) < 0)
- {
- FATAL("Listen errno,error:%d,errstring:%s", errno, strerror(errno));
- exit(ListenErr);
- }
- }
- // 知道谁链接的我
- int Accept(string *clientip, uint16_t *clientport)//获取连接的接口
- {
- struct sockaddr_in peer;//远端的意思
- socklen_t len = sizeof(peer);
- int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);
- if(newfd < 0)
- {
- WARN("accept error, %s: %d", strerror(errno), errno);
- return -1;
- }
- //网络转主机
- //拿出客户端的ip和端口号
- char ipstr[64];
- inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));//网络转主机
- *clientip = ipstr;//网络转主机
- *clientport = ntohs(peer.sin_port);//网络转主机
- return newfd;
- }
- void Close()
- {
- close(sockfd_);
- }
- int Connect()//方便两个客户端和服务器都能使用这个Sock的这个公共方法
- {
- return 0;
- }
-
- private:
- int sockfd_;
- };
2.1.4 TcpServer.hpp提供服务
2.1.5定制协议(Protocol)(约定好)
双方约定好,把请求放在Request(请求),结果放在Response(响应)
约定好了,最好就不要动他了
- #pragma once
-
- #include
-
- class Request
- {
- public:
-
- public:
- int x;
- int y;
- char op;// + - * / %
- };
-
- class Response
- {
- public:
-
- public:
- int result;
- int code;// 0,可信,否则!0具体是几,表明对应的错误原因
- };
2.1.6 (手写)序列化和反序列化
serialization 序列化(把struct 转换成 字符串)
deserialization 反序列化
2.1.6.1序列化
2.1.6.2反序列化
2.1.6.3添加报头(要往网络里发 先添加报头)
这种
2.1.6.4 提出有效载荷
2.1.6.5代码
- #pragma once
-
- #include
- #include
- using namespace std;
-
- const string blank_space_sep = " ";//空格分隔符
- const string protocol_sep = "\n";//报文分隔符
-
-
- //序列化和反序列化解决的是对应的是一个报文内部的问题
- //还要解决一下报文与报文之间的问题
-
- //封装报头
- string Encode(string &content)
- {
- //封装报头 这里规定的协议就是长度+有效载荷
- string package = to_string(content.size());
- package += protocol_sep;
- package += content;
- package += protocol_sep;
-
- return package;
- }
-
- //"len"\n"x op y"\n
- string Decode(string &package)//将"x op y"提取出来
- {
- return "";
- }
-
- class Request
- {
- public:
- Request(int data1, int data2, char oper): x(data1), y(data2), op(oper)
- {}
-
- public:
- bool serialization(string *out)
- {
- //构建报文的有效载荷
- //序列化的就是把结构化的(struct)转成string,
- // "x op y"
- //数字之间是不可能出现分隔符的
- string s = to_string(x);
- s += blank_space_sep;
- s += op;
- s += blank_space_sep;
- s += to_string(y);
-
- *out = s;
- return true;
- }
- //"x op y" 进行分割
- bool deserialization(const string &in)
- {
- size_t left = in.find(blank_space_sep);
- if(left == string::npos) return false;//没找到
- string part_x = in.substr(0, left);
-
- size_t right = in.rfind(blank_space_sep);//逆着找
- if(right == string::npos) return false;//没找到
- string part_y = in.substr(right + 1);
-
- if(left + 2 != right) return false;
- op = in[right -1];
-
- x = stoi(part_x);
- y = stoi(part_y);
- return true;
- }
- public:
- int x;
- int y;
- char op;// + - * / %
- };
-
- class Response
- {
- public:
- Response(int res, int c):result(res), code(c)
- {}
- public:
- //序列化是结构体转字符串的
- bool serialization(string *out)
- {
- // 序列化成这样"result code"
- //构建报文的有效载荷
- string s = to_string(result);
- s += blank_space_sep;
- s += to_string(code);
-
- *out = s;
- return true;
- }
- //反序列化 一定是你要传过来一个字符串
- bool deserialization(const string &in) //"result code" 协议定制好了就必须是这个样子
- {
- size_t pos = in.find(blank_space_sep);
- if(pos == string::npos) return false;//没找到
- string part_left = in.substr(0, pos);
- string part_right = in.substr(pos + 1);
- result = stoi(part_left);
- code = stoi(part_right);
- return true;
- }
- public:
- int result;
- int code;// 0,可信,否则!0具体是几,表明对应的错误原因
- };
2.1.6.6 测试一下序列化和添加报头
- #include
- #include "TcpServer.hpp"
- #include "Protocol.hpp"
-
- using namespace std;
-
- int main()
- {
- Request req(123, 456, '+');
- string s;
- req.serialization(&s);//序列化
- cout << s << endl;
-
- s = Encode(s);//给s报文添加报头
- cout << s ;//这是网络里发的样子
-
- return 0;
- }
2.1.6.7 测试一下拿到报文的内容
- #include
- #include "TcpServer.hpp"
- #include "Protocol.hpp"
-
- using namespace std;
-
- int main()
- {
- Request req(123, 456, '+');
- string s;
- req.serialization(&s);//序列化
- //cout << s << endl;
-
- s = Encode(s);//给s报文添加报头
- cout << s ;//这是网络里发的样子
-
- //拿到报文的内容
- string content;
- bool r = Decode(s, &content);
- cout<< content <
-
- return 0;
- }

2.1.6.8 测试对收到报文进行反序列化 打散成对象

2.1.6.9 测试Response的要往网络里发 先添加报头

2.1.7.0 测试解码

2.1.7.1 测试Response反序列化

2.1.7.2 创建ServerCal.hpp处理整个报文
如何处理整个报文
把协议和网络功能组合在一起 然后再把这个头文件"ServerCal.hpp"交给ServerCal.cc
- #pragma once
- #include
- #include
- #include "Protocol.hpp"
- using namespace std;
-
- //如何处理整个报文
- //把协议和网络功能组合在一起 然后再把这个头文件"ServerCal.hpp"交给ServerCal.cc
-
- class ServerCal
- {
- public:
- ServerCal()
- {}
- Response CalculatorHelper(const Request &req)
- {
- Response resp(0, 0);
- switch(req.op)
- {
- case '+':
- resp.result = req.x + req.y;
- case '-':
- resp.result = req.x - req.y;
- case '*':
- resp.result = req.x * req.y;
- case '/':
- resp.result = req.x / req.y;
- }
- return resp;
- }
-
- //提供一个网络计算服务 把先把报文给我
- string Calculator(string &package)//肯定是收到一个序列化的请求报文
- {
- //收到一个请求报文 添加报头
- string content;
- bool r = Decode(package, &content);
- //报文不完整就直接返回
- if(!r) return;
-
- //完整的报文
- //请求的对象
- Request req;
- //反序列化
- r = req.deserialization(content);
- if(!r) return;
-
- //结果
- Response resp = CalculatorHelper(req);
- //返回结果
- //序列化
- string s;
- resp.serialization(&s);
- //序列化后,想把它当作网络报文发出去
- //添加报头
- s = Encode(s);
-
- return s;
- }
-
- ~ServerCal()
- {}
- };
2.1.7.3 完善一下TcpServer.hpp


2.1.7.4 测试

2.1.7.5 完善一下ClientCal.cc
tcp客户端要绑定,但不用显示的绑定
客户端端口号是随机的,但是唯一的
udp端口那里随机绑定是首次发送数据的时候,那么你的端口号就随之确定了
tcp这里是面向连接的,客户端连接成功了,我才想让你进行通信
没有bind 也就没有listen了
向服务器发起连接的接口 connect
客户端发起connect的时候,系统进行自动随机bind
- #include
- #include
- #include
- #include
- #include "Socket.hpp"
- #include "Protocol.hpp"
-
- using namespace std;
-
- void Usage(const string &proc)
- {
- cout << "\nUsage" << proc << " serverip serverport\n\n"
- << endl;
- }
-
- //./clientcal serverip serverport
- int main(int argc, char *argv[])
- {
- if (argc != 3)
- {
- Usage(argv[0]);
- exit(0);
- }
- string serverip = argv[1];
- uint16_t serverport = stoi(argv[2]);
-
- Sock sockfd;
- sockfd.Socket();
- bool r = sockfd.Connect(serverip, serverport);
- if (!r)
- return 1;
-
- srand(time(nullptr));
- const string s = "+-*/^!~";
- int cnt = 10;
-
- string inbuffer_stream;
- while (cnt--)
- {
- cout<< "第" <
"次测试..." < - int x = rand() % 100 + 1;
- usleep(1234);
- int y = rand() % 100 + 1;
- usleep(4321);
- char op = s[rand() % s.size()];
- Request req(x, y, op);
-
- req.DebugPrint();
-
- // 根据协议把请求发给服务器,Request是一个结构化的数据,发不了
- // 先序列化得到对应的字符串
- string package;
- req.serialization(&package);
-
- // 还是不能往网络里发送 添加报头还要
- //"len"\n"x op y"\n
- package = Encode(package);
- cout << "这是最新的发出去的请求:\n"
- << package;
- // 把请求往服务器端写
- write(sockfd.Fd(), package.c_str(), package.size());
- cout<
-
- // 接收服务器发回来的信息
- char buffer[128];
- //我们也没法保证我们能够读到一个完整的报文
- ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer));
- if (n > 0)
- {
- buffer[n] = 0;
- inbuffer_stream += buffer;// "len"\n"result code"\n
- string content;
- //解码
- bool r = Decode(inbuffer_stream, &content);//"result code"
- assert(r);
-
- //反序列化
- Response resp;
- r = resp.deserialization(content);
- assert(r);
- resp.DebugPrint();
-
- }
- sleep(1);
- }
-
- sockfd.Close();
-
- return 0;
- }

2.1.7.6 客户端一次发多个请求呢

解决方案

2.1.7 (jsoncpp)序列化和反序列化
2.1.7.1 安装json库
sudo apt install libjsoncpp-dev
这是头文件(只使用json.h)

2.1.7.2 简单的json的用法
- #include
- #include "jsoncpp/json/json.h"
-
- using namespace std;
-
- int main()
- {
- Json::Value root;
- root["x"] = 100;
- root["y"] = 100;
- root["op"] = '+';
- //描述
- root["dect"] = "this is a + oper";
-
- //比较快的序列化
- Json::FastWriter w;
- string res = w.write(root);
-
- cout<< res <
-
- return 0;
- }
编译时要带-ljsoncpp


2.1.7.2.1 序列化

2.1.7.2.2 反序列化

2.1.7.2.3 json套json

2.1.7.3 Protocol.hpp中的json序列化和反序列化






2.1.7.4 守护进程化

3 重谈OSI七层模型


4 代码总合
4.1 ClientCal.cc
- #include
- #include
- #include
- #include
- #include "Socket.hpp"
- #include "Protocol.hpp"
-
- using namespace std;
-
- void Usage(const string &proc)
- {
- cout << "\nUsage" << proc << " serverip serverport\n\n"
- << endl;
- }
-
- //./clientcal serverip serverport
- int main(int argc, char *argv[])
- {
- if (argc != 3)
- {
- Usage(argv[0]);
- exit(0);
- }
- string serverip = argv[1];
- uint16_t serverport = stoi(argv[2]);
-
- Sock sockfd;
- sockfd.Socket();
- bool r = sockfd.Connect(serverip, serverport);
- if (!r)
- return 1;
-
- srand(time(nullptr));
- const string s = "+-*/^!~";
- int cnt = 10;
-
- string inbuffer_stream;
- while (cnt--)
- {
- cout<< "第" <
"次测试..." < - int x = rand() % 100 + 1;
- usleep(1234);
- int y = rand() % 100 + 1;
- usleep(4321);
- char op = s[rand() % s.size()];
- Request req(x, y, op);
-
- req.DebugPrint();
-
- // 根据协议把请求发给服务器,Request是一个结构化的数据,发不了
- // 先序列化得到对应的字符串
- string package;
- req.serialization(&package);
-
- // 还是不能往网络里发送 添加报头还要
- //"len"\n"x op y"\n
- package = Encode(package);
- cout << "这是最新的发出去的请求:\n"
- << package;
- // 把请求往服务器端写
- write(sockfd.Fd(), package.c_str(), package.size());
- // write(sockfd.Fd(), package.c_str(), package.size());
- // write(sockfd.Fd(), package.c_str(), package.size());
- // write(sockfd.Fd(), package.c_str(), package.size());
- // write(sockfd.Fd(), package.c_str(), package.size());
- // write(sockfd.Fd(), package.c_str(), package.size());
- cout<
-
- // 接收服务器发回来的信息
- char buffer[128];
- //我们也没法保证我们能够读到一个完整的报文
- ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer));
- if (n > 0)
- {
- buffer[n] = 0;
- inbuffer_stream += buffer;// "len"\n"result code"\n
- cout<< inbuffer_stream <
- string content;
- //解码
- bool r = Decode(inbuffer_stream, &content);//"result code"
- assert(r);
-
- //反序列化
- Response resp;
- r = resp.deserialization(content);
- assert(r);
- resp.DebugPrint();
-
- }
- sleep(1);
- }
-
- sockfd.Close();
-
- return 0;
- }
4.2 makefile
- .PHONY:all
- all:servercal clientcal
- servercal:ServerCal.cc
- g++ -g -o $@ $^ -lpthread -std=c++11 -ljsoncpp
- clientcal:ClientCal.cc
- g++ -o $@ $^ -lpthread -std=c++11 -ljsoncpp
- .PHONT:clean
- clean:
- rm -f clientcal servercal
4.3 Protocol.hpp
- #pragma once
-
- #include
- #include
- #include "jsoncpp/json/json.h"
- using namespace std;
-
- const string blank_space_sep = " ";//空格分隔符
- const string protocol_sep = "\n";//报文分隔符
-
-
- //序列化和反序列化解决的是对应的是一个报文内部的问题
- //还要解决一下报文与报文之间的问题
-
- //添加报头 "len"\n"x op y"\n
- string Encode(string &content)
- {
- //封装报头 这里规定的协议就是长度+有效载荷
- string package = to_string(content.size());
- package += protocol_sep;
- package += content;
- package += protocol_sep;
-
- return package;
- }
-
- //将"x op y"提取出来 有效载荷
- //"len"\n"x op y"\n package整个报文 *content把结果带出去
- bool Decode(string &package, string *content)
- {
- size_t pos = package.find(protocol_sep);
- if(pos == string::npos) return false;
- string len_str = package.substr(0, pos);
- size_t len = stoi(len_str); //拿到了len长度
-
- //判断一下package的长度够不够
- //package = len_str + content_str + 2
- size_t total_len = len_str.size() + len + 2;
- if(package.size() < total_len) return false;
- //大于没事,因为肯定有完整的报文
-
- //*content把结果带出去 "x op y"
- *content = package.substr(pos + 1, len);
-
- // earse 移除报文
- package.erase(0, total_len);
- return true;
- }
-
- class Request
- {
- public:
- Request(int data1, int data2, char oper): x(data1), y(data2), op(oper)
- {}
- Request()
- {}
- public:
- bool serialization(string *out)
- {
- // //构建报文的有效载荷
- // //序列化的就是把结构化的(struct)转成string,
- // // "x op y"
- // //数字之间是不可能出现分隔符的
- // string s = to_string(x);
- // s += blank_space_sep;
- // s += op;
- // s += blank_space_sep;
- // s += to_string(y);
-
- // *out = s;
- // return true;
-
- //Json 序列化
- Json::Value root;
- root["x"] = x;
- root["y"] = y;
- root["op"] = op;
- Json::FastWriter w;
- *out = w.write(root);
- return true;
- }
-
- //"x op y" 进行分割
- bool deserialization(const string &in)
- {
- // size_t left = in.find(blank_space_sep);
- // if(left == string::npos) return false;//没找到
- // string part_x = in.substr(0, left);
-
- // size_t right = in.rfind(blank_space_sep);//逆着找
- // if(right == string::npos) return false;//没找到
- // string part_y = in.substr(right + 1);
-
- // if(left + 2 != right) return false;
- // op = in[right -1];
-
- // x = stoi(part_x);
- // y = stoi(part_y);
- // return true;
-
- //Json 反序列化
- Json::Value root;
- Json::Reader r;
- r.parse(in, root);
- //反序列化到root
- //提取出来
- x = root["x"].asInt();
- y = root["y"].asInt();
- op = root["op"].asInt();
- return true;
- }
- void DebugPrint()
- {
- cout<< "新请求构建完成" << x << op << y << "=?" <
- }
-
- public:
- int x;
- int y;
- char op;// + - * / %
- };
-
- class Response
- {
- public:
- Response(int res, int c):result(res), code(c)
- {}
- Response()
- {}
- public:
- //序列化是结构体转字符串的
- bool serialization(string *out)
- {
- // // 序列化成这样"result code"
- // //构建报文的有效载荷
- // string s = to_string(result);
- // s += blank_space_sep;
- // s += to_string(code);
-
- // *out = s;
- // return true;
-
- //Json 序列化
- Json::Value root;
- root["result"] = result;
- root["code"] = code;
- Json::FastWriter w;
- *out = w.write(root);
- return true;
- }
-
- //反序列化 一定是你要传过来一个字符串
- bool deserialization(const string &in) //"result code" 协议定制好了就必须是这个样子
- {
- // size_t pos = in.find(blank_space_sep);
- // if(pos == string::npos) return false;//没找到
- // string part_left = in.substr(0, pos);
- // string part_right = in.substr(pos + 1);
- // result = stoi(part_left);
- // code = stoi(part_right);
- // return true;
-
- //Json 反序列化
- Json::Value root;
- Json::Reader r;
- r.parse(in, root);
- //反序列化到root
- //提取出来
- result = root["result"].asInt();
- code = root["code"].asInt();
- return true;
- }
-
- void DebugPrint()
- {
- cout<< "响应结果完成" << "result:"<< result <<"code:" <
- }
- public:
- int result;
- int code;// 0,可信,否则!0具体是几,表明对应的错误原因
- };
4.4 ServerCal.cc
- #include
- #include
- #include "TcpServer.hpp"
- #include "Protocol.hpp"
- #include "ServerCal.hpp"
-
- using namespace std;
-
- void Usage(const string& proc)
- {
- cout<<"\nUsage" << proc << "port\n\n" <
- }
-
- int main(int argc, char* argv[])
- {
- if(argc != 2)
- {
- Usage(argv[0]);
- exit(0);
- }
- uint16_t port = stoi(argv[1]);
-
- ServerCal cal;
- TcpServer *tsvp = new TcpServer(port, bind(&ServerCal::Calculator, &cal, placeholders::_1));
- tsvp->InitServer();
- daemon(0, 0);
- tsvp->Start();
-
-
-
-
- // Response resp(1000, 0);
- // //发回去
- // string content;
- // resp.serialization(&content);
- // //cout << content << endl;
- // //要往网络里发 先添加报头
- // string package = Encode(content);
- // cout << package << endl;
-
- // //解码 放进s
- // string s;
- // bool r = Decode(package, &s);
- // cout<< s <
-
- // //反序列化 打散成对象
- // Response temp;
- // temp.deserialization(s);
- // cout<<"\n\n";
- // cout<< temp.result <
- // cout<< temp.code << endl;
-
-
- // Request req(123, 456, '+');
- // string s;
- // req.serialization(&s);//序列化
- // //cout << s << endl;
-
- // s = Encode(s);//给s报文添加报头
- // cout << s ;//这是网络里发的样子
-
- // //拿到报文的内容
- // string content;
- // bool r = Decode(s, &content);
- // cout<< content <
-
- // //对收到报文进行反序列化 打散成对象
- // Request temp;
- // temp.deserialization(content);
- // cout<< temp.x <
-
- return 0;
- }
4.5 ServerCal.hpp
- #pragma once
- #include
- #include
- #include "Protocol.hpp"
- using namespace std;
-
- //如何处理整个报文
- //把协议和网络功能组合在一起 然后再把这个头文件"ServerCal.hpp"交给ServerCal.cc
-
- class ServerCal
- {
- public:
- ServerCal()
- {}
- Response CalculatorHelper(const Request &req)
- {
- Response resp(0, 0);
- switch(req.op)
- {
- case '+':
- resp.result = req.x + req.y;
- break;
- case '-':
- resp.result = req.x - req.y;
- break;
- case '*':
- resp.result = req.x * req.y;
- break;
- case '/':
- if(req.y == 0) resp.code = 1;
- else
- {
- resp.result = req.x / req.y;
- }
- break;
- default:
- resp.code = 2;
- break;
- }
- return resp;
- }
-
- //提供一个网络计算服务 把先把报文给我
- // package: "len"\n"10 + 20"\n
- string Calculator(string &package)//肯定是收到一个序列化的请求报文
- {
- //收到一个请求报文 添加报头
- string content;
- bool r = Decode(package, &content);
- //报文不完整就直接返回
- if(!r) return "";
-
- //完整的报文
- //请求的对象
- Request req;
- //反序列化
- r = req.deserialization(content);
- if(!r) return "";
-
- //结果
- Response resp = CalculatorHelper(req);
- //返回结果
- //序列化
- string s;
- resp.serialization(&s);
- //序列化后,想把它当作网络报文发出去
- //添加报头
- s = Encode(s);
-
- return s;
- }
-
- ~ServerCal()
- {}
- };
4.6 Socket.hpp
- #pragma once
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include "./logs/ljwlog.h"
-
- using namespace std;
-
- enum
- {
- SocketErr = 2,
- BindErr,
- ListenErr
- };
- const int backlog = 10;
- class Sock
- {
- public:
- Sock()
- {
- }
- ~Sock()
- {
- }
-
- public:
- void Socket() // 创建套接字的接口
- {
- sockfd_ = socket(AF_INET, SOCK_STREAM, 0); // 流式套接字 第二个参数是协议类型
- if (sockfd_ < 0)
- {
- FATAL("Socket errno,error:%d,errstring:%s", errno, strerror(errno));
- exit(SocketErr);
- }
- }
- void Bind(uint16_t port) // 绑定的接口
- {
- 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; // ip默认0.0.0.0
- if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0)
- {
- FATAL("Bind errno,error:%d,errstring:%s", errno, strerror(errno));
- exit(BindErr);
- }
- }
- void Listen() // 监听状态的接口
- {
- if (listen(sockfd_, backlog) < 0)
- {
- FATAL("Listen errno,error:%d,errstring:%s", errno, strerror(errno));
- exit(ListenErr);
- }
- }
- // 知道谁链接的我
- int Accept(string *clientip, uint16_t *clientport) // 获取连接的接口
- {
- struct sockaddr_in peer; // 远端的意思
- socklen_t len = sizeof(peer);
- int newfd = accept(sockfd_, (struct sockaddr *)&peer, &len);
- if (newfd < 0)
- {
- WARN("accept error, %s: %d", strerror(errno), errno);
- return -1;
- }
- // 网络转主机
- // 拿出客户端的ip和端口号
- char ipstr[64];
- inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr)); // 网络转主机
- *clientip = ipstr; // 网络转主机
- *clientport = ntohs(peer.sin_port); // 网络转主机
- return newfd;
- }
- void Close()
- {
- close(sockfd_);
- }
- int Connect(const string &ip, const uint16_t &port) // 方便两个客户端和服务器都能使用这个Sock的这个公共方法
- {
- struct sockaddr_in peer;
- memset(&peer, 0, sizeof(peer));
- peer.sin_family = AF_INET;
- peer.sin_port = htons(port);
- inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));
- int n = connect(sockfd_, (struct sockaddr *)&peer, sizeof(peer));
- if (n == -1)
- {
- cerr << "connect to" << ip << "::" << port <<"error"<< endl;
- return false;
- }
-
- return true;
- }
- int Fd()
- {
- return sockfd_;
- }
-
- private:
- int sockfd_;
- };
4.7 TcpServer.hpp
- #pragma once
- #include
- #include
- #include
- #include "Socket.hpp"
- #include "./logs/ljwlog.h"
-
- // 返回值 参数
- using fun_t = function< string(string &package)>;
-
- class TcpServer
- {
- public:
- TcpServer(uint16_t port, fun_t callback):port_(port), callback_(callback)
- {}
- bool InitServer( )
- {
- //先创建套接字,再绑定,设置监听状态
- listensock_.Socket();
- listensock_.Bind(port_);
- listensock_.Listen();
- INFO("init server");
- return true;
- }
- void Start()
- {
- signal(SIGCHLD, SIG_IGN);//忽略就不用等待了
- signal(SIGPIPE, SIG_IGN);//忽略就不用等待了
- while(true)
- {
- string clientip;
- uint16_t clientport;
- int sockfd = listensock_.Accept(&clientip, &clientport);
- if(sockfd < 0) continue;
- INFO("Accept server sockfd:%d clientip:%s clientport:%d", sockfd, clientip.c_str(), clientport);
- //提供服务
- if(fork() == 0)
- {
- //子进程
- listensock_.Close();
-
- //请求的信息流
- string inbuffer_stream;
-
- //数据计算
- while(true)
- {
- //从网络当中读数据到buffer当中
- char buffer[1024];
- ssize_t n = read(sockfd, buffer, sizeof(buffer));
- if(n > 0)
- {
- buffer[n] = 0;
- inbuffer_stream += buffer;
-
- DEBUG("inbuffer_stream: %s", inbuffer_stream.c_str());
- while(true)
- {
- string info = callback_(inbuffer_stream);
- //如果没有读到完整的报文,就会返回一个空串infp
- if(info.empty()) break;
-
- write(sockfd, info.c_str(), info.size());
- }
-
- }
- else if(n == 0) break;
- else break;
- }
-
- exit(0);
- }
- close(sockfd);//不用等了直接关就好了
- }
- }
-
- ~TcpServer()
- {}
- private:
- uint16_t port_;
- Sock listensock_; //叫listen套接字,Listen完后有真正的网络文件描述符
-
- fun_t callback_;
- };
-
-
注:本文转载自blog.csdn.net的Ljw...的文章"https://blog.csdn.net/2401_83427936/article/details/144003486"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
评论记录:
回复评论: