博客主页:花果山~程序猿-CSDN博客
关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长!
目录
嗨!收到一张超美的风景图,愿你每天都能顺心!
一, 再谈协议
- 定义结构体来表示我们需要交互的信息;
- 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体; (将数据打包好,接收方按照规则再解包)
- 这个过程叫做 "序列化" 和 "反序列化"。
网络版计算器小项目
目的:理解序列化的过程
namespace Ser_Change
{
// 负责数据正反序列化
class DataConversion
{
public:
DataConversion(): _a(0), _b(0), _synbol(""){}
DataConversion(int a, int b, std::string sy)
: _a(a), _b(b), _synbol(sy) {}
// 转序列化 “1 + 1”
std::string serialize()
{
std::string tmp;
tmp += std::to_string(_a);
tmp += " ";
tmp += _synbol;
tmp += " ";
tmp += std::to_string(_b);
return move(tmp);
}
// 反序列化 从"1 + 2"提取数据
bool Disserialize(const std::string &str)
{
size_t left = str.find(" ");
size_t tail = str.rfind(" ");
if (left != str.npos && tail != left)
{
_a = atoi(str.substr(0, left).c_str());
_synbol = str.substr(left + 1, 1);
_b = atoi(str.substr(tail + 1, str.size()).c_str());
return 1;
}
else
{
return false;
}
}
private:
int _a;
int _b;
std::string _synbol;
};
编码经验:
服务器端一般都会屏蔽13号信号,SIGPIPE——目的:防止非法写入,服务器开发最容易出现的错误,没有之一。
2. 长数据分割优化
解决方法:"5\r\n1 + 1\r\n6\r\n20 + 3\r\n"这样两数据就能分开了。
简单实现其提取信息序列化与反序列化,代码:
class DataConversion
{
public:
DataConversion(): _a(0), _b(0), _synbol(""){}
DataConversion(int a, int b, std::string sy)
: _a(a), _b(b), _synbol(sy) {}
// 使用更加规范的协议:
// “lenght\r\n123+4321\r\n”
// 在客户端高并发的情况下,对服务端socket缓冲区满载打入,服务端可以一次接收数据
// 转序列化 “1 + 1” 转换
std::string serialize()
{
std::string tmp;
tmp += std::to_string(_a);
tmp += SPACE;
tmp += _synbol;
tmp += SPACE;
tmp += std::to_string(_b);
std::string data;
data += std::to_string(tmp.size());
data += DECOLL;
data += tmp;
data += DECOLL;
return move(data);
}
// 反序列化 "1 + 2"
std::string Disserialize(std::string &str)
{
size_t left = str.find(DECOLL);
size_t tail = str.find(DECOLL, left + DEC_LENGTH);
// “lenght\r\n123 + 4321\r\n”
if (left != str.npos && atoi(str.substr(0,left).c_str())
== tail - left - DEC_LENGTH)
// 检测数据包,是否包含1个完整包
{
size_t left_data = str.find(SPACE);
size_t tail_data = str.rfind(SPACE, tail);
_a = atoi(str.substr(left + DEC_LENGTH, left_data - left -
DEC_LENGTH).c_str());
_synbol = str.substr(left_data + SPACE_SIZE, SPACE_SIZE);
_b = atoi(str.substr(tail_data + SPACE_SIZE, tail - tail_data -
SPACE_SIZE).c_str());
std::string surplus = str.substr(DEC_LENGTH);
str.erase(0, tail + DEC_LENGTH);
return move(surplus); // 返回拷贝后的剩余字段
}
else
{
return "";
}
}
// private:
int _a;
int _b;
std::string _synbol;
};
二,守护进程
知识铺垫
既然会话结束,会话中的进程将被关闭,而一些重要的进程我们不希望关闭,那我们就可以使用守护进程来保护,会话关闭,目标进程也不被关闭。
1. setsid
返回值:成功,返回守护进程ID;失败,-1
创建守护进程条件:不能是进程组的第一个(组长),通常使用fork来保证。
使用setsid系统调用,可以创建一个新的会话,并将调用进程设置为会话的领头进程(session leader)。这个新的会话不会与任何终端相关联,也不会受到父进程的影响,因此可以独立于父进程运行。
意义:可以创建一个独立于终端和父进程的守护进程,使其能够在后台运行并且不受到外部干扰。
守护进程本质上是孤儿进程的一种,孤儿进程是其他某个会话,而守护进程是自成会话。
2. 制作守护进程函数
当然我们也有现成的守护进程接口——daemon,但在大多数情况下,我们则会更多的自己实现,代码如下:
void MyDaemon()
{
// 1.屏蔽影响信号,SIGPIPE SIGCHIL
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
// 2.fork, 确保自身不是组长
pid_t pd = fork();
if (pd > 0)
exit(0);
// 3.setsid,设置守护进程
setsid(); //设置自己为单独会话
int devnull = open("dev/null", O_RDONLY | O_WRONLY);
// 4.将输入,输出,错误重定向到 /dev/null(垃圾黑洞,就是用来丢弃数据的)
if (devnull)
{
dup2(devnull, 0);
dup2(devnull, 1);
dup2(devnull, 2);
}
}
说到这里,我们是否会想,我们自主实现的数据的序列化,反序列化真的没问题吗??答案是肯定的,肯定有问题,不过我们可以通过自己编写来更好的理解协议。下面我们就要了解比较成熟的数据转换方案——json
三,json
简单了解:JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于前后端之间的数据传输。它基于JavaScript的语法,易于阅读和编写,并且能够被多种编程语言解析和生成。(来自chatgpt)
centos 下载jsoncpp:
sudo yum install -y jsoncpp-devel
由于这是第三方的插件包,下载完成后,会自动将头文件,源码导入系统的头文件目录,源码库中。因此我们不需要-I,-L去手动添加头文件与源码地址。
基本接口使用例子如下:
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
using namespace std;
int main()
{
std::string fastmessage;
// 序列化
{
Json::Value send;
send["a"] = 1;
send["b"] = 2;
send["op"] = '+';
//还满足啊嵌套使用
Json::Value send2;
send2["a"] = 1;
send2["b"] = 2;
send2["op"] = "+";
send["send2"] = send2;
Json::StyledWriter writer;
//1.产生格式型的数据,如
// {
// "a" : _a
// "b" : _b
// "op" : _synbol
// }
// 更适合dubug时使用
std::string message = writer.write(send);
Json::FastWriter fastwriter;
//2.产生类似数组的数据格式,可读性偏差些,如:
// {"a":_a,"b":_b, "op":_synbol,{"a":_a,"b":_b, "op":_synbol}}
fastmessage = fastwriter.write(send);
std::cout << message << std::endl;
std::cout << fastmessage << std::endl;
}
{
//反序列化
string str;
Json::Value accept;
Json::Reader reader;
reader.parse(fastmessage, accept);
int _a = accept["a"].asInt();
int _b = accept["b"].asInt();
char _op = accept["op"].asInt(); //char类型本质上是整数
printf("_a:%d _b:%d _op:%c\n", _a, _b, _op);
}
return 0;
}
四,http协议
应用层的协议是可以我们自己编写,但目前有比较成熟的协议如,http(底层是基于TCP协议),https。
1. URL
url也就是我们俗称的网址
现在主流的是HTTPS协议,这个我们后面再学。
2. 特殊字符的转义
当我们输入一些,像 / ? +: 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现. 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
将需要转码的字符转为 16 进制,然后从右到左,取 4 位 ( 不足 4 位直接处理 ) ,每 2 位做一位,前面加上 % ,编码成%XY格式。 "+" 被转义成了 "%2B",服务端接受到请求后也就是将转义后的信息转会原文, urldecode 就是 urlencode 的逆过程;
3. HTTP协议格式
![](https://i-blog.csdnimg.cn/blog_migrate/ebe8c5ae161f63b739f9bc88c0c6f7bd.png)
![](https://i-blog.csdnimg.cn/blog_migrate/60554e67716c180f5d0ed597c3909e24.png)
1)、ifstream——文件流
ifstream是 C++ 标准库中用于从文件读取数据的输入流类。它继承自istream类,因此可以使用istream类提供的所有输入操作符和函数。以下是ifstream类的一些常用方法和操作:
1. 打开文件:使用 open()方法可以打开一个文件,并将文件和 ifstream对象关联起来。例如:
2. 读取数据:可以使用 >>操作符或getline()函数从文件中读取数据。例如:
3. 检查文件是否打开成功:可以使用 is_open()方法来检查文件是否成功打开。例如:
4. 关闭文件:使用 `close()` 方法可以关闭文件。关闭文件后,不再能从文件中读取数据。例如:
5. 检查文件是否到达文件末尾:可以使用 eof()方法来检查是否已经读取到文件末尾。例如:
#include <iostream>
#include <fstream>
int main() {
std::ifstream file("example.txt", std::ifstream::in); // 以读的形式,打开名为example.txt的文件
if (file.is_open()) { // 检查文件是否成功打开
std::string line;
while (std::getline(file, line)) { // 逐行读取文件内容
std::cout << line << std::endl; // 输出到控制台
}
file.close(); // 关闭文件
} else {
std::cout << "无法打开文件" << std::endl;
}
return 0;
}
总的来说,ifstream类提供了一种方便的方式来从文件中读取数据,并且具有与istream类相同的输入功能。通过使用 ifstream类,可以轻松地读取文件中的数据并进行处理。
五,Http协议内容
1. 请求方法
![](https://i-blog.csdnimg.cn/blog_migrate/dfd0484a1834efeb20a2720724e4fb53.png)
![](https://i-blog.csdnimg.cn/blog_migrate/471d3a85bcb0550988a45a127b1c5504.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6848f80de055c43439cbb0e82cd21ce3.png)
总结:POST较GET,对于计算机小白来说,有了更多的 隐私性,但 不代表安全,因为协议安全得通过加密保证。
2. HTTP状态码
我们只要最基本的状态码信息就行,具体信息可以查找状态码表。
![](https://i-blog.csdnimg.cn/blog_migrate/7a2dc47330de5f1ee732fcf3eda93016.png)
301永久重定向 & 302,307临时重定向区别
![](https://i-blog.csdnimg.cn/blog_migrate/870e23a196c5c3647ad94158615d5fe3.png)
3. HTTP常见的header
- Content-Type: 数据类型(根据你请求的文件名后缀来判断,如text/html等)
- Content-Length: Body的长度(资源长度)
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- referer: 当前页面是从哪个页面跳转过来的(上一次的旧页面地址)
- location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能
![](https://i-blog.csdnimg.cn/blog_migrate/0f567f0816e0a08309df94911bf55f1f.png)
Connection
![](https://i-blog.csdnimg.cn/blog_migrate/ca35a090501e85a09664cf5ce3c2867d.png)