本章作用主要为承上启下,一方面训练tcp使用的流程接口,另一方面为下一章介绍http做铺垫
模拟计算器无序列化版
我们要自己定义一个协议,client && server都要遵守!
业务逻辑存在Protocol.hpp中,即请求消息的结构类型request_t,响应消息的结构类型response_t。
server_cal.cpp
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <pthread.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;
void *HandlerRequest(void *argc)
{
int sock = *(int *)argc;
delete (int *)argc;
pthread_detach(pthread_self());
// version1 原生方法,没有明显的序列化和反序列化的过程
// 业务逻辑, 做一个短服务
// request -> 分析处理 -> 构建response -> sent(response)->close(sock)
// 1. 读取请求
request_t req;
ssize_t s = read(sock, &req, sizeof(req));
if (s == sizeof(req))
{
//读取到了完整的请求,待定
// req.x , req.y, req.op
// 2. 分析请求 && 3. 计算结果
// 4. 构建响应,并进行返回
response_t resp = {0, 44};
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;//代表除0
else
resp.result = req.x / req.y;
break;
case '%':
if (req.y == 0)
resp.code = -2;//代表%0
else
resp.result = req.x % req.y;
break;
default:
resp.code = -3;//请求异常
break;
}
cout << "request: " << req.x << req.op << req.y << endl;
write(sock, &resp, sizeof(resp));
cout << "服务结束" << endl;
}
// 5.关闭连接
close(sock);
}
void Usage()
{
cout << "格式有误,应当: ./server_cal + port " << endl;
}
int main(int argv, char *argc[])
{
if (argv != 2)
{
Usage();
return 1;
}
uint16_t port = atoi(argc[1]);
int listen_sock = Sock::Socket();
Sock::Bind(listen_sock, port);
Sock::Listen(listen_sock);
for (;;)
{
int newsock = Sock::Accept(listen_sock);
//连接成功开始服务
if (newsock >= 0)
{
cout << "与客户连接成功" << endl;
pthread_t tid;
int *parm = new int(newsock);
pthread_create(&tid, nullptr, HandlerRequest, (void *)parm);
}
}
return 0;
}
client_cal.cpp
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;
void Usage()
{
cout << "格式有误,应当: ./client_cal + ip + port " << endl;
}
int main(int argv, char *argc[])
{
if (argv != 3)
{
Usage();
return 1;
}
int sock = Sock::Socket();
Sock::Connect(sock, argc[1], atoi(argc[2]));
// 业务逻辑
request_t req;
memset(&req, 0, sizeof(req));
cout << "Please Enter Data One# ";
cin >> req.x;
cout << "Please Enter Data Two# ";
cin >> req.y;
cout << "Please Enter operator# ";
cin >> req.op;
write(sock, &req, sizeof(req));
response_t resp;
ssize_t s = read(sock, &resp, sizeof(resp));
if (s == sizeof(resp))
{
cout << "code[0:success]: " << resp.code << endl;
cout << "result: " << resp.result << std::endl;
}
else
{
cout << "read faile" << endl;
}
return 0;
}
Sock.hpp
#pragma once
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
using namespace std;
class Sock
{
public:
static int Socket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
cerr << "sock create fail" << errno << endl;
exit(2);
}
return sock;
}
static void Bind(int sock, 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;
if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
cerr << "bind erro" << errno << endl;
exit(3);
}
}
static void Listen(int sock)
{
const int back_log = 5;
if (listen(sock, back_log) < 0)
{
cerr << "listen erro" << errno << endl;
exit(4);
}
}
static int Accept(int sock)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(sock, (struct sockaddr *)&peer, &len);
if(fd >= 0)
return fd;
return -1;
}
static void Connect(int sock, std::string ip, uint16_t port)
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip.c_str());
if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0)
{
cout << "Connect Success!" << endl;
}
else
{
cout << "Connect Failed!" << endl;
exit(5);
}
}
};
Protocol.hpp
#pragma once
#include <iostream>
#include <string>
using namespace std;
// 定制协议的过程,目前就是定制结构化数据的过程
// 请求格式
// 我们自己定义的协议,client && server 都必须遵守! 这就叫做自定义协议
typedef struct Request
{
int x;
int y;
char op;
}request_t;
typedef struct Response
{
int code;
int result;
}response_t;
Makefile
.PHONY:all
all:server_cal client_cal
server_cal:server_cal.cpp
g++ -o $@ $^ -std=c++11 -lpthread
client_cal:client_cal.cpp
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -rf client_cal server_cal
上述没有序列化的版本,只是通过原生结构体的形式发送二进制的方式进行通信的,看似可行,只不过是少数情况而已。
在现实情况中如果软件版本迭代了,比如内存对齐规则改变等,因此如果传过来的2进制数据一个字节发生了变化就造成无法通信。
所以这种通信的方式是不科学的,因此就需要序列化与反序列化!!
序列化与反序列化组件的使用(了解即可)
两种序列化格式
反序列化
模拟计算器序列化与反序列化版
server_cal.cpp
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <pthread.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;
void *HandlerRequest(void *argc)
{
int sock = *(int *)argc;
delete (int *)argc;
pthread_detach(pthread_self());
// version1 原生方法,没有明显的序列化和反序列化的过程
// 业务逻辑, 做一个短服务
// request -> 分析处理 -> 构建response -> sent(response)->close(sock)
// 1. 读取请求 + 反序列化
char buffer[1024];
memset(&buffer, 0, sizeof(buffer));
ssize_t s = read(sock, &buffer, sizeof(buffer));
request_t req;
if (s > 0)
{
buffer[s] = 0;
cout << "获取到新的请求 " << buffer << endl;
DeserializationRequest(buffer, req);
}
//读取到了完整的请求,待定
// req.x , req.y, req.op
// 2. 分析请求 && 3. 计算结果
// 4. 构建序列化响应,并进行返回
response_t resp = {0, 44};
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; //代表除0
else
resp.result = req.x / req.y;
break;
case '%':
if (req.y == 0)
resp.code = -2; //代表%0
else
resp.result = req.x % req.y;
break;
default:
resp.code = -3; //请求异常
break;
}
cout << "request: " << req.x << req.op << req.y << endl;
string send_string = SerializeResponse(resp);
write(sock, send_string.c_str(), send_string.size());
cout << "服务结束 " << send_string << endl;
// 5.关闭连接
close(sock);
}
void Usage()
{
cout << "格式有误,应当: ./server_cal + port " << endl;
}
int main(int argv, char *argc[])
{
if (argv != 2)
{
Usage();
return 1;
}
uint16_t port = atoi(argc[1]);
int listen_sock = Sock::Socket();
Sock::Bind(listen_sock, port);
Sock::Listen(listen_sock);
for (;;)
{
int newsock = Sock::Accept(listen_sock);
//连接成功开始服务
if (newsock >= 0)
{
cout << "与客户连接成功" << endl;
pthread_t tid;
int *parm = new int(newsock);
pthread_create(&tid, nullptr, HandlerRequest, (void *)parm);
}
}
return 0;
}
client_cal.cpp
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace std;
void Usage()
{
cout << "格式有误,应当: ./client_cal + ip + port " << endl;
}
int main(int argv, char *argc[])
{
if (argv != 3)
{
Usage();
return 1;
}
int sock = Sock::Socket();
Sock::Connect(sock, argc[1], atoi(argc[2]));
// 业务逻辑
request_t req;
memset(&req, 0, sizeof(req));
cout << "Please Enter Data One# ";
cin >> req.x;
cout << "Please Enter Data Two# ";
cin >> req.y;
cout << "Please Enter operator# ";
cin >> req.op;
string send_string = SerializeRequest(req);
write(sock, send_string.c_str(), send_string.size());
char buffer[1024];
memset(&buffer, 0, sizeof(buffer));
ssize_t s = read(sock, &buffer, sizeof(buffer));
if (s > 0)
{
buffer[s] = 0;
string str = buffer;
response_t resp;
DeserializationResponse(str, resp);
cout << "code[0:success]: " << resp.code << endl;
cout << "result: " << resp.result << std::endl;
}
else
{
cout << "read faile" << endl;
}
return 0;
}
Sock.hpp
#pragma once
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
using namespace std;
class Sock
{
public:
static int Socket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
cerr << "sock create fail" << errno << endl;
exit(2);
}
return sock;
}
static void Bind(int sock, 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;
if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
cerr << "bind erro" << errno << endl;
exit(3);
}
}
static void Listen(int sock)
{
const int back_log = 5;
if (listen(sock, back_log) < 0)
{
cerr << "listen erro" << errno << endl;
exit(4);
}
}
static int Accept(int sock)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(sock, (struct sockaddr *)&peer, &len);
if(fd >= 0)
return fd;
return -1;
}
static void Connect(int sock, std::string ip, uint16_t port)
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip.c_str());
if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0)
{
cout << "Connect Success!" << endl;
}
else
{
cout << "Connect Failed!" << endl;
exit(5);
}
}
};
Protocol.hpp
#pragma once
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
using namespace std;
// 定制协议的过程,目前就是定制结构化数据的过程
// 请求格式
// 我们自己定义的协议,client && server 都必须遵守! 这就叫做自定义协议
typedef struct Request
{
int x;
int y;
char op;
} request_t;
typedef struct Response
{
int code;
int result;
} response_t;
//序列化请求 : request_t --> string
string SerializeRequest(const request_t &req)
{
Json::Value root; //可以承装任何对象, json是一种kv式的序列化方案
root["datax"] = req.x;
root["datay"] = req.y;
root["op"] = req.op;
// Json::StyledWriter writer;
Json::FastWriter writer;
// write的返回值是经过序列化后的string类型的字符串
string res = writer.write(root);
return res;
}
//反序列化请求 :string --> request_t
void DeserializationRequest(const string &json_string, request_t &out)
{
Json::Reader read;
Json::Value root;
read.parse(json_string, root);
out.x = root["datax"].asInt();
out.y = root["datay"].asInt();
out.op = (char)root["op"].asInt();
}
//序列化响应 : response_t --> string
string SerializeResponse(const response_t &resp)
{
Json::Value root;
root["code"] = resp.code;
root["result"] = resp.result;
Json::FastWriter writer;
string res = writer.write(root);
return res;
}
//反序列化响应 :string --> response_t
void DeserializationResponse(const string &json_string, response_t &out)
{
Json::Reader read;
Json::Value root;
read.parse(json_string, root);
out.code = root["code"].asInt();
out.result = root["result"].asInt();
}
接下来请看第五部分