自定义协议(应用层协议)——网络版计算机基于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.xdnf.cn/news/1488494.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

友思特应用 | 硅片上的光影贴合:UV-LED曝光系统在晶圆边缘曝光中的高效应用

导读 晶圆边缘曝光是帮助减少晶圆涂布过程中多余的光刻胶对电子器件影响的重要步骤。友思特 ALE/1 和 ALE/3 UV-LED 高性能点光源&#xff0c;作为唯一可用于宽带晶圆边缘曝光的 i、h 和 g 线的 LED 解决方案&#xff0c;可高效实现WEE系统设计和曝光需求。 晶圆边缘曝光及处…

<数据集>棉花开花程度识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;13765张 标注数量(xml文件个数)&#xff1a;13765 标注数量(txt文件个数)&#xff1a;13765 标注类别数&#xff1a;4 标注类别名称&#xff1a;[Partially opened, Fully opened boll, Defected boll, Flower] 序…

【Android】大喇叭——广播

广播机制介绍 Android中的广播分为两种类型&#xff1a;标准广播和有序广播 标准广播&#xff1a;是一种完全异步执行的广播&#xff0c;在广播发出之后&#xff0c;所有的广播接收器几乎都会在同一时刻接受到这条广播信息&#xff0c;它们之间没有先后顺序。这种广播的效率会…

ARTMO Table ‘db1.test_mla_result‘ doesn‘t exist解决方案

com.mysql.jdbc.JDBC4PreparedStatement3f3c966c: describe test_mla_result; Java exception occurred: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table db1.test_mla_result doesnt exist解决方案&#xff1a; 打开MySQL的command Line, 输入SHOW TABLES…

Eclipse 生成 jar 包

打开 Jar 文件向导 Jar 文件向导可用于将项目导出为可运行的 jar 包。 打开向导的步骤为: 在 Package Explorer 中选择你要导出的项目内容。如果你要导出项目中所有的类和资源&#xff0c;只需选择整个项目即可。点击 File 菜单并选择 Export。在输入框中输入"JAR"…

谷粒商城实战笔记-55-商品服务-API-三级分类-修改-拖拽数据收集

文章目录 一&#xff0c;拖拽后结点的parentCid的更新二&#xff0c;拖拽后结点的父节点下所有结点的sort排序属性的变化更新排序的逻辑代码分析 三&#xff0c;拖拽后结点及其子节点catLevel的变化判断是否需要更新 catLevel获取拖动后的新节点 更新 catLevel完整代码 这一节的…

Apache ShardingSphere Proxy5.5.0实现MySQL分库分表与读写分离

1. 前提准备 1.1 主机IP:192.168.186.77 version: 3.8services:mysql-master:image: mysql:latestcontainer_name: mysql-masterenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: masterMYSQL_PASSWORD: 123456MYSQL_DATABASE: db1 ports:- "3306:3306&quo…

十一、Dockerfile解析

目录 一、Dockerfile简介 二、dockerfile的构建的三个步骤 三、Dockerfile的构建过程 1、DockerFile内容的基础知识 2、Docker执行Dockerfile的大致流程 四、dockerfile常用的保留字 1、FROM 2、MAINTAINER 3、RUN 4、EXPOSE 5、WORKDIR 6、USER 7、ENV 8、VOLUME …

Tensorflow2.0全套学习(持续更新中)

0 简介 1.学习目标 两大核心模块&#xff1a;深度学习经典算法与Tensorflow项目实战 从零开始&#xff0c;详细的网络模型架构与框架实现方法 2.tensorflow&#xff1a;核心开源库&#xff0c;深度学习框架&#xff0c;帮助开发和训练机器学习模型 3.版本2比1有哪些优势 …

企元数智:数字化转型,免费送新零售合规分销系统

企元数智&#xff0c;作为数字化转型领域的领先者&#xff0c;宣布推出一项划时代的举措&#xff1a;免费送出新零售合规分销系统&#xff0c;助力更多企业加速数字化转型进程。 随着新零售潮流的席卷&#xff0c;企业们越来越意识到数字化转型的紧迫性和必要性。然而&#xff…

LLMs之Llama 3.1:Llama 3.1的简介、安装和使用方法、案例应用之详细攻略

LLMs之Llama 3.1&#xff1a;Llama 3.1的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;2024年7月23日&#xff0c;Meta重磅推出Llama 3.1。本篇文章主要提到了Meta推出的Llama 3.1自然语言生成模型。 背景和痛点 >> 过去开源的大型语言模型在能力和性能上一…

后台管理系统登录安全和权限要求

一、前言 几乎所有的系统都有后台管理系统&#xff0c;后台登录需要账号和密码&#xff0c;后台管理员权限需要有控制。所有管理员的操作都应该有操作日志。 二、存在的问题 现在很多系统只需要账号和密码就能登录&#xff0c;有的还是简单账号和简单密码&#xff0c;就是弱口…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十六章 自动创建设备节点

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

H3CNE(vlan的基础配置)

目录 9.1 传统以太网的问题 9.2 VLAN基础实现的原理 示例一&#xff08;vlan配置的基础实现&#xff09;&#xff1a; 示例二&#xff08;交换机间配置trunk&#xff09;&#xff1a; 9.3 hybrid接口类型与打标签的原理 示例三&#xff08;配置hybrid接口&#xff09;&#x…

通过IEC104转MQTT网关轻松接入阿里云平台

随着智能电网和物联网技术的飞速发展&#xff0c;电力系统中的传统IEC 104协议设备正面临向现代化、智能化转型的迫切需求。阿里云作为全球领先的云计算服务提供商&#xff0c;其强大的物联网平台为IEC 104设备的接入与数据处理提供了强大的支持。本文将深入探讨钡铼网关在MQTT…

网站被浏览器提示“不安全”,如何解决

在互联网时代&#xff0c;网络安全是每个网站所有者和用户共同关心的问题&#xff0c;当网站的数据传输未经过加密时&#xff0c;会被主流浏览器标记为“不安全”时&#xff0c;这不仅会影响用户体验&#xff0c;还可能损害网站的信誉&#xff0c;导致访问量下降。 一、理解警…

go语言开发Prometheus Exporter(DM数据库)

一、介绍 源码步骤基于dameng_exporter源码讲解&#xff0c;看完本篇文章可以直接进行二次开发。 dameng exporter的开源地址&#xff1a;https://github.com/gy297879328/dameng_exporter&#xff08;可直接对接prometheusgrafana 提供表盘&#xff09; 开发一个exporter 其…

基于微信小程序+SpringBoot+Vue的自习室选座与门禁系统(带1w+文档)

基于微信小程序SpringBootVue的自习室选座与门禁系统(带1w文档) 基于微信小程序SpringBootVue的自习室选座与门禁系统(带1w文档) 本课题研究的研学自习室选座与门禁系统让用户在小程序端查看座位&#xff0c;预定座位&#xff0c;支付座位价格&#xff0c;该系统让用户预定座位…

Discourse 如何通过终端工具访问 PGSQL

PGSQL 在 Discourse 中是通过容器方式运行的&#xff0c;要访问 PGSQL 中的数据那么首先需要进入到容器后才可以。 进入容器的命令为&#xff1a; cd /var/discourse/./launcher enter appsu discoursepsql discourse最后的命令是登录到运行在容器中的 pgsql。 查看 pgsql 上…

学习笔记之Java篇(0725)

p this 普通方法中&#xff0c;this总是指向调用该方法的对象。 构造方法中&#xff0c;this总是指向正要初始化的对象。 this&#xff08;&#xff09;调用必须重载的构造方法&#xff0c;避免相同地址初始化代码&#xff0c;但只能在构造方法中用&#xff0c;比企鹅必须位…