目录
嗨!收到一张超美的风景图,愿你每天都能顺心!
- 定义结构体来表示我们需要交互的信息;
- 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体; (将数据打包好,接收方按照规则再解包)
- 这个过程叫做 "序列化" 和 "反序列化"。
目的:理解序列化的过程
- 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——目的:防止非法写入,服务器开发最容易出现的错误,没有之一。
解决方法:"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;
- };
知识铺垫
既然会话结束,会话中的进程将被关闭,而一些重要的进程我们不希望关闭,那我们就可以使用守护进程来保护,会话关闭,目标进程也不被关闭。
返回值:成功,返回守护进程ID;失败,-1
创建守护进程条件:不能是进程组的第一个(组长),通常使用fork来保证。
使用setsid系统调用,可以创建一个新的会话,并将调用进程设置为会话的领头进程(session leader)。这个新的会话不会与任何终端相关联,也不会受到父进程的影响,因此可以独立于父进程运行。
意义:可以创建一个独立于终端和父进程的守护进程,使其能够在后台运行并且不受到外部干扰。
守护进程本质上是孤儿进程的一种,孤儿进程是其他某个会话,而守护进程是自成会话。
当然我们也有现成的守护进程接口——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(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于前后端之间的数据传输。它基于JavaScript的语法,易于阅读和编写,并且能够被多种编程语言解析和生成。(来自chatgpt)
centos 下载jsoncpp:
sudo yum install -y jsoncpp-devel
由于这是第三方的插件包,下载完成后,会自动将头文件,源码导入系统的头文件目录,源码库中。因此我们不需要-I,-L去手动添加头文件与源码地址。
基本接口使用例子如下:
- #include
- #include
- #include
- 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(底层是基于TCP协议),https。
url也就是我们俗称的网址
现在主流的是HTTPS协议,这个我们后面再学。
当我们输入一些,像 / ? +: 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现. 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
将需要转码的字符转为 16 进制,然后从右到左,取 4 位 ( 不足 4 位直接处理 ) ,每 2 位做一位,前面加上 % ,编码成%XY格式。 "+" 被转义成了 "%2B",服务端接受到请求后也就是将转义后的信息转会原文, urldecode 就是 urlencode 的逆过程;
ifstream是 C++ 标准库中用于从文件读取数据的输入流类。它继承自istream类,因此可以使用istream类提供的所有输入操作符和函数。以下是ifstream类的一些常用方法和操作:
1. 打开文件:使用 open()方法可以打开一个文件,并将文件和 ifstream对象关联起来。例如:
2. 读取数据:可以使用 >>操作符或getline()函数从文件中读取数据。例如:
3. 检查文件是否打开成功:可以使用 is_open()方法来检查文件是否成功打开。例如:
4. 关闭文件:使用 `close()` 方法可以关闭文件。关闭文件后,不再能从文件中读取数据。例如:
5. 检查文件是否到达文件末尾:可以使用 eof()方法来检查是否已经读取到文件末尾。例如:
- #include
- #include
-
- 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类,可以轻松地读取文件中的数据并进行处理。
总结:POST较GET,对于计算机小白来说,有了更多的 隐私性,但 不代表安全,因为协议安全得通过加密保证。
我们只要最基本的状态码信息就行,具体信息可以查找状态码表。