计算机网络(4) --- 协议定制

计算机网络(3) --- 网络套接字TCP_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/132035757?spm=1001.2014.3001.5501

目录

1. 协议的基础知识

TCP协议通讯流程

​编辑

2.协议

1.介绍

2.手写协议

1.内容

2.接口

3.序列化和反序列化

4.协议化

5.读操作

3.客户端和服务端的业务逻辑

1.服务端

2.客户端


1. 协议的基础知识

TCP协议通讯流程

 1.不论是三次握手四次挥手还是数据的传输,其实都是操作系统进行执行的。那么两边的函数也只是操作系统的调用接口,操作系统依然是整体的执行者

2.那么我们不能简单的认为accept就是构建链接,因为链接的建立是操作系统提供的,accept更多的是以一种接收管理的姿态。那么三次握手有没有aceept其实不大有关系。

3.链接其实是一种结构体,该结构体是用于存储客户端连接服务器时对客户端的描述,毕竟连接服务端的不一定只有一个客户端,只要客户端多起来,就一定需要被管理,那么aceept的作用就是将接收到的链接进行描述管理

2.协议

1.介绍

1.协议是一种 "约定"。 socket api的接口,之前写的套接字在读写数据时, 都是按 "字节流" 的方式来发送接收的。

2.但是我们需要传输一些"结构化的数据",比如图片,视频之类的东西。设计为了这些功能的实现,我们将这些东西对应的字节流组成一个结构体,那么由该结构体存储,只要我们将这些字节流存储到结构体内,随后进行序列化得到一个字节流,之后传入到网络中去。接收方接收,得到的则是一个字节流,反序列化出结构体再将所有的数据拿出来,这样就实现了所有形式的数据都能被一个报文直接转发过来。

3.同时也要保证接收方收到一个完整的报文。udp不需要考虑,因为它面向数据报的;tcp不同,它是传输字节流的,需要考虑。

2.手写协议

1.内容

1.为了保证接收方收到一个完整的报文,我们需要自己设计报文和报文之间的边界。

2.除了物理层都没有返送和接收的直接连接,应用层的数据想要发送,其实这些数据都在应用层的缓冲区中,而发送的过程,其实是像tcp/ip层进行拷贝,把应用层的缓冲区拷贝到tcp/ip层的发送缓冲区,而另一边的主机则是通过tcp/ip层的接收缓冲区拷贝下层传上来的数据。这些数据最终是通过网络发送的。应用层的函数调用write和read接口都是拷贝函数。那考虑最终结果其实呈现出的是:C的发送缓冲区的数据拷贝到S的接收缓冲区,反之亦然。

3.由于两边都拥有发送和接收缓冲区,那么也就意味着两边同时的传输是不影响的,这种工作模式是全双工的

4.发送数据很多,并且对端不处理这些发送过来的数据,难免出现数据扎堆出现在缓冲区中,那么读取这些数据就想要分离出以一个报文为单位的数据。这种划分有三种方法:定长,定特殊符号或者这自描述方法

2.接口

与read的作用一致,都是把字节流发送出去

 

 与write的作用一致,都是把字节流接收会来

3.序列化和反序列化

#define SEP " "
#define SEP_LEN strlen(SEP) // 不要使用sizeof
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP) // 不要使用sizeof

class Request
{
public:
    Request()
    {
    }

    Request(int x_, int y_, char op_)
        : x(x_), y(y_), op(op_)
    {
    }

    bool serialize(std::string *out)
    {
        *out = "";
        std::string x_string = std::to_string(x);
        std::string y_string = std::to_string(y);
        *out = x_string;
        *out += SEP;
        *out += op;
        *out += SEP;
        *out += y_string;
        *out += LINE_SEP;
    }

    bool deserialize(const std::string &in)
    {
        auto left = in.find(SEP);
        auto right = in.find(SEP);
        if (left == std::string::npos || right == std::string::npos)
            return false;
        if (left == right)
            return false;
        if (right - (left + SEP_LEN))
            return false;

        std::string x_string = in.substr(0, left);
        if (x_string.empty())
            return false;
        std::string y_string = in.substr(right + SEP_LEN);
        if (y_string.empty())
            return false;
        x = std::stoi(x_string);
        y = std::stoi(y_string);

        op = in[left + SEP_LEN];
        return true;
    }

public:
    int x;
    int y;
    char op;
};

class Response
{
public:
    Response()
    {
    }

    Response(int exitcode_, int result_)
        : exitcode(exitcode_), result(result_)
    {
    }

    bool serialize(std::string *out)
    {
        *out = "";
        std::string ec_string = std::to_string(exitcode);
        std::string res_string = std::to_string(result);

        *out = ec_string;
        *out += SEP;
        *out += res_string;
        *out += LINE_SEP;
        return true;
    }

    bool deserialize(const std::string &in)
    {
        auto mid = in.find(SEP);
        if (mid == std::string::npos)
            return false;
        std::string ec_string = in.substr(0, mid);
        std::string res_string = in.substr(mid + SEP_LEN);

        if (ec_string.empty() || res_string.empty())
            return false;

        exitcode = std::stoi(ec_string);
        result = std::stoi(res_string);
        return true;
    }

public:
    int exitcode;
    int result;
};

基于计算的业务处理逻辑下:

1.request表示请求,即接收方接收的数据

2.response表示应答,即发送方得到接收方处理结果后的数据

3.不论发送还是接收方,其request和response都是想要有序列化和反序列化的。想要注意的是,序列化和反序列化是一种结构化的类型,不表示协议,它是协议的有效载荷部分

4.序列化的逻辑:将当前存储的一系列想要被操作的数据,通过存储在out中。通过分隔符进行组合

5.反序列化的逻辑:接受到了in,将in的数据拆分回原来的结构,拆分的依据就是分隔符

4.协议化

std::string enLength(const std::string &text)
{
    std::string send_string = std::to_string(text.size());
    send_string += LINE_SEP;
    send_string += text;
    send_string += LINE_SEP;

    return send_string;
}

bool deLength(const std::string &package, std::string *text)
{
    auto pos = package.find(LINE_SEP);
    if (pos == std::string::npos)
        return false;
    std::string text_len_string = package.substr(0, pos);
    int text_len = std::stoi(text_len_string);
    *text = package.substr(pos + LINE_SEP_LEN, text_len);
    return true;
}

1.规定的协议为:当前序列化的字节流长度大小作为前面的一个字节流且用\r\n来进行分隔序列化的字节流,大概模式为XXX\r\nYYY\r\n

2.加协议enLength:就是将传入的text之前算出text的大小,先加入大小后填入分隔符最后加入序列化的字节流

3.减协议deLength:先找到第一个分隔符,得到整个序列化字节流的大小,随后通过大小长度收录整个序列化的字节流

5.读操作

bool recvPackage(int sock, std::string &inbuffer,std::string *text)
{
    char buffer[1024];
    while (true)
    {
        ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer += buffer;
            auto pos = inbuffer.find(LINE_SEP);
            if (pos == std::string::npos)
                continue;
            std::string text_len_string = inbuffer.substr(0, pos);
            int text_len = std::stoi(text_len_string);
            int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;
            if (inbuffer.size() < total_len)
                continue;

            *text = inbuffer.substr(0, total_len);
            inbuffer.erase(0, total_len);
            break;
        }
        else
            return false;
    }
    return true;
}

3.客户端和服务端的业务逻辑

1.服务端

void handerEnter(int sock, func_t func)
    {
        std::string inbuffer;
        while (true)
        {
            // 1.读取
            // 1.1读取完整的一个报文
            std::string req_text;
            if (!recvRequest(sock, inbuffer, &req_text))
                return;
            // 1.2读取完整的报文
            std::string req_str;
            if (!deLength(req_text, &req_str))
                return;

            // 2.反序列化
            // 2.1得到一个结构化的请求对象
            Request req;
            if (!req.deserialize(req_str))
                return;

            // 3.计算业务
            // 3.1得到一个结构化的响应
            Response resp;
            func(req, resp);

            // 4.对响应进行序列化
            // 4.1得到一个字符串
            std::string resp_str;
            resp.serialize(&resp_str);

            // 5.然会发送给客户端一个响应
            // 5.1构造新报文
            std::string send_string = enLength(resp_str);
            send(sock, send_string.c_str(), send_string.size(), 0);
        }
    }

2.客户端

void start()
        {
            // 5.要发起链接
            struct sockaddr_in server;
            memset(&server, 0, sizeof server);
            server.sin_family = AF_INET;
            server.sin_port = htons(_serverport);
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());

            if (connect(_sock, (struct sockaddr *)&server, sizeof server) != 0)
            {
                std::cerr << "connect error: " << errno << " : " << strerror(errno) << std::endl;
            }
            else
            {
                std::string msg;
                std::string inbuffer;
                while (true)
                {
                    std::cout << "mycal>>> ";
                    std::getline(std::cin, msg);

                    //msg要拆分
                    Request req(msg);
                    std::string content;
                    req.serialize(&content);
                    std::string send_string = enLength(content);
                    send(_sock, send_string.c_str(), send_string.size(), 0);

                    std::string package, text;
                    if (!recvPackage(_sock, inbuffer, &package))
                        continue;
                    if (!deLength(package, &text))
                        continue;
                    Response resp;
                    resp.deserialize(text);
                    std::cout << "exitcode: " << resp.exitcode << std::endl;
                    std::cout << "result: " << resp.result << std::endl;
                }
            }
        }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 watch监听器

概览&#xff1a;watch监听器的定义以及使用场景。在vue3中的监听器的使用方式&#xff0c;watch的三个参数&#xff0c;以及进一步了解第一个参数可以是一个属性&#xff0c;也可以是一个数组的形式包含多个属性。 watch在vue3和vue2中的使用&#xff1a; vue3中&#xff1a…

填补5G物联一张网,美格智能快速推进RedCap商用落地

自5G R17版本标准冻结以来&#xff0c;RedCap一直引人注目。2023年更是5G RedCap突破性发展的一年&#xff0c;从首款5G RedCap调制解调器及射频系统——骁龙X35发布&#xff0c;到国内四大运营商发布RedCap技术白皮书&#xff0c;芯片厂商、模组厂商、运营商及终端企业都在积极…

不懂这些专业名词,你很难成为有水平的项目经理——数据分析篇

大家好&#xff0c;我是老原。 前段时间我们项目组招了个新人小林&#xff0c;让他去和产品经理对下产品上线情况&#xff0c;等到下班也没等来反馈。 第二天在茶水间遇到了产品经理就问了一嘴&#xff0c;才知道已经对接到位了。 一问小林才知道&#xff0c;他完全不知道产…

【react】react生命周期钩子函数:

文章目录 一、生命周期概念:二、生命周期:三、挂载阶段&#xff08;constructor > render > componentDidMount&#xff09;&#xff1a;四、更新阶段&#xff08;render > componentDidUpdate&#xff09;&#xff1a;五、卸载阶段&#xff08;componentWillUnmount …

CSS 滚动条

一、滚动条样式属性 ::-webkit-scrollbar {width: 6px; /* 竖向滚动条宽度 */height: 6px; /* 横向滚动条高度 */ }::-webkit-scrollbar-thumb {border-radius: 10px; /* 滚动条样式 */-webkit-box-shadow: inset 0 0 3px red; /* 内阴影 */background-color: blue; /* 滚动条…

eNSP 路由器启动时一直显示 # 号的解决办法

文章目录 1 问题截图2 解决办法2.1 办法一&#xff1a;排除防火墙原因导致 3 验证是否成功 1 问题截图 路由器命令行一直显示 # 号&#xff0c;如下图 2 解决办法 2.1 办法一&#xff1a;排除防火墙原因导致 排查是否因为系统防火墙原因导致。放行与 eNSP 和 virtualbox 相…

从零搭建一个react + electron项目

最近打算搭建一个react electron的项目&#xff0c;发现并不是那么傻瓜式 于是记录一下自己的实践步骤 通过create-react-app 创建react项目 npx create-react-app my-app 安装electron依赖 npm i electron -D暴露react项目的配置文件&#xff08;这一步看自己需求&#xff0c…

Redis的缓存穿透、缓存击穿和缓存雪崩

目录 一、解释说明 二、缓存穿透 1. 什么是缓存穿透&#xff1f; 2. 常见的两种解决方案 &#xff08;1&#xff09;缓存空对象 &#xff08;2&#xff09;布隆过滤 3. 编码解决商品查询的缓存穿透问题 三、缓存击穿 1. 什么是缓存击穿&#xff1f; 2、缓存击穿解决方案&a…

prometheus监控k8s kube-proxy target down

prometheus kube-proxy target down 解决 修改配置 kubectl edit cm/kube-proxy -n kube-systemmetricsBindAddress: "0.0.0.0:10249"删除 kube-proxy pod 使之重启应用配置 kubectl delete pod --force `kubectl get pod -n kube-system |grep kube-proxy|awk {pr…

iOS——Block two

Block 的实质究竟是什么呢&#xff1f;类型&#xff1f;变量&#xff1f;还是什么黑科技&#xff1f; Blocks 是 带有局部变量的匿名函数 Blocks 由 OC 转 C 源码方法 在项目中添加 blocks.m 文件&#xff0c;并写好 block 的相关代码。打开「终端」&#xff0c;执行 cd XX…

前端vue uni-app自定义精美海报生成组件

在当前技术飞速发展的时代&#xff0c;软件开发的复杂度也在不断提高。传统的开发方式往往将一个系统做成整块应用&#xff0c;一个小的改动或者一个小功能的增加都可能引起整体逻辑的修改&#xff0c;从而造成牵一发而动全身的情况。为了解决这个问题&#xff0c;组件化开发逐…

数据结构入门指南:带头双向循环链表

目录 文章目录 前言 1.结构与优势 2.链表实现 2.1 定义链表 2.2 创建头节点 2.3 尾插 2.4 输出链表 2.5 尾删 2.6 头插 2.7头删 2.8 节点个数 2.9 查找 2.10 位置插入 2.11 位置删除 2.12 销毁链表 3. 源码 总结 前言 链表一共有8种结构&#xff0c;但最常用的就是无头单…

NsPack3.x脱壳手记

发现是NsPack3.x的壳 使用ESP守恒快速脱壳 F9遇到popfd后下面的jmp就是通往OEP了 打开LordPE准备转储映像, 首先调整下ImageSize, 接着dump full 接着不要退出目前的调试, 打开Scylla修复IAT, 把OEP的VA地址输入到OEP处, 接着按照如下图所示步骤 完成后如下, 但NsPack3.x…

探索创意之路:稳定扩散AI绘画指南

文章目录 引言第一部分&#xff1a;了解稳定扩散AI绘画1.1 稳定扩散AI绘画简介1.2 稳定扩散AI绘画的优势 第二部分&#xff1a;使用稳定扩散AI绘画2.1 获取稳定扩散AI绘画工具2.2 准备绘画素材和设置参数2.3 进行AI绘画 第三部分&#xff1a;发挥创意&#xff0c;创作精彩绘画3…

结构体和 Json 相互转换(序列化反序列化)

关于 JSON 数据 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也 易于机器解析和生成。RESTfull Api 接口中返回的数据都是 json 数据。 Json 的基本格式如下&#xff1a; { "a": "Hello", "b": "…

【Linux】五种IO模型

文章目录 1. IO基本概念2. 五种IO模型2.1 五个钓鱼的例子2.2 五种IO模型2.2.1 阻塞IO2.2.2 非阻塞IO2.2.3 信号驱动IO2.2.4 IO多路转接2.2.5 异步IO 1. IO基本概念 认识IO IO就是输入和输出&#xff0c;在冯诺依曼体系结构中&#xff0c;将数据从输入设备拷贝到内存就叫输入&am…

STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解

STM32使用HAL库之Msp回调函数 1.问题提出 在STM32的HAL库使用中&#xff0c;会发现库函数大都被设计成了一对&#xff1a; HAL_PPP/PPPP_Init HAL_PPP/PPPP_MspInit 而且HAL_PPP/PPPP_MspInit函数的defination前面还会有__weak关键字 上面的PPP/PPPP代表常见外设的名称为…

什么是OCR?OCR技术详解

光学字符识别(Optical Character Recognition)简称为“OCR”。ORC是指对包含文本资料的图像文件进行分析识别处理&#xff0c;获取文字及版面信息的技术。 一般包括以下几个过程&#xff1a; 1.图像输入 针对不同格式的图像&#xff0c;有着不同的存储格式和压缩方式。目前&…

JMeter 的使用

文章目录 1. JMeter下载2. JMeter的使用2.1 JMeter中文设置2.2 JMeter的使用2.2.1 创建线程组2.2.2 HTTP请求2.2.3 监听器 1. JMeter下载 官网地址 https://jmeter.apache.org/download_jmeter.cgi https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.2.zip 下载解…

[JAVAee]锁策略

目录 乐观锁与悲观锁 乐观锁 乐观锁的冲突检测 悲观锁 读锁与写锁 重量级锁与轻量级锁 重量级锁 轻量级锁 自旋锁 公平锁与非公平锁 可重入锁与不可重入锁 乐观锁与悲观锁 乐观锁 在乐观锁中,假设数据并不会发生冲突,在正式提交数据时会对数据进行冲突检测,如果发…