当前位置: 首页 > news >正文

【Hello Network】协议

作者:@小萌新
专栏:@网络
作者简介:大二学生 希望能和大家一起进步
本篇博客简介:简单介绍下协议并且设计一个简单的网络服务器

协议

    • 协议的概念
    • 结构化数据传输
    • 序列化和反序列化
    • 网络版计算机
      • 服务端代码
      • 协议定制
      • 客户端代码
      • 服务线程执行例程
      • 存在的问题
      • 代码测试

协议的概念

协议 网络协议的简称 网络协议是通信计算机双方必须共同遵从的一组约定 比如怎么建立连接、怎么互相识别等

就像我们在之前的博客 网络基础 里面写的一样 协议的本质其实就是一种约定

结构化数据传输

通信双方在进行网络通信的时候:

  • 如果要传输的数据是一个字符串 那么通信双方直接发送即可
  • 如果要传输的数据是一些结构体 此时就不能将这些数码一个个发送到网络中

比如说我们现在要实现一个网络版本的计算器 那么客户端每次发送的请求就需要包括左操作数 右操作数 和对应的操作 那么此时客户端要发送的就不是一个简单的字符串 而是一个结构体

如果客户端将这些结构化的数据单独一个个发送到网络中 那么服务端也只能一个个的接受 但是这样子传输容易导致数据错乱

所以说最好的方案是客户端将这些结构化的数据统一打包发送到网络中 此时服务端接受的就是一个完整的请求了 客户端常见的打包方式有下面两种

将结构化的数据组合成一个字符串

约定方案一:

  • 客户端发送一个形如“1+1”的字符串
  • 这个字符串中有两个操作数 都是整型
  • 两个数字之间会有一个字符是运算符
  • 数字和运算符之间没有空格

客户端可以按某种方式将这些结构化的数据组合成一个字符串 然后将这个字符串发送到网络当中 此时服务端每次从网络当中获取到的就是这样一个字符串 然后服务端再以相同的方式对这个字符串进行解析 此时服务端就能够从这个字符串当中提取出这些结构化的数据

定制结构体+序列化和反序列化

约定方案二:

  • 定制结构体来表示我们想要传递的信息
  • 发送数据时将这个结构体按照一个规则转换成网络标准数据格式 接收数据时再按照相同的规则把接收到的数据转化为结构体
  • 这个过程我们就叫做序列化和反序列化

客户端可以定制一个结构体 将需要交互的信息定义到这个结构体当中

客户端发送数据时先对数据进行序列化 服务端接收到数据后再对其进行反序列化 此时服务端就能得到客户端发送过来的结构体 进而从该结构体当中提取出对应的信息

序列化和反序列化

序列化和反序列化

  • 序列化就是将对象的状态信息转化为字节序的过程
  • 反序列化就是将字节序恢复为对象的过程

OSI七层模型中表示层的作用就是 实现设备固有数据格式和网络标准数据格式的转换

其中设备固有的数据格式指的是数据在应用层上的格式 而网络标准数据格式则指的是序列化之后可以进行网络传输的数据格式

序列化和反序列化的目的

  • 在网络传输时 序列化目的是为了方便网络数据的发送和接收 无论是何种类型的数据 经过序列化后都变成了二进制序列 此时底层在进行网络数据传输时看到的统一都是二进制序列
  • 序列化后的二进制序列只有在网络传输时能够被底层识别 上层应用是无法识别序列化后的二进制序列的 因此需要将从网络中获取到的数据进行反序列化 将二进制序列的数据转换成应用层能够识别的数据格式

我们可以认为网络通信和业务处理处于不同的层级 在进行网络通信时底层看到的都是二进制序列的数据 而在进行业务处理时看得到则是可被上层识别的数据 如果数据需要在业务处理和网络通信之间进行转换 则需要对数据进行对应的序列化或反序列化操作

在这里插入图片描述

网络版计算机

服务端代码

首先我们需要对服务器进行初始化:

  • 调用socket函数,创建套接字
  • 调用bind函数,为服务端绑定一个端口号
  • 调用listen函数,将套接字设置为监听状态

初始化完服务器后就可以启动服务器了 服务器启动后要做的就是不断调用accept函数 从监听套接字当中获取新连接 每当获取到一个新连接后就创建一个新线程 让这个新线程为该客户端提供计算服务

  #include <iostream>    #include <string>    #include <cstring>    #include <sys/socket.h>    #include <sys/types.h>    #include <netinet/in.h>    #include <arpa/inet.h>    #include <unistd.h>    using namespace std;    int main(int argc , char* argv[])    {    if (argc != 2)    {    cout << "usage error" << endl;    exit(1);    }    // port socket                     int port = atoi(argv[1]);    int listen_sock = socket(AF_INET , SOCK_STREAM , 0);    if (listen_sock < 0)    {    cout << "socket error" << endl ;    exit(2);                                                                                                                                                                                                                                                                                                                                                      }    struct sockaddr_in local;    memset(&local , 0 , sizeof(local));    local.sin_family = AF_INET;    local.sin_port = htons(port);    local.sin_addr.s_addr = htonl(INADDR_ANY);    if (bind(listen_sock , (struct sockaddr*)&local , sizeof(local)) < 0)    {    cout << "bind error" << endl;    exit(3);    }    if (listen(listen_sock , 5) < 0)    {    cout << "listen error" << endl;    exit(4);    }    struct sockaddr_in peer;    memset(&peer , '\0' , sizeof(peer));    socklen_t len;    for(;;)    {    int sock = accept(listen_sock , (struct sockaddr*)&peer , &len);    if (sock < 0)    {    cout << "accept error" << endl;    continue; // do not stop     }    pthread_t tid;    int* p = new int(sock);    
E>    pthread_create(&tid , nullptr , Rontinue , (void*) p)    }    return 0;    }  

说明一下:

  • 当前服务器采用的是多线程的方案 你也可以选择采用多进程的方案或是将线程池接入到多线程当中
  • 服务端创建新线程时 需要将调用accept获取到套接字作为参数传递给该线程 为了避免该套接字被下一次获取到的套接字覆盖 最好在堆区开辟空间存储该文件描述符的值

协议定制

要实现一个网络版的计算器 就必须保证通信双方能够遵守某种协议约定 因此我们需要设计一套简单的约定 数据可以分为请求数据和响应数据 因此我们分别需要对请求数据和响应数据进行约定

在实现时可以采用C++当中的类来实现 也可以直接采用结构体来实现 这里就使用结构体来实现 此时就需要一个请求结构体和一个响应结构体

  • 请求结构体中需要包括两个操作数 以及对应需要进行的操作
  • 响应结构体中需要包括一个计算结果 除此之外 响应结构体中还需要包括一个状态字段 表示本次计算的状态 因为客户端发来的计算请求可能是无意义的 比如说除0操作等

规定状态字段对应的含义:

  • 状态字段为0 表示计算成功
  • 状态字段为1 表示非法计算
  typedef struct request    {    int left;    int right;    char op;    }request_t;    typedef struct response    {    int code;    int result;                                                                                                                          }response_t;   

要注意的是作为一种约定 它必须要被通信的双方所知晓 也就是说 要么我们将这个协议写在一个头文件中并同时包含在客户端和服务端中 要么在客户端和服务端都写上这么一段相同的代码

客户端代码

客户端首先也需要进行初始化:

  • 调用socket函数 创建套接字

客户端初始化完毕后需要调用connect函数连接服务端 当连接服务端成功后 客户端就可以向服务端发起计算请求了 这里可以让用户输入两个操作数和一个操作符构建一个计算请求 然后将该请求发送给服务端 而当服务端处理完该计算请求后 会对客户端进行响应 因此客户端发送完请求后还需要读取服务端发来的响应数据

int main(int argc , char* argv[])    
{    if (argc != 3)    {    cerr << "usage error" << endl;    exit(1);    }    string ip = argv[1];    int port = atoi(argv[2]);    int sockfd = socket(AF_INET , SOCK_STREAM , 0);    if (sockfd < 0)    {    cerr << "socket error" << endl;    exit(2);    }    struct sockaddr_in peer;    memset(&peer , '\0' , sizeof(peer));    peer.sin_family = AF_INET;    peer.sin_port = htons(port);    peer.sin_addr.s_addr = inet_addr(ip.c_str());    // connect     if (connect(sockfd , (struct sockaddr*)&peer , sizeof(peer)) < 0)    {    cerr << "connect error" << endl;    exit(3);    }    while(true)    {    request_t rq;    cout << "请输入左操作数#" ;    cin >> rq.left;    cout << "请输出右操作数#" ;    cin >> rq.right;    cout << "请输出操作符#" ;    cin >> rq.op;    write(sockfd , &rq , sizeof(rq));    response_t rp;    read(sockfd , &rp , sizeof(rp)) ;    cout << "code: " << rp.code << endl;    cout << "result:" << rp.result << endl;    }    return 0;    
}    

服务线程执行例程

当服务端调用accept函数获取到新连接并创建新线程后 该线程就需要为该客户端提供计算服务 此时该线程需要先读取客户端发来的计算请求 然后进行对应的计算操作

    
void* Routine(void* arg)    
{                      pthread_detach(pthread_self()); //分离线程    int sock = *(int*)arg;    delete (int*)arg;    while (true){       request_t rq;                  ssize_t size = recv(sock, &rq, sizeof(rq), 0);    if (size > 0){    response_t rp = { 0, 0 };            switch (rq.op){    case '+':    rp.result = rq.left + rq.right;    break;    case '-':    rp.result = rq.left - rq.right;    break;    case '*':    rp.result = rq.left * rq.right;    break;                      case '/':    if (rq.right == 0){    rp.code = 1; //除0错误             }    else{     rp.result = rq.left / rq.right;    }                      break;                      case '%':    if (rq.right == 0){    rp.code = 2; //模0错误             }    else{     rp.result = rq.left % rq.right;    }                          break;    default:    rp.code = 3; //非法运算          break;    }                     send(sock, &rp, sizeof(rp), 0);    }           else if (size == 0){    cout << "service done" << endl;    break;                           }           else{    cerr << "read error" << endl;                                                                                         break;      }                }    close(sock);    return nullptr;    
}  

存在的问题

  • 如果客户端和服务器分别在不同的平台下运行 在这两个平台下计算出请求结构体和响应结构体的大小可能会不同 此时就可能会出现一些问题
  • 在发送和接收数据时没有进行对应的序列化和反序列化操作 正常情况下是需要进行的

虽然当前代码存在很多潜在的问题 但这个代码能够很直观的告诉我们什么是约定 这里将其当作一份示意性代码

代码测试

我们开始运行代码
在这里插入图片描述

如果是正常的计算 我们的计算器就能正常运行

如果涉及到除0 模0操作 该服务器就会返回我们一个错误码

相关文章:

【Hello Network】协议

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;简单介绍下协议并且设计一个简单的网络服务器 协议 协议的概念结构化数据传输序列化和反序列化网络版计算机服务端代码协议定制客户端代码服务线程执…...

零项目零科研,本科排名倒数,一战上岸上海交大电子与通信工程

笔者来自通信考研小马哥23上交819全程班学员 本科就读于哈工大&#xff08;威海&#xff09;&#xff0c;本科成绩很差&#xff0c;专业排名62/99&#xff0c;没有科研&#xff0c;没有实验室&#xff0c;没有项目&#xff0c;连最基本大家都会参加的科技立项我四年也没有参与…...

NOIP模拟赛 T3区间

题目大意 有 n n n个数字&#xff0c;第 i i i个数字为 a i a_i ai​。有 m m m次询问&#xff0c;每次给出 k i k_i ki​个区间&#xff0c;每个区间表示第 l i , j l_{i,j} li,j​到第 r i , j r_{i,j} ri,j​个数字&#xff0c;求这些区间中一共出现了多少种不同的数字。部…...

【Python】如何用pyth做游戏脚本(太简单了吧)

文章目录 前言一、开发前景二、开发流程3.1、获取窗口句柄&#xff0c;把窗口置顶3. 2、截取游戏界面&#xff0c;分割图标&#xff0c;图片比较 二、程序核心-图标连接算法&#xff08;路径寻找&#xff09;四、开发总结五、源码总结 前言 简述&#xff1a;本文将以4399小游戏…...

【Linux】磁盘与文件系统

目录 一、磁盘的物理结构 二、磁盘逻辑抽象 三、文件系统 1、Super Block 2、Group Descriptor Table 3、inode Table 4、Data Blocks 5、inode Bitmap 6、Block Bitmap 四、Linux下文件系统 1、inode与文件名 2、文件的增删查改 2.1、查看文件内容 2.2、删除文件…...

Transformer中的注意力机制及代码

文章目录 1、简介2、原理2.1 什么是注意力机制2.2 注意力机制在NLP中解决了什么问题2.3 注意力机制公式解读2.4 注意力机制计算过程 3、单头注意力机制与多头注意力机制4、代码4.1 代码14.2 代码2 1、简介 最近在学习transformer&#xff0c;首先学习了多头注意力机制&#xf…...

ChatGPT在连续追问下对多线程和双重检查锁模式的理解--已经超越中级程序员

一、问&#xff1a; private static final Map<Method, GZHttpClientResultModel> CACHE_RESULT_MODEL new ConcurrentHashMap<>();public void abc(Method method){cacheResultMode(method);GZHttpClientResultModel model CACHE_RESULT_MODEL.get(method);}pr…...

每天一道大厂SQL题【Day22】华泰证券真题实战(四)

每天一道大厂SQL题【Day22】华泰证券真题实战(四) 大家好&#xff0c;我是Maynor。相信大家和我一样&#xff0c;都有一个大厂梦&#xff0c;作为一名资深大数据选手&#xff0c;深知SQL重要性&#xff0c;接下来我准备用100天时间&#xff0c;基于大数据岗面试中的经典SQL题&…...

【智能电网】智能电网中针对DOS和FDIA的弹性分布式EMA(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

IDEA 创建微服务项目实例

🎈 作者:Linux猿 🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、云计算、物联网、面试、刷题、算法尽管咨询我,关注我,有问题私聊! 🎈 关注专栏:C/C++面试通关【精讲】 优质好文持续更新中……🚀🚀🚀 🎈 欢迎小伙伴们点赞👍、收藏⭐、留…...

注册苹果开发者账号的方法

在2020年以前&#xff0c;注册苹果开发者账号后&#xff0c;就可以生成证书。 但2020年后&#xff0c;因为注册苹果开发者账号需要使用Apple Developer app注册开发者账号&#xff0c;所以需要缴费才能创建ios证书了。 所以新政策出来后&#xff0c;注册苹果开发者账号&#…...

OpenCV2 计算机视觉应用编程秘籍:1~5

原文&#xff1a;OpenCV2 Computer Vision Application Programming Cookbook 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 计算机视觉 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 当别人说你没有底线…...

Domino自带的JSON校验工具

大家好&#xff0c;才是真的好。 JSON数据在Notes/Domino已经变得非常重要。从Domino 10开始&#xff0c;在LotusScript语言中就加入了对JSON数据处理功能。在管理中&#xff0c;我们知道&#xff0c;从Domino 12版本开始就支持Domino自动化配置&#xff0c;也是使用JSON数据作…...

CentOS(linux)使用Docker安装nacos

1. 拉取nacos镜像 docker pull nacos/nacos-server:2.0.3 2. 创建所需文件夹(以安装在home目录下为例) 1) 创建conf文件夹 mkdir -p /home/nacos/conf a. 新增文件application.properties(或者不增加该文件,会使用默认的) 文件内容如下: # spring server.servlet.contextP…...

无线测温在线监测系统工作原理与产品选型

摘要&#xff1a;本文首先介绍了无线测温在线监测系统的基本工作原理以及软硬件组成&#xff0c;重点介绍了在线监测的无线测温技术特点。在此研究基础上&#xff0c;探讨了无线测温在线监测系统在实际工作场景中的应用案例&#xff0c;证明了其在温度检测方面的重要应用价值。…...

Nginx rewrite ——重写跳转

Nginx常见模块 http http块是Nginx服务器配置中的重要部分&#xff0c;代理、缓存和日志定义等绝大多数的功能和第三方模块的配置都可以放在这模块中。作用包括&#xff1a;文件引入、MIME-Type定义、日志自定义、是否使用sendfile传输文件、连接超时时间、单连接请求数上限等…...

【华为OD机试真题 C++】1038 - 全量和已占用字符集 | 机试题+算法思路+考点+代码解析

文章目录 一、题目🔸题目描述🔸输入输出🔸样例1二、代码参考作者:KJ.JK🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🍂个人博客首页: KJ.JK 💖系列专栏:华为OD机试真题(C++) 一、题目 🔸题目描述 所谓水仙花数,是指一个n位的正整数…...

网络中的网关和物联网的网关区别 局域网 路由器 交换机 服务器

网关&#xff1a;是个概念。连接两种不同的网络。例如局域网要与外部通信&#xff0c;需要经过网关。 设备和设备之间的通信&#xff0c;转换协议需要网关 路由器里有功能是对网关这个概念的实现。 所以网关它可以是路由器&#xff0c;交换机或者是PC。 路由器有网关功能&a…...

2023 年嵌入式世界的3 大趋势分析

目录 大家好&#xff0c;本文讲解了嵌入式发展的3个大趋势&#xff0c;分享给大家。 趋势#1 – Visual Studio Code Integration 趋势#2 –支持“现代”软件流程 趋势 #3 – 在设计中利用 AI 和 ML 结论 大家好&#xff0c;本文讲解了嵌入式发展的3个大趋势&#xff0c;分享…...

基于 DolphinDB 机器学习的出租车行程时间预测

DolphinDB 集高性能时序数据库与全面的分析功能为一体&#xff0c;可用于海量结构化数据的存储、查询、分析、实时计算等&#xff0c;在工业物联网场景中应用广泛。本文以纽约出租车行程时间预测为例&#xff0c;介绍如何使用 DolphinDB 训练机器学习模型&#xff0c;并进行实时…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

WebRTC调研

WebRTC是什么&#xff0c;为什么&#xff0c;如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...

在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南

在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...

机器学习的数学基础:线性模型

线性模型 线性模型的基本形式为&#xff1a; f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法&#xff0c;得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...