自定义协议(应用层协议)——网络版计算机基于TCP传输协议

应用层:自定义网络协议:序列化和反序列化,如果是TCP传输的:还要关心区分报文边界(在序列化设计的时候设计好)——粘包问题
在这里插入图片描述

1、首先想要使用TCP协议传输的网络,服务器和客户端都应该要创建自己的套接字,因为两个都要创建,所以我们把套接字封装为一个类:
封装方法:设计模式:模版方法:先写一个模版类(基类),里面有各种函数,然后再写一个派生类里面有各种方法的实现,创建对象的时候

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define Convert(addrptr) ((struct sockaddr *)addrptr)namespace Net_Work
{const static int defaultsockfd = -1;const int backlog = 5;enum{SocketError = 1,BindError,ListenError,};// 封装一个基类,Socket接口类// 设计模式:模版方法类class Socket{public:virtual ~Socket() {}virtual void CreateSocketOrDie() = 0;virtual void BindSocketOrDie(uint16_t port) = 0;virtual void ListenSocketOrDie(int backlog) = 0;virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;virtual bool ConnectServer(std::string &serverip, uint16_t serverport) = 0;virtual int GetSockFd() = 0;virtual void SetSockFd(int sockfd) = 0;virtual void CloseSocket() = 0;virtual bool Recv(std::string *buffer, int size) = 0;// TODOpublic:// 创建服务器端的套接字,并设置为监听状态监听套接字void BuildListenSocketMethod(uint16_t port, int backlog){CreateSocketOrDie();BindSocketOrDie(port);ListenSocketOrDie(backlog);}// 创建客户端的套接字,并且申请链接bool BuildConnectSocketMethod(std::string &serverip, uint16_t serverport){CreateSocketOrDie();return ConnectServer(serverip, serverport);}void BuildNormalSocketMethod(int sockfd){SetSockFd(sockfd);}};class TcpSocket : public Socket{public:TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd){}~TcpSocket(){}void CreateSocketOrDie() override // 创建套接字{_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0)exit(SocketError);}void BindSocketOrDie(uint16_t port) override // 绑定套接字{struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(port);int n = ::bind(_sockfd, Convert(&local), sizeof(local));if (n < 0)exit(BindError);}void ListenSocketOrDie(int backlog) override // 设置套接字为监听状态{int n = ::listen(_sockfd, backlog);if (n < 0)exit(ListenError);}// 获取链接套接字——服务器Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) override{struct sockaddr_in peer;socklen_t len = sizeof(peer);int newsockfd = ::accept(_sockfd, Convert(&peer), &len);if (newsockfd < 0)return nullptr;*peerport = ntohs(peer.sin_port);*peerip = inet_ntoa(peer.sin_addr);Socket *s = new TcpSocket(newsockfd);return s;}// 申请链接——客户端bool ConnectServer(std::string &serverip, uint16_t serverport) override{struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_port = htons(serverport);int n = ::connect(_sockfd, Convert(&server), sizeof(server));if (n == 0)return true;elsereturn false;}int GetSockFd() override{return _sockfd;}void SetSockFd(int sockfd) override{_sockfd = sockfd;}void CloseSocket() override{if (_sockfd > defaultsockfd)::close(_sockfd);}bool Recv(std::string *buffer, int size)//接收消息到buffer中{char inbuffer[size];ssize_t n = recv(_sockfd, inbuffer, size - 1, 0);if (n > 0){inbuffer[n] = 0;*buffer += inbuffer;return true;}else if (n == 0 || n < 0)return false;}private:int _sockfd;};}

创建服务器:
1、创建套接字
2、把套接字设置为监听状态
3、获取连接,产生新的套接字
4、把新的套接字作为新线程的参数传到新线程执行的代码中,实现收发消息的操作
在这里插入图片描述
代码:

#pragma once#include"Socket.hpp"
#include<pthread.h>
#include<functional>using func_t=std::function<void(Net_Work::Socket* sockp)>;//服务器类
class TcpServer;class ThreadData
{public:ThreadData(TcpServer*tcp_this, Net_Work::Socket *sockp): _this(tcp_this), _sockp(sockp){}public:TcpServer *_this;Net_Work::Socket *_sockp;
};class TcpServer
{public:TcpServer(uint16_t port, func_t handler_request):_port(port),_listensocket(new Net_Work::TcpSocket()),_handler_request(handler_request){_listensocket->BuildListenSocketMethod(_port,Net_Work::backlog);}static void *ThreadRun(void *args){pthread_detach(pthread_self());//分离线程ThreadData *td=static_cast<ThreadData*>(args);td->_this->_handler_request(td->_sockp);td->_sockp->CloseSocket();delete td->_sockp;delete td;return nullptr;}void Loop(){while(true){//获取连接std::string peerip;uint16_t peerport;Net_Work::Socket* newsock=_listensocket->AcceptConnection(&peerip,&peerport);if(newsock==nullptr) continue;;std::cout<<"获取了一个新链接,sockfd: "<<newsock->GetSockFd()<<"client info:"<< peerip<<":"<<peerport<<std::endl;//创建线程去完成此次sockfd收发pthread_t tid;ThreadData *td=new ThreadData(this,newsock);pthread_create(&tid,nullptr,ThreadRun,td);}}~TcpServer(){delete _listensocket;}private:int _port;Net_Work::Socket *_listensocket;
public:func_t _handler_request;};

客户端:
在这里插入图片描述

2、TCP是向字节流(字符串)

在这里插入图片描述
在这里插入图片描述

序列化、反序列化

自定义协议就是定义双方都认识的结构化字段,并且协议中有序列化和反序列化的实现
先定义一个协议(结构化字段)——双方都能看到
设计模式:工厂模式:
客户端发送的结构体+序列化函数+反序列化函数
在这里插入图片描述

服务器反序列化的接收的结构体+反序列函数+序列化函数
在这里插入图片描述
代码:

#pragma once#include <iostream>
#include <memory>namespace Protocol
{const std::string ProtSep = " ";const std::string LineBreakSep = "\n";// 封装为报文——序列化的一部分std::string Encode(const std::string &message) //"x op y"或者"_result _code"{std::string len = std::to_string(message.size());std::string package = len + LineBreakSep + message + LineBreakSep; //"len\n""x op y\n"return package;}// 解报——反序列化的一部分bool Decode(std::string &package, std::string *message) //"len\n""x op y\n"{// 除了解报,我们还要判断是否认正确auto pos = package.find(LineBreakSep);if (pos == std::string::npos)return false;std::string lens = package.substr(0, pos);int messagelen = std::stoi(lens);int total = lens.size() + messagelen + 2 * LineBreakSep.size();if (package.size() < total)return false;// 至少有一个完整报文*message = package.substr(pos + LineBreakSep.size(), messagelen);// 收到的报文可能是"len\n""x op y\n""len\n""x op y\n""len\n""x op y\n"// 所以我解报一个报文后,要删除这个报文,让后面的继续Dcode解报package.erase(0, total);return true;}class Requset{public:Requset() : _data_x(0), _data_y(0), _oper(0){}Requset(int x, int y, int op) : _data_x(x), _data_y(y), _oper(op){}~Requset(){}void Debug(){std::cout << "_data_x:" << _data_x << std::endl;std::cout << "_data_y:" << _data_y << std::endl;std::cout << "_oper:" << _oper << std::endl;}void Inc(){_data_x++;_data_y++;}// 自定义序列化协议:结构体数据->字符串bool Serialize(std::string *out){//"x op y"*out = std::to_string(_data_x) + ProtSep + _oper + ProtSep + std::to_string(_data_y);return true;}// 反序列化   :  字符串->结构体数据bool Deserialize(std::string &in) //"x op y"{auto left = in.find(ProtSep);if (left == std::string::npos)return false;auto right = in.rfind(ProtSep);if (right == std::string::npos)return false;//[)_data_x = std::stoi(in.substr(0, left));_data_y = std::stoi(in.substr(right + ProtSep.size()));std::string oper = in.substr(left + ProtSep.size(), right - (left + ProtSep.size()));if (oper.size() == 1)return false;_oper = oper[0];return true;}int Getx() { return _data_x; }int Gety() { return _data_y; }char GetOper() { return _oper; }//_data_x+_data_y// 报文的自描述//"len\n""x op y\n"private:int _data_x; // 第一个参数int _data_y; // 第二个参数char _oper;  //+ - * / %};class Response{public:Response(): _rseult(0), _code(0){}Response(int result, int code) : _result(result), _code(code){}// 自定义序列化协议:结构体数据->字符串bool Serialize(std::string *out){//"_result  _code"*out = std::to_string(_result) + ProtSep + std::to_string(_code);return true;}// 反序列化:字符串->数据架构数据bool Deserialize(std::string &in) //"_result  _code"{auto pos = in.find(ProtSep);if (pos == std::string::npos)return false;_result = std::stoi(in.substr(0, pos));_code = std::stoi(in.substr(pos + ProtSep.size()));return true;}void SetResult(int res) { _result = res; }void SetCode(int code){ _code=code;}private:int _result; // 运算结果int _code;   // 运算状态};// 简单的工厂模式,建造类设计模式class Factory{public:std::shared_ptr<Requset> BulidRequest(){std::shared_ptr<Requset> req = std::make_shared<Requset>();return req;}std::shared_ptr<Requset> BulidRequest(int x, int y, int op){std::shared_ptr<Requset> req = std::make_shared<Requset>(x, y, op);return req;}std::shared_ptr<Response> BulidResponse(){std::shared_ptr<Response> resp = std::make_shared<Response>();return resp;}std::shared_ptr<Response> BulidResponse(int result, int code){std::shared_ptr<Response> resp = std::make_shared<Response>(result, code);return resp;}};
}

成熟的序列化反序列化:
Json:Value 万能类型
在这里插入图片描述

那么我们序列化和反序列化就可以用这样的:
在这里插入图片描述

总结:发送的数据为结构体,这个结构体就是我们自定义的协议,他的序列化反序列化 这些都是应用层的协议

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/49119.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无损放大图片工具

一、简介 1、Upscayl 是一款无损放大图片工具&#xff0c;支持CPU和GPU&#xff0c;扩图速度特别的快&#xff0c;而且效果特别的好。而且它有多种模型&#xff0c;比如说艺术动漫风格、真实风格、快速生成等等。最大支持16倍放大和亿级别像素&#xff0c;同时支持Windows、Mac…

Ruoyi-WMS本地运行

所需软件 1、JDK&#xff1a;8 安装包&#xff1a;https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.htmlopen in new window 安装文档&#xff1a;https://cloud.tencent.com/developer/article/1698454open in new window 2、Redis 3.0 安装包&a…

Weakly Supervised Contrastive Learning 论文阅读

Abstract 无监督视觉表示学习因对比学习的最新成就而受到计算机视觉领域的广泛关注。现有的大多数对比学习框架采用实例区分作为预设任务&#xff0c;将每个实例视为一个不同的类。然而&#xff0c;这种方法不可避免地会导致类别冲突问题&#xff0c;从而损害所学习表示的质量…

鸿蒙应用框架开发【多HAP】程序框架

多HAP 介绍 本示例展示多HAP开发&#xff0c;简单介绍了多HAP的使用场景&#xff0c;应用包含了一个entry HAP和两个feature HAP&#xff0c;两个feature HAP分别提供了音频和视频播放组件&#xff0c;entry中使用了音频和视频播放组件。 三个模块需要安装三个hap包&#xff…

玩游戏总缺少dll文件怎么办,免费修复DirectX方法

玩游戏或者运行程序时&#xff0c;突然蹦出个提示说“缺少xxxx.dll”&#xff0c;简直让人火大&#xff01;你是不是也遇到过这种情况&#xff0c;重新安装游戏也没用&#xff0c;各种错误提示让人崩溃&#xff1f;别急&#xff0c;阿星今天就来给你支个招&#xff0c;让这烦人…

电子签章-开放签应用

开放签电子签章系统开源工具版旨在将电子签章、电子合同系统开发中的前后端核心技术开源开放&#xff0c;适合有技术能力的个人 / 团队学习或自建电子签章 \ 电子合同功能或应用&#xff0c;避免研发同仁在工作过程中重复造轮子&#xff0c;降低电子签章技术研发要求&#xff0…

Spring源码学习笔记之@Async源码

文章目录 一、简介二、异步任务Async的使用方法2.1、第一步、配置类上加EnableAsync注解2.2、第二步、自定义线程池2.2.1、方法一、不配置自定义线程池使用默认线程池2.2.2、方法二、使用AsyncConfigurer指定线程池2.2.3、方法三、使用自定义的线程池Excutor2.2.4、方法四、使用…

7.25 阿里云OSS上传 + 后台返回token + 导出excel

1.阿里云Oss上传 只需要一点就是上传到云端后&#xff0c;前端调用上传文件接口&#xff0c;返回一个资源路径。 接着在提交表单时&#xff0c;前端把这个路径设置为img的参数即可。 1.1上传限制 只上传图片 Api("阿里云文件管理") CrossOrigin //跨域 RestContr…

算法 定长按组翻转链表

一、题目 已知一个链表的头部head&#xff0c;每k个结点为一组&#xff0c;按组翻转。要求返回翻转后的头部 k是一个正整数&#xff0c;它的值小于等于链表长度。如果节点总数不是k的整数倍&#xff0c;则剩余的结点保留原来的顺序。示例如下&#xff1a; &#xff08;要求不…

谷粒商城实战笔记-60-商品服务-API-品牌管理-效果优化与快速显示开关

文章目录 一&#xff0c;显示状态列改为switch开关二&#xff0c;监听状态改变 首先&#xff0c;把ESLint语法检查关掉&#xff0c;因为这个语法检查过于严格&#xff0c;在控制台输出很多错误信息&#xff0c;干扰开发。 在build目录下下webpack.base.conf.js中&#xff0c;把…

昇思MindSpore学习总结十七 —— 基于MindSpore通过GPT实现情感分类

1、要求 2、导入了一些必要的库和模块 以便在使用MindSpore和MindNLP进行深度学习任务时能使用各种功能&#xff0c;比如数据集处理、模型训练、评估和回调功能。 import os # 导入操作系统相关功能的模块&#xff0c;如文件和目录操作import mindspore # 导入MindSpore库&a…

数据开发/数仓工程师上手指南(二)数仓构建分层概念

前言 回顾上篇文章我们可以用思维导图一遍概览&#xff1a; 在了解了数仓的基本架构之后&#xff0c;我们还需要掌握数仓构建方法&#xff0c;也就是了解数仓是如何建模的&#xff0c;有什么规则和通用方法。我们应该如何去构建一个性能良好、稳定高效、契合业务的数据仓库。…

图形/视图架构的坐标系

图形/视图架构有 3 个有效的坐标系&#xff1a;场景坐标系、视图坐标系、图形项坐标系。 视图坐标系 视图坐标系就是视图组件的物理坐标系&#xff0c;单位是像素。QGraphicsView 视口的左上角坐标总是(0,0)。 场景坐标系 场景坐标系定义了所有图形项的基础坐标&#xff0c;场…

如何排查GD32 MCU复位是由哪个复位源导致的?

上期为大家讲解了GD32 MCU复位包括电源复位和系统复位&#xff0c;其中系统复位还包括独立看门狗复位、内核软复位、窗口看门狗复位等&#xff0c;在一个GD32系统中&#xff0c;如果莫名其妙产生了MCU复位&#xff0c;如何排查具体是由哪个复位源导致的呢&#xff1f; GD32 MC…

Idea如何查看Maven依赖树

1、使用idea自带的功能查看依赖树 2、使用Maven Helper插件 https://zhuanlan.zhihu.com/p/699663369

《Milvus Cloud向量数据库指南》——监管机构和社区:开源许可证标准的守护者与推动者

在开源软件的浩瀚宇宙中,监管机构和社区构成了其稳定运行与持续发展的双轮驱动。这些组织不仅定义了开源的本质,还通过制定、维护和执行许可证标准,确保了开源生态的开放性、透明性和协作精神得以传承。其中,开源倡议组织(OSI)、自由软件基金会(FSF)以及Apache软件基金…

【STM32】IIC学习笔记

学习IIC 前言一、基础知识GPIO_WriteBit 写入高低电平 二、放代码三、逐行细读总结 前言 最近沉迷手写笔记~ 尝试解读江科大的IIC程序&#xff0c;结合笔记更理解IIC 一、基础知识 GPIO_WriteBit 写入高低电平 二、放代码 这个是江科大的软件IIC的设置部分 #include "s…

正点原子 通用外设配置模型 GPIO配置步骤 NVIC配置

1. 这个是通用外设驱动模式配置 除了初始化是必须的 其他不是必须的 2. gpio配置步骤 1.使能时钟是相当于开电 2.设置工作模式是配置是输出还是输入 是上拉输入还是下拉输入还是浮空 是高速度还是低速度这些 3 和 4小点就是读写io口的状态了 3. 这个图是正点原子 将GPIO 的时…

Axure设计之轮播图(动态面板+中继器)

轮播图&#xff08;Carousel&#xff09;是一种网页或应用界面中常见的组件&#xff0c;用于展示一系列的图片或内容&#xff0c;通常通过自动播放或用户交互&#xff08;如点击箭头按钮&#xff09;来切换展示不同的内容。轮播图能够吸引用户的注意力&#xff0c;有效展示重要…

全能数据分析工具:Tableau Desktop 2019 for Mac 中文激活版

Tableau Desktop 2019 一款专业的全能数据分析工具&#xff0c;可以让用户将海量数据导入并记性汇总&#xff0c;并且支持多种数据类型&#xff0c;比如像是编程常用的键值对、哈希MAP、JSON类型数据等&#xff0c;因此用户可以将很多常用数据库文件直接导入Tableau Desktop&am…