1 序列化和反序列化
write和read实质是拷贝函数
1.1序列化和反序列化的概述:

2网络版计算器
2.1代码实现

先把日志拷贝过来
2.1.1必须先要有网络功能
先把 TcpServer.hpp编写号
cpp
#pragma once
#include <cstdint>
#include "Socket.hpp"
#include "./logs/ljwlog.h"
class TcpServer
{
public:
TcpServer()
{}
bool InitServer()
{}
void Start()
{}
~TcpServer()
{}
private:
uint16_t port;
};

2.1.2 把套接字接口封装一下方便使用
cpp
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
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接口的补充

cpp
#pragma once
#include <cstdint>
#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接口的补充
cpp
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <cstring>
#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(响应)
约定好了,最好就不要动他了

cpp
#pragma once
#include<iostream>
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代码
cpp
#pragma once
#include<iostream>
#include<string>
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 测试一下序列化和添加报头
cpp
#include<iostream>
#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 测试一下拿到报文的内容
cpp
#include<iostream>
#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 <<endl;
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
cpp
#pragma once
#include<string>
#include<string>
#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
cpp
#include <iostream>
#include <ctime>
#include <unistd.h>
#include <assert.h>
#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<< "第" <<cnt <<"次测试..." <<endl;
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<<flush;
// 接收服务器发回来的信息
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库
bash
sudo apt install libjsoncpp-dev
这是头文件(只使用json.h)

2.1.7.2 简单的json的用法
cpp
#include<iostream>
#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 <<endl;
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
cpp
#include <iostream>
#include <ctime>
#include <unistd.h>
#include <assert.h>
#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<< "第" <<cnt <<"次测试..." <<endl;
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<<flush;
// 接收服务器发回来的信息
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 <<endl;
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
cpp
.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
cpp
#pragma once
#include<iostream>
#include<string>
#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 << "=?" <<endl<<endl;
}
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:" <<code <<endl <<endl;
}
public:
int result;
int code;// 0,可信,否则!0具体是几,表明对应的错误原因
};
4.4 ServerCal.cc
cpp
#include<iostream>
#include<unistd.h>
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "ServerCal.hpp"
using namespace std;
void Usage(const string& proc)
{
cout<<"\nUsage" << proc << "port\n\n" <<endl;
}
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 <<endl;
// //反序列化 打散成对象
// Response temp;
// temp.deserialization(s);
// cout<<"\n\n";
// cout<< temp.result <<endl;
// 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 <<endl;
// //对收到报文进行反序列化 打散成对象
// Request temp;
// temp.deserialization(content);
// cout<< temp.x <<endl << temp.op <<endl << temp.y <<endl;
return 0;
}
4.5 ServerCal.hpp
cpp
#pragma once
#include<string>
#include<string>
#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
cpp
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <cstring>
#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
cpp
#pragma once
#include <cstdint>
#include <signal.h>
#include <functional>
#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_;
};