【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】协议
作者:小萌新 专栏:网络 作者简介:大二学生 希望能和大家一起进步 本篇博客简介:简单介绍下协议并且设计一个简单的网络服务器 协议 协议的概念结构化数据传输序列化和反序列化网络版计算机服务端代码协议定制客户端代码服务线程执…...
零项目零科研,本科排名倒数,一战上岸上海交大电子与通信工程
笔者来自通信考研小马哥23上交819全程班学员 本科就读于哈工大(威海),本科成绩很差,专业排名62/99,没有科研,没有实验室,没有项目,连最基本大家都会参加的科技立项我四年也没有参与…...
NOIP模拟赛 T3区间
题目大意 有 n n n个数字,第 i i i个数字为 a i a_i ai。有 m m m次询问,每次给出 k i k_i ki个区间,每个区间表示第 l i , j l_{i,j} li,j到第 r i , j r_{i,j} ri,j个数字,求这些区间中一共出现了多少种不同的数字。部…...
【Python】如何用pyth做游戏脚本(太简单了吧)
文章目录 前言一、开发前景二、开发流程3.1、获取窗口句柄,把窗口置顶3. 2、截取游戏界面,分割图标,图片比较 二、程序核心-图标连接算法(路径寻找)四、开发总结五、源码总结 前言 简述:本文将以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,首先学习了多头注意力机制…...
ChatGPT在连续追问下对多线程和双重检查锁模式的理解--已经超越中级程序员
一、问: 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】华泰证券真题实战(四) 大家好,我是Maynor。相信大家和我一样,都有一个大厂梦,作为一名资深大数据选手,深知SQL重要性,接下来我准备用100天时间,基于大数据岗面试中的经典SQL题&…...
【智能电网】智能电网中针对DOS和FDIA的弹性分布式EMA(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
IDEA 创建微服务项目实例
🎈 作者:Linux猿 🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、云计算、物联网、面试、刷题、算法尽管咨询我,关注我,有问题私聊! 🎈 关注专栏:C/C++面试通关【精讲】 优质好文持续更新中……🚀🚀🚀 🎈 欢迎小伙伴们点赞👍、收藏⭐、留…...
注册苹果开发者账号的方法
在2020年以前,注册苹果开发者账号后,就可以生成证书。 但2020年后,因为注册苹果开发者账号需要使用Apple Developer app注册开发者账号,所以需要缴费才能创建ios证书了。 所以新政策出来后,注册苹果开发者账号&#…...
OpenCV2 计算机视觉应用编程秘籍:1~5
原文:OpenCV2 Computer Vision Application Programming Cookbook 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 计算机视觉 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 当别人说你没有底线…...
Domino自带的JSON校验工具
大家好,才是真的好。 JSON数据在Notes/Domino已经变得非常重要。从Domino 10开始,在LotusScript语言中就加入了对JSON数据处理功能。在管理中,我们知道,从Domino 12版本开始就支持Domino自动化配置,也是使用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…...
无线测温在线监测系统工作原理与产品选型
摘要:本文首先介绍了无线测温在线监测系统的基本工作原理以及软硬件组成,重点介绍了在线监测的无线测温技术特点。在此研究基础上,探讨了无线测温在线监测系统在实际工作场景中的应用案例,证明了其在温度检测方面的重要应用价值。…...
Nginx rewrite ——重写跳转
Nginx常见模块 http http块是Nginx服务器配置中的重要部分,代理、缓存和日志定义等绝大多数的功能和第三方模块的配置都可以放在这模块中。作用包括:文件引入、MIME-Type定义、日志自定义、是否使用sendfile传输文件、连接超时时间、单连接请求数上限等…...
【华为OD机试真题 C++】1038 - 全量和已占用字符集 | 机试题+算法思路+考点+代码解析
文章目录 一、题目🔸题目描述🔸输入输出🔸样例1二、代码参考作者:KJ.JK🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🍂个人博客首页: KJ.JK 💖系列专栏:华为OD机试真题(C++) 一、题目 🔸题目描述 所谓水仙花数,是指一个n位的正整数…...
网络中的网关和物联网的网关区别 局域网 路由器 交换机 服务器
网关:是个概念。连接两种不同的网络。例如局域网要与外部通信,需要经过网关。 设备和设备之间的通信,转换协议需要网关 路由器里有功能是对网关这个概念的实现。 所以网关它可以是路由器,交换机或者是PC。 路由器有网关功能&a…...
2023 年嵌入式世界的3 大趋势分析
目录 大家好,本文讲解了嵌入式发展的3个大趋势,分享给大家。 趋势#1 – Visual Studio Code Integration 趋势#2 –支持“现代”软件流程 趋势 #3 – 在设计中利用 AI 和 ML 结论 大家好,本文讲解了嵌入式发展的3个大趋势,分享…...
基于 DolphinDB 机器学习的出租车行程时间预测
DolphinDB 集高性能时序数据库与全面的分析功能为一体,可用于海量结构化数据的存储、查询、分析、实时计算等,在工业物联网场景中应用广泛。本文以纽约出租车行程时间预测为例,介绍如何使用 DolphinDB 训练机器学习模型,并进行实时…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...
