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

news2024/11/17 6:33:40

应用层:自定义网络协议:序列化和反序列化,如果是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;
        // TODO
    public:
        // 创建服务器端的套接字,并设置为监听状态监听套接字
        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;
            else
                return 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.coloradmin.cn/o/1949611.html

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

相关文章

AI发展下的伦理挑战:构建未来科技的道德框架

一、引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;我们正处在一个前所未有的科技变革时代。AI不仅在医疗、教育、金融、交通等领域展现出巨大的应用潜力&#xff0c;也在日常生活中扮演着越来越重要的角色。然而&#xff0c;这一技术的迅猛进步也带来…

RuoYi基于SpringBoot+Vue前后端分离的Java快速开发框架学习_2_登录

文章目录 一、登录1.生成验证码2.验证码作用1.大体流程2.代码层面(我们都是从前端开始看起) 一、登录 1.生成验证码 基本思路&#xff1a; 后端生成一个表达式&#xff0c;例如34?7,显而易见后面是答案截取出来题干和答案把题干11&#xff1f;变成图片&#xff0c;变成流&a…

下属不把你当回事?就做好这3步,他们会对你唯命是从!

下属不把你当回事&#xff1f;就做好这3步&#xff0c;他们会对你唯命是从&#xff01; 一&#xff1a;规范制度&#xff0c;做事有理可依 企业管理好比是满汉全席&#xff0c;制度才是压轴大菜&#xff0c;人性化说教不过是菜盘边上的点缀罢了。 千万不可舍本逐末。 事要有人干…

React间的组件通信

一、父传子&#xff08;props&#xff09; 步骤 父组件传递数据&#xff0c;子组件标签身上绑定属性子组件接收数据&#xff0c;props的参数 // 子组件 function Son(props) {return (<div>this is Son, {props.name}</div>) }// 父组件 function App() {const n…

如何使用 DSPy 构建多步骤推理的 RAG 系统

一、前言 检索增强生成 (RAG) 系统已经成为构建基于大语言模型 (LLM) 应用的强大方法。RAG 系统的工作原理是&#xff1a;首先使用检索模型从外部知识源检索相关信息&#xff0c;然后使用这些信息来提示 LLM 生成最终的响应。 然而&#xff0c;基本的 RAG 系统&#xff08;也…

谷粒商城实战笔记-47-商品服务-API-三级分类-网关统一配置跨域

文章目录 一&#xff0c;跨域问题1&#xff0c;跨域问题产生的原因2&#xff0c;预检请求3&#xff0c;跨域解决方案3.1 CORS (Cross-Origin Resource Sharing)后端配置示例&#xff08;Spring Boot&#xff09; 3.2 JSONP (JSON with Padding)3.3 代理服务器Nginx代理配置示例…

python自动化中正则表达式提取(适用于提取文本结果)

对于结果是json格式的我们经常使用jsonpath&#xff0c;但是很多时候我们需要从一些文本中提取数据&#xff0c;这个时候正则表达式的提取就很重要&#xff0c;这边主要分享一些正则表达式的提取方法和应用场景的实践&#xff0c;主要介绍两种用法re.search()跟re.findall() 1…

基于springboot+vue+uniapp的居民健康监测小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

系留无人机在技术上有哪些优势或创新点

系留无人机在技术上具有显著的优势和创新点&#xff0c;主要体现在以下几个方面&#xff1a; 1. 长航时飞行作业&#xff1a; - 系留无人机系统由地面通过市电、发电机或电池组供电&#xff0c;并通过系留线缆将电力传输至无人机&#xff0c;实现了不间断供电。 - 这种供电方式…

概率论--矩估计

目录 简介 矩估计法的基本步骤 延伸 矩估计法在大样本情况下的准确性和有效性如何评估&#xff1f; 在实际应用中&#xff0c;矩估计法的局限性有哪些具体例子&#xff1f; 如何处理矩估计法在某些情况下可能出现的不合理解或无法唯一确定参数的问题&#xff1f; …

江科大/江协科技 STM32学习笔记P13

文章目录 TIM定时中断1、TIM简介计数器预分频器自动重装寄存器 2、定时器类型基本定时器主模式触发DAC 通用定时器高级定时器 3、定时器原理定时中断基本结构预分频器时序计数器时序RCC时钟树 TIM定时中断 1、TIM简介 定时器的基准时钟一般都是主频72MHz&#xff0c;如果对72M…

优化教学流程和架构:构建高效学习环境的关键步骤

在教育领域&#xff0c;设计和优化教学流程和架构是提高学习效果和学生参与度的关键。本文将探讨如何通过合理的教学流程和有效的架构来构建一个高效的学习环境。 ### 1. 理解教学流程和架构的重要性 教学流程指的是教学活动的组织和顺序&#xff0c;而教学架构则是指支持教学…

Centos 8 配置网络源

备份当前的软件源配置文件&#xff1a; sudo cp -a /etc/yum.repos.d /etc/yum.repos.d.bak 清理原有的 yum 仓库配置信息&#xff1a; sudo rm -f /etc/yum.repos.d/*.repo 获取阿里云的 CentOS 8 源配置&#xff1a; sudo curl -o /etc/yum.repos.d/CentOS-Base.repo ht…

Go之Web急速入门Gin+Gorm框架

简介 只作为快速入门、了解Go的GinGorm框架的demo&#xff0c;不能作为企业级开发。 详细用法请看官网 《Gin官网》 《Gorm官网》 使用GoLand创建Go项目&#xff08;默认modules&#xff09; go版本1.22.2 需要设置代理下载go相关软件包&#xff0c;否则软件包可能无法下载。…

unity2D游戏开发06稳定,材质,碰撞器

稳定性 在操控玩家时,我们会发现玩家移动时,摄像头会有抖动,这是摄像机过度精确造成的。 创建名为RoundCameraPos的C#脚本,用Visual Studio打开 代码 using System.Collections; using System.Collections.Generic; using UnityEngine; using Cinemachine;//导入Cinemac…

大模型llama结构技术点分享;transformer模型常见知识点nlp面经

1、大模型llama3技术点 参考&#xff1a;https://www.zhihu.com/question/662354435/answer/3572364267 Llama1-3&#xff0c;数据tokens从1-2T到15T;使用了MHA&#xff08;GQA缓存&#xff09;&#xff1b;上下文长度从2-4-8K&#xff1b;应用了强化学习对其。 1、pretraini…

【YashanDB知识库】yasdb jdbc驱动集成BeetISQL中间件,业务(java)报autoAssignKey failure异常

问题现象 BeetISQL中间件版本&#xff1a;2.13.8.RELEASE 客户在调用BeetISQL提供的api向yashandb的表中执行batch insert并将返回sequence设置到传入的java bean时&#xff0c;报如下异常&#xff1a; 问题的风险及影响 影响业务流程正常执行&#xff0c;无法获得batch ins…

html css 分页按钮效果

结果展示&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>分页按钮效果</title>&…

Richtek立锜科技微型辅听器系统电源解决方案

立锜科技推出微型辅听器系统电源解决方案&#xff0c;为新型设计提供了关键助力&#xff0c;克服小尺寸的挑战&#xff0c;同时确保系统发挥最佳性能。高效率&#xff1a;低静态电流Buck 转换器具有最佳性能&#xff0c;在 1mA 到 10mA 轻载范围内&#xff0c;效率高达 90&…

{Spring Boot 原理篇} Spring Boot自动装配原理

SpringBootApplication 1&#xff0c;Spring Boot 应用启动&#xff0c;SpringBootApplication标注的类就是启动类&#xff0c;它去实现配置类中的Bean的自动装配 SpringBootApplication public class SpringbootRedis01Application {public static void main(String[] args)…