网站怎么弄,Xammp安装Wordpress,自己做链接网站,企业网站的切片怎么做文章目录 协议定制#xff0c;序列化和反序列化应用层网络版计算器协议的定制序列反序列化序列化未复用版 反序列化 TCP是面向字节流的#xff0c;你怎么保证#xff0c;你读取上来的数据#xff0c;是‘’一个“ “完整””的报文呢#xff1f; 我们没有区分字符串里面有… 文章目录 协议定制序列化和反序列化应用层网络版计算器协议的定制序列反序列化序列化未复用版 反序列化 TCP是面向字节流的你怎么保证你读取上来的数据是‘’一个“ “完整””的报文呢 我们没有区分字符串里面有什么内容连字符串都没有区分我给你发了个你好服务器这里read就一定能读到个你好 吗
对方发过来因为网络问题只发过来一个你而已但是之前并没有这个问题啊那是因为网络好数据量也不大。
就像之前管道写了好几次但是读端把数据一下子全读上来了。 为什么觉得文件操作恶心文件部分也和协议有关。 比如我把数据写到文件里可能连续写了十几条消息写好写可是读的时候里面有字符串有整数当我读取时比较困难的把数据反向转换回来且读的时候可能人家写了十几次我要读我调用一个fread把整个文件全都读进来了此时你怎么保证你读上来的就是一个完整的拿到一段你想要的数据呢 这个其实比较困难因为这里有知识的残缺必须解决。 那我按行读取不就完了吗按行读取仅仅是对文件一种读取方式那我一大堆二进制数据你怎么保证读上来了关键字段呢
正确对文件读写对管道读写对网络读写尤其是面向字节流你怎么保证你读上来的是一个完整的报文呢
则今天服务器的read是有BUG的。 我们今天拷贝字符串从C到S可是服务器就是从接受缓冲区里读造成接受缓冲区快被打满了此时客户端发数据可能只发了一部分这种情况
作为用户只需要在应用层通过write把数据拷贝到TCP发送缓冲区里write调用直接返回这个函数调用完了可是这个数据不一定已经发给对方接受缓冲区 数据不一定发送到网络里所以write ,read,send , recv这样的接口不叫网络收发实际上他的作用就是用户到内核拷贝而真正决定网络收发的协议是由TCP协议决定的因为只有TCP协议比较了解网络当前的健康状态和接收方的接受能力所以必须由TCP全权负责 1.什么时候发? 2.发多少? 3.出错了怎么办
TCP协议是不是在OS内部实现的 属于OS编译好的一部分。 所以OS属于内核你所谓的用户把数据交给TCP这句话有点不太可信实际上是用户把自己的数据交给了OS了那你放心OS吗当年讲文件操作时其实是把用户的数据拷贝到文件的缓冲区里最后由OS来把文件缓冲区的数据定期刷新到磁盘如果你不相信OS为什么当年你要相信呢 所以用户把数据交给TCP是用户把自己的数据交给了OS 所以文件操作叫log.tx今天的文件就叫做TCP。 此时把数据从用户层拷贝到内核你就别管了作为用户直接返回就行了OS会决定定期发多少。
发送谈完了再来谈谈接受这时候就有点搞笑了怎么搞笑呢 作为接收方来讲他要通过read调用说白了是把接受缓冲区的数据拷贝到用户层缓冲区buffer[] 所以对方给我发了多少数据什么时候发的有没有把完整的数据一次全打包还是分三份打包过来 接受缓冲区接受到的数据完全由OS决定了对方发的时候可能把整个字符串分了三份发过来也可能整体打包发过来甚至把发了三四次写的数据最后打包整体发过来了。 假设读的时候一次全读完有时候还读不完。 假设把缓冲区一次全读完到底把数据有没有读到在应用层是一个我所认识的完整的报文是一个字符串的一部分还是读上来三四个报文我们这里就完全不确定了所以要对读上来数据进行更细节的分析。
所以在应用层我们要清楚我们就必须得保证在应用层把协议定好我们才能更好的进行读上来的数据的分析。
协议定制序列化和反序列化
我规定好通信双方使用固定大小的报文比如发送端是64字节我在read返回值小于64字节这个报文就不完整我就把报文继续维持起来当我下次继续读的时候一定要凑够64字节然后进行处理。
相当于把文件固定大小每次写一行读也读一行也叫订协议。
应用层
网络版计算器
方案一 发送“11”字符串然后以类似方式返回 这样不太好如果一下发了三四个请求该如何区分呢
方案二
定义请求结构体直接把结构体大小字节的二进制数据发给对方对方使用同样类型的结构体就可以提取出整数 a和整数 b结果也是结构体处理 通信双发不发字符串直接发对象可以吗 这是肯定可以的。但是在应用层不建议。 问题
同一个结构体先后在不同的编译器下编译出来的同一个结构体的大小一定是一样吗 在内核层这样搞没问题因为不管Windows还是Linux你们用的协议都叫TCP/IP协议。 可是用户应用层的编译器就千差万别了编译出来的大小可能就是不一样的因为有结构体对齐他按8他按4大小就不一样。
比如说网络带宽特别差接收方接受缓冲区已经被打满了接收方给发送方说我来不及接收了先别发所以TCP发送方就不发了可是用户不知道他就一直拷到TCP发送缓冲区里四五个报文全在TCP缓冲区里后来接收方说我好了你发吧 我可以接受很大数据TCP把四五个请求全给他发过去了四五个请求是纯二进制的那你怎么去区分一个一个报文呢
我们该如何设计网络版本计算器的协议 协议 结构体 整个结构体当中每一个结构体每一个字段都要让客户端和服务端约定好的。 约定好之后使用结构化的方式把约定表达出来这就叫做定义出来了协议。
协议的定制
我们在微信聊天的时候约定通用结构体这就叫定了聊天协议。
序列反序列化
我们在网络通信的时候整个结构化的数据把多个字符串转化成一个字符串这个过程称之为序列化。 把一个字符串打散成多个字符串转换成结构体化数据叫做反序列化
我们最终为什么要序列反序列化呢主要是序列反序列化之后方便网络进行收和发。
那发过去的一个大字符串收过来一个字符串我怎么保证把字符串收全呢 后面还要设计报文和报文之间的分隔符。
所以方案一和方案二我们两个都用了。
不把多字符串整个成一个那一个一个发昵称啊内容啊时间啊对端还要把多个字符串关联起来难度太大。 我们要把创建套接字绑定监听accept,connect全部封装成一个类Sock,不然写到Tcpserver类里面就很混乱。 这样Tcpserver调用起来逻辑很清晰。 客户端同理 让客户端和服务器双方使用同一套接口就行不用来回写车轱辘代码了。 从此往后我们再也不写套接字了因为我们有了小组件Socket.hpp的类Sock 前面说了要定制协议就是设计Request和Response结构体再把结构化的数据序列化成一个大字符串
我们的请求计算器可以从 int x int y char op 变为 字符串 “x op y”
接受缓冲区收到这种风格的字符串也能解析可是对方发来了多个这样的字符串上层通过调用read读取字符串的时候他怎么保证上层读上来的是一个完整的报文呢 有没有他读上来的是半个一个半两个。 如果你读上来的是两个你怎么把这两个区分开 如果你读上来的是一个半你怎么证明一个半当中哪一个是当前的哪一个是下一个。 如果读上来是半个呢你怎么知道报文多长呢
请求和请求之间用特殊符号把他们分隔开 当上层读的时候碰到\n字符\n之前的所有字符全部都收到了就可做到报文和报文之间隔开了。
假设把缓冲区全部读上来之后的问题就是字符串分析 把整个报文用\n分开左边一个报文右边一个报文。 不一定非得用\n你可以用\3或者绝不会在报文中出现的分隔符 计算器场景比较简单用\n分割一个个报文这样我们再做字符串分析绝对可以分出一个一个子串然后再对子串切割绝对能恢复成结构体
这样做有点简单另一方面还是不够完善因为有可能读上来的字符根本不是你想的那样如果缓冲区只有半个报文呢你怎么保证你读上来的是一个完整的报文呢
有人说我只要判断有没有\n就可以了没毛病这样肯定可以的不过今天我还想加一个东西我想在报文前再加一个字段长度
这个长度字段可以固定大小也可以是字符串 这个长度是整个完整报文的长度是多少这个长度不包括\n 我们还可以添加一个协议类型报头让计算器支持浮点数或者整形计算。
今天就不这么复杂带这个协议类型了
最终的报文字符串风格就是这样子 将来读取的这一方他可以一直读到\n就可以把第一个字段读上来也就是读到了第一个报文的长度。 也可以把全部缓冲区的报文都读上来读上来之后根据第一个\n分析清楚是9就知道第一个报文的长度从第一个\n往后连续读取9个字符我就能保证读到了一个完整的报文了。 100 200 \n这个\n一会再说。 后续字符串同理处理。 有人说200 后面的\n 要不要其实都无所谓了因为已经有长度了为什么还要带呢今天把他带上未来想调试的话printf cout 直接打出来就会变成如下的样子。 很直观就能看到协议定制效果。
序列化
未复用版
请求“len\nx op y y后面的\n其他地方加
反序列化
我们除了要解决一个报文内部的问题这是序列化和反序列化
我们还要解决报文和报文之间的问题
课上代码因为在序列化过程中无论是客户端还是服务端都需要封装报头长度外加最后的\n所以不如在设置Encode来复用他们两个都可以加报头和尾部\n Encode 对x op y 添加报头长度\n 和 尾部\n
Decode函数 从len\nx op y\n提取有效报文 “x op y” 内部 1.查找第一个\n如果没找到直接返回说明长度报头没有就不玩了。 2.整个报文的长度应该是lenx op y的长度len长度报头本身的长度2 两个\n 对要Decode的len\nx op y\n…进行长度检查如果整个读上来的报文长度不够说明这不是一个完整的报文请求我也不玩了。 3.利用substr切出有效报文的子串 4.从package大长字符串移除此报文
一 把网络功能写出来 二 协议定好 三 把这俩货捏在一起 servercal.hpp Tcpserver 在tcpserver类中设置回调函数 涉及了bindbind的是calculato函数