朋友们、伙计们,我们又见面了,本期来给大家带来应用层自定义协议相关的知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!
C 语 言 专 栏:C语言:从入门到精通
数据结构专栏:数据结构
个 人 主 页 :stackY、
C + + 专 栏 :C++
Linux 专 栏 :Linux

目录
1. 协议
前面说过,协议其实就是一种约定,我们实现的tcp通信时,都是按照字符串的方式进行发送的,那么对方收到的也是字符串,那么如果我们需要发送一些具体化的数据呢?
就比如:现在使用的这些聊天软件,我们在发送数据时,有昵称、时间、具体的消息内容,因此,在发送数据时,不仅仅是将消息内容发送过去,而是将这三样东西发送过去了,这是一种结构化的数据;
- 所以在发送类似与这种结构化字段的数据就要制定一种协议;
- 协议其实就是双方在通信时约定好的一种结构化字段;
在应用层这里我们发送时并不是直接将这个结构化的字段发送给对方:
- 因为在应用层很可能双方系统有所差异,对于结构体的计算不统一,导致数据的不准确;
- 所以,在应用层这里,我们要发送结构化的字段,必须要将结构化字段进行序列化成为字节流(“字符串”),将字节流发送给对方,对方通过反序列化将字节流转化为结构化字段;
- 序列化的目的是为了更好的网络发送,反序列化的目的是为了上层更好的对数据进行有效字段的提取;
- 序列化和反序列化的方式双方可以进行统一的约定;
2. 自定义协议
在自定协议这里我们直接实现一个网络版本的计算器来提现一下自定义协议的过程;
我们采用分模块来实现:
- Socket.hpp:对网络套接字进行封装
- TcpServer.hpp:实现Tcp的服务器
- TcpServerMain.cc:测试Tcp服务器
- TcpClientMain.cc:完成客户端
- Protocol.hpp:自定义协议
- Calculate.hpp:实现计算的业务
2.1 预备工作
既然要进行网络通信,那么就少不了需要套接字接口,前面已经写过好多次套接字的接口了,这里对套接字进行封装,将服务器和客户端各自使用的接口整合在一起,我们对封装好的套接字提供一些我们需要的接口接口;
我们之前发送数据使用的read和write,其实还有两个接口:
Socket.hpp:#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; // 读取信息 virtual void Send(std::string &send_str) = 0; // 发送信息 public: // 创建监听套接字----Server void BuildListenSocketMethod(uint16_t port, int blacklog) { CreateSocketorDie(); BindSocketorDie(port); ListenSocketorDie(blacklog); } // 创建连接套接字---Client 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; local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = INADDR_ANY; 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)



最低0.47元/天 解锁文章
3932

被折叠的 条评论
为什么被折叠?



