目录
实验目标:
源代码:
实验结果:
Linux网络编程✨
实验目标:
制作一个应用层的简易版的计算器(加、减、乘、除、取余);
源代码:
- makefile
.PHONY:all
all:CalClient CalServer
CalClient:CalClient.cc
g++ -o $@ $^ -std=c++11 -ljsoncpp
CalServer:CalServer.cc
g++ -o $@ $^ -std=c++11 -lpthread -ljsoncpp
.PHONY:clean
clean:
rm CalClient CalServer -rf
- Protocol.hpp
#pragma once
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
using namespace std;
// 定制协议的过程,目前就是定制结构化数据的过程
// 请求格式
// 我们自己定义的协议,client && server 都必须遵守! 这就叫做自定义协议
typedef struct request
{
int x; //10
int y; //0
char op; // '/' "+-*/%"
} request_t; //10/0
// 响应格式
typedef struct response
{
int code; // server运算完毕的计算状态: code(0:success), code(-1: div 0) ...
int result; // 计算结果, 能否区分是正常的计算结果,还是异常的退出结果
}response_t;
//request_t -> string
std::string SerializeRequest(const request_t &req)
{
// 序列化的过程
Json::Value root; //可以承装任何对象, json是一种kv式的序列化方案
root["datax"] = req.x;
root["datay"] = req.y;
root["operator"] = req.op;
//FastWriter, StyledWriter
// Json::StyledWriter writer;
Json::FastWriter writer;
std::string json_string = writer.write(root);
return json_string;
}
// string -> request_t
void DeserializeRequest(const std::string &json_string, request_t &out)
{
//反序列化
Json::Reader reader;
Json::Value root;
reader.parse(json_string, root);
out.x = root["datax"].asInt();
out.y = root["datay"].asInt();
out.op = (char)root["operator"].asInt();
}
std::string SerializeResponse(const response_t &resp)
{
Json::Value root;
root["code"] = resp.code;
root["result"] = resp.result;
Json::FastWriter writer;
std::string res = writer.write(root);
return res;
}
void DeserializeResponse(const std::string &json_string, response_t &out)
{
//反序列化
Json::Reader reader;
Json::Value root;
reader.parse(json_string, root);
out.code = root["code"].asInt();
out.result = root["result"].asInt();
}
- Sock.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
class Sock
{
public:
static int Socket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
cerr << "socket error" << 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 error!" << endl;
exit(3);
}
}
static void Listen(int sock)
{
if (listen(sock, 5) < 0)
{
cerr << "listen error !" << 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);
}
}
};
- CalServer.cc
#include <pthread.h>
#include "Protocol.hpp"
#include "Sock.hpp"
static void Usage(string proc)
{
cout << "Usage: " << proc << " port" << endl;
exit(1);
}
void *HandlerRequest(void *args)
{
int sock = *(int *)args;
delete (int *)args;
pthread_detach(pthread_self());
// version1 原生方法,没有明显的序列化和反序列化的过程
// 业务逻辑, 做一个短服务
// request -> 分析处理 -> 构建response -> sent(response)->close(sock)
// 1. 读取请求
char buffer[1024];
request_t req;
ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
if (s > 0)
{
buffer[s] = 0;
cout << "get a new request: " << buffer << endl;
std::string str = buffer;
DeserializeRequest(str, req); //反序列化请求
// 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, 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; //代表除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));
std::string send_string = SerializeResponse(resp); //序列化之后的字符串
write(sock, send_string.c_str(),send_string.size());
cout << "服务结束: " << send_string << endl;
// }
}
// 5. 关闭链接
close(sock);
}
// ./CalServer port
int main(int argc, char *argv[])
{
if (argc != 2)
Usage(argv[0]);
uint16_t port = atoi(argv[1]);
int listen_sock = Sock::Socket();
Sock::Bind(listen_sock, port);
Sock::Listen(listen_sock);
for (;;)
{
int sock = Sock::Accept(listen_sock);
if (sock >= 0)
{
cout << "get a new client..." << endl;
int *pram = new int(sock);
pthread_t tid;
pthread_create(&tid, nullptr, HandlerRequest, pram);
}
}
return 0;
}
- CalClient.cc
#include "Protocol.hpp"
#include "Sock.hpp"
void Usage(string proc)
{
cout << "Usage: " << proc << " server_ip server_port" << endl;
}
// ./CalClient server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
int sock = Sock::Socket();
Sock::Connect(sock, argv[1], atoi(argv[2]));
// while (true)
// {
// 业务逻辑
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;
std::string json_string = SerializeRequest(req);
// ssize_t s = write(sock, &req, sizeof(req));
ssize_t s = write(sock, json_string.c_str(), json_string.size());
char buffer[1024];
s = read(sock, buffer, sizeof(buffer) - 1);
if (s > 0)
{
response_t resp;
buffer[s] = 0;
std::string str = buffer;
DeserializeResponse(str, resp);
cout << "code[0:success]: " << resp.code << endl;
cout << "result: " << resp.result << std::endl;
}
return 0;
}
实验结果:
这个CS模式的在线版本计算器,本质上就是一个应用层(TCP/IP四层协议)网络服务;
对应OIS七层协议的会话层、表示层、应用层都是我们自己写的;
基本通信代码(套接字)——> 会话层;
序列和反序列化(使用 json 组件完成)——> 表示层;
业务逻辑(请求、结果格式等约定是我们自己做的)——> 应用层;
如果上述文章对您有所帮助的话,还请点赞👍,收藏😉,关注🎈