协议定制 + Json序列化反序列化

2023-11-17

协议定制 + Json序列化反序列化

1. 再谈 “协议”

1.1 结构化数据

协议是一种 “约定”,socket api的接口, 在读写数据时,都是按 “字符串” 的方式来发送接收的。如果我们要传输一些"结构化的数据" 怎么办呢?

结构化数据:

比如我们在QQ聊天时,并不是单纯地只发送了消息本身,是把自己的头像、昵称、发送时间、消息本身一起发送给别人,这种一起发送的就是结构化数据。

1.2 序列化和反序列化

  • 序列化:就是将对象转化成字节序列的过程。便于在传递和保存对象时保证对象的完整性和可传递性同时对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
  • 反序列化:就是将字节序列转化成对象的过程。便于根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。

在这里插入图片描述

  • 在socket编程的基础上,我们发现在实际生活中网络通信并不是单单发送一条消息本身,它包含了很多其他类型的数据,所以我们引入了结构化数据的概念,将这些各种类型的数据都定义在一个结构体中形成结构化数据方便被上层设置与读取。

  • 发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则把字符串转化回结构体;实现序列化和反序列化方便网络通信。

  • 那么发送方发送的结构化数据序列化成字符串,接收方收到后是怎么知道反序列化成结构化数据呢?这是因为两者间存在定址好的协议。所以协议的本质就是双方约定好的某种格式的数据,常见的就是用结构体或者类来表达。

2. 网络版计算器

我们需要实现一个服务器版的计算器,客户端把要计算的两个数和计算类型发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。

2.1 服务端

服务端创建步骤:

  1. 调用socket,创建套接字
  2. 调用bind,绑定端口
  3. 调用listen,将套接字状态设置为监听
  4. 调用accept,获取新连接
  5. 处理读取与写入的问题(重点)

2.2 协议定制

(1) 网络发送和读取的正确理解

在这里插入图片描述

客户端和服务器通信时,会调用read和write函数,它们是把数据直接发送到对端吗?不是

  • TCP协议有自己的发送缓冲区和接收缓冲区
  • 调用write本质:把用户所对应的数据拷贝到TCP的发送缓冲区
  • 调用read本质:把数据从接收缓冲区拷贝到用户层
  • 所以read和write的本质是拷贝函数
  • 把数据拷贝到TCP发送缓冲区后,剩下的数据怎么发,是由TCP决定的,所以TCP又叫做传输控制协议
  • 因为发送和接收是成对的,可以同时进行的,所以TCP协议是全双工的

综上:

  • TCP通信的本质是自己发送缓冲区的数据经过网络拷贝到对方的接收缓冲区中
  • 网络通信的本质也是拷贝

(2) 协议定制的问题

在定制协议之前先解决一个问题,之前在使用TCP协议时我们只是简单的读取,没有考虑TCP是面向字节流的,读取数据不完整的问题。这里同样存在相同的问题,如果一下子对方发送了很多报文,这些报文都堆积在TCP的接收缓冲区中,你怎么保证自己读到的是一个完整的报文呢?

我们采用这样的方式:

  • 对报文定长
  • 使用特殊符号(在报文与报文之间增加特殊符号)
  • 自描述方式(自己设计协议)

协议设计格式:

在这里插入图片描述

Protocol.hpp

#include<string>
#include<iostream>
#include<vector>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include"Util.hpp"
using namespace std;

// 给网络版本计算器定制协议
namespace Protocol_ns
{

    #define SEP " "
    #define SEP_LEN strlen(SEP)   // 绝对不能写成sizeof
    #define HEADER_SEP "\r\n"
    #define HEADER_SEP_LEN strlen("\r\n")

    // "长度"\r\n" "_x op _y"\r\n
    // "10 + 20" => "7"r\n""10 + 20"\r\n => 报头 + 有效载荷
    // 请求/响应 = 报头\r\n有效载荷\r\n
    // 请求 = 报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n

    // "10 + 20" => "7"r\n""10 + 20"\r\n
    string AddHeader(string&str)
    {
        cout<<"AddHeader 之前:\n"
            <<str<<endl;

        string s=to_string(str.size());
        s+=HEADER_SEP;
        s+=str;
        s+=HEADER_SEP;

        cout<<"AddHeader 之后:\n"
            <<s<<endl;

        return s;
    }

    // "7"r\n""10 + 20"\r\n => "10 + 20" 
    string RemoveHeader(const string&str,int len)
    {
        cout<<"RemoveHeader 之前:\n"
            <<str<<endl;

        // 从后面开始截取
        string res=str.substr(str.size()-HEADER_SEP_LEN-len,len); 

        cout<<"RemoveHeader 之后:\n"
            <<res<<endl;   

        return res;
    }

    int Readpackage(int sock,string&inbuffer,string*package)
    {
        cout<<"ReadPackage inbuffer 之前:\n"
            <<inbuffer<<endl;

        // 边读取
        char buffer[1024];
        ssize_t s=recv(sock,&buffer,sizeof(buffer)-1,0);
        if(s<=0)
            return -1;

        buffer[s]=0;
        inbuffer+=buffer;

        cout<<"ReadPackage inbuffer 之中:\n"
            <<inbuffer<<endl;

        // 边分析,  "7"r\n""10 + 20"\r\n
        auto pos=inbuffer.find(HEADER_SEP);
        if(pos==string::npos)
            return 0;
        
        string lenStr=inbuffer.substr(0,pos);    // 获取头部字符串, 没有动inbuffer
        int len=Util::toInt(lenStr);             // 得到有效载荷的长度 => "123" -> 123
        int targetPackageLen=len+2*HEADER_SEP_LEN+lenStr.size();   // 得到整个报文长度
        if(inbuffer.size()<targetPackageLen)     // 不是一个完整的报文
            return 0;
        
        *package=inbuffer.substr(0,targetPackageLen);  // 提取到了报文有效载荷, 没有动inbuffer
        inbuffer.erase(0,targetPackageLen);      // 从inbuffer中直接移除整个报文

        cout<<"ReadPackage inbuffer 之后:\n"
            <<inbuffer<<endl;

        return len;
    }

    // Request && Response都要提供序列化和反序列化功能
    // 1. 自己手写
    // 2. 用别人的 --- json, xml, protobuf

    class Request
    {
    public:

        Request()
        {

        }

        Request(int x,int y,char op)
            :_x(x)
            ,_y(y)
            ,_op(op)
        {
        }

        // 序列化: struct->string
        bool Serialize(string* outStr)     
        {
            *outStr=""; 
            string x_string=to_string(_x);
            string y_string=to_string(_y);

            // 手动序列化
            *outStr=x_string + SEP + _op + SEP + y_string;

            std::cout << "Request Serialize:\n"
                      << *outStr << std::endl;

            return true;
        }

        // 反序列化: string->struct
        bool Deserialize(const string&inStr)   
        {
            // inStr:  10 + 20 => [0]=>10, [1]=>+, [2]=>20
            vector<string> result;
            Util::StringSplit(inStr,SEP,&result);

            if(result.size()!=3)
                return false;
            if(result[1].size()!=1)
                return false;

            _x=Util::toInt(result[0]);
            _y=Util::toInt(result[2]);
            _op=result[1][0];

            return true;
        }

        ~Request()
        {
            
        }

    public:
        // _x op _y ==> 10 * 9 ? ==> 10 / 0 ?
        int _x;
        int _y;
        char _op;
    };

    class Response
    {
    public:
        Response()
        {

        }
        
        Response(int result,int code)
            :_result(result)
            ,_code(code)
        {
            
        }

        // 序列化: struct->string
        bool Serialize(string* outStr)     
        {
            // _result _code
            *outStr=""; 
            string res_string = to_string(_result);
            string code_string = to_string(_code);

            // 手动序列化
            *outStr=res_string + SEP + code_string;

            return true;
        }

        // 反序列化: string->struct
        bool Deserialize(const string&inStr)   
        {
            // 10 0
            vector<string> result;
            Util::StringSplit(inStr,SEP,&result);

            if(result.size()!=2)
                return false;

            _result=Util::toInt(result[0]);
            _code=Util::toInt(result[1]);
            return true;
        }

        ~Response()
        {

        }

    public:
        int _result;
        int _code;   // 0 success; 1,2,3,4代表不同错误码
    };

}

Util.hpp

#pragma once

#include<iostream>
#include<string>
#include<vector>
using namespace std;

class Util
{
public:
    // 输入: const &
    // 输出: *
    // 输入输出: *
    static bool StringSplit(const string &str, const string &sep, vector<string> *result)
    {
        // 10 + 20
        size_t start = 0;

        while (start < str.size())
        {
            auto pos = str.find(sep, start);
            if (pos == string::npos)
                break;

            result->push_back(str.substr(start, pos - start));

            // 更新位置
            start = pos + sep.size();
        }

        // 处理最后的字符串
        if(start<str.size())
            result->push_back(str.substr(start));

        return true;
    }

    static int toInt(const string&s)  // 字符串转整数
    {
        return atoi(s.c_str());
    }
};

2.3 客户端

客户端创建步骤:

  1. 调用socket,创建套接字
  2. 客户端不用自己bind端口
  3. 调用connect,连接服务器
  4. 处理读取与写入的问题

2.4 代码

完整的代码:lesson36 · 遇健/Linux - 码云 - 开源中国 (gitee.com)

运行结果:

在这里插入图片描述

3. Json实现序列化反序列化

3.1 简单介绍

上面是自己定制协议实现序列化和反序列化,下面我们使用一些现成的方案来实现序列化和反序列化。C++常用的:protobuf 和 json,这里使用简单的 json。

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。Json数据由键值对组成,大括号表示对象,方括号表示数组。

在这里插入图片描述

3.2 使用

  • 安装json库
yum install -y jsoncpp-devel
  • 使用json包含的头文件:
#include <jsoncpp/json/json.h>

注意makefile文件要包含Json库的名称

在这里插入图片描述

我们在使用的时候直接创建Json对象来进行序列化和反序列化

Protocol.hpp

#include<string>
#include<iostream>
#include<vector>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include"Util.hpp"
#include<jsoncpp/json/json.h>
using namespace std;

// #define MYSELF 1

// 给网络版本计算器定制协议

namespace Protocol_ns
{

    #define SEP " "
    #define SEP_LEN strlen(SEP)   // 绝对不能写成sizeof
    #define HEADER_SEP "\r\n"
    #define HEADER_SEP_LEN strlen("\r\n")

    // "长度"\r\n" "_x op _y"\r\n
    // "10 + 20" => "7"r\n""10 + 20"\r\n => 报头 + 有效载荷
    // 请求/响应 = 报头\r\n有效载荷\r\n
    // 请求 = 报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n

    // 未来: "长度"\r\n"协议号\r\n""_x op _y"\r\n
     

    // "10 + 20" => "7"r\n""10 + 20"\r\n
    string AddHeader(string&str)
    {
        cout<<"AddHeader 之前:\n"
            <<str<<endl;

        string s=to_string(str.size());
        s+=HEADER_SEP;
        s+=str;
        s+=HEADER_SEP;

        cout<<"AddHeader 之后:\n"
            <<s<<endl;

        return s;
    }


    // "7"r\n""10 + 20"\r\n => "10 + 20" 
    string RemoveHeader(const string&str,int len)
    {
        cout<<"RemoveHeader 之前:\n"
            <<str<<endl;

        // 从后面开始截取
        string res=str.substr(str.size()-HEADER_SEP_LEN-len,len); 

        cout<<"RemoveHeader 之后:\n"
            <<res<<endl;   

        return res;
    }


    int Readpackage(int sock,string&inbuffer,string*package)
    {
        cout<<"ReadPackage inbuffer 之前:\n"
            <<inbuffer<<endl;

        // 边读取
        char buffer[1024];
        ssize_t s=recv(sock,&buffer,sizeof(buffer)-1,0);
        if(s<=0)
            return -1;

        buffer[s]=0;
        inbuffer+=buffer;


        cout<<"ReadPackage inbuffer 之中:\n"
            <<inbuffer<<endl;


        // 边分析,  "7"r\n""10 + 20"\r\n
        auto pos=inbuffer.find(HEADER_SEP);
        if(pos==string::npos)
            return 0;
        
        string lenStr=inbuffer.substr(0,pos);    // 获取头部字符串, 没有动inbuffer
        int len=Util::toInt(lenStr);             // 得到有效载荷的长度 => "123" -> 123
        int targetPackageLen=len+2*HEADER_SEP_LEN+lenStr.size();   // 得到整个报文长度
        if(inbuffer.size()<targetPackageLen)     // 不是一个完整的报文
            return 0;
        
        *package=inbuffer.substr(0,targetPackageLen);  // 提取到了报文有效载荷, 没有动inbuffer
        inbuffer.erase(0,targetPackageLen);      // 从inbuffer中直接移除整个报文

        cout<<"ReadPackage inbuffer 之后:\n"
            <<inbuffer<<endl;

        return len;
    }



    // Request && Response都要提供序列化和反序列化功能
    // 1. 自己手写
    // 2. 用别人的

    class Request
    {
    public:

        Request()
        {

        }

        Request(int x,int y,char op)
            :_x(x)
            ,_y(y)
            ,_op(op)
        {

        }


        // 序列化: struct->string
        bool Serialize(string* outStr)     
        {
            *outStr=""; 
#ifdef  MYSELF
            string x_string=to_string(_x);
            string y_string=to_string(_y);

            // 手动序列化
            *outStr=x_string + SEP + _op + SEP + y_string;

            std::cout << "Request Serialize:\n"
                      << *outStr << std::endl;
#else
            Json::Value root;   // Value: 一种万能对象, 接受任意的kv类型
            root["x"]=_x;
            root["y"]=_y;
            root["op"]=_op;

            // Json::FastWriter writer;  // writer: 是用来进行序列化的 struct -> string
            Json::StyledWriter writer;

            *outStr=writer.write(root);
#endif
            return true;
        }

        // 反序列化: string->struct
        bool Deserialize(const string&inStr)   
        {
#ifdef  MYSELF
            // inStr:  10 + 20 => [0]=>10, [1]=>+, [2]=>20
            vector<string> result;
            Util::StringSplit(inStr,SEP,&result);

            if(result.size()!=3)
                return false;
            if(result[1].size()!=1)
                return false;

            _x=Util::toInt(result[0]);
            _y=Util::toInt(result[2]);
            _op=result[1][0];

#else
            Json::Value root;   
            Json::Reader reader;  // Reader: 是用来反序列化的
            reader.parse(inStr,root);

            _x=root["x"].asUInt();
            _y=root["y"].asUInt();
            _op=root["op"].asUInt();
    
#endif
            Print();

            return true;
        }

        void Print()
        {
            std::cout << "_x: " << _x << std::endl;
            std::cout << "_y: " << _y << std::endl;
            std::cout << "_z: " << _op << std::endl;
        }

        ~Request()
        {
            
        }

    public:
        // _x op _y ==> 10 * 9 ? ==> 10 / 0 ?
        int _x;
        int _y;
        char _op;

    };

    class Response
    {
    public:
        Response()
        {

        }

        
        Response(int result,int code)
            :_result(result)
            ,_code(code)
        {
            
        }


        // 序列化: struct->string
        bool Serialize(string* outStr)     
        {
            // _result _code
            *outStr=""; 
#ifdef  MYSELF
            string res_string = to_string(_result);
            string code_string = to_string(_code);

            // 手动序列化
            *outStr=res_string + SEP + code_string;

#else
            Json::Value root;   
            root["result"]=_result;
            root["code"]=_code;
            // Json::FastWriter writer;
            Json::StyledWriter writer;

            *outStr=writer.write(root);
#endif
            return true;
        }

        // 反序列化: string->struct
        bool Deserialize(const string&inStr)   
        {
#ifdef  MYSELF
            // 10 0
            vector<string> result;
            Util::StringSplit(inStr,SEP,&result);

            if(result.size()!=2)
                return false;

            _result=Util::toInt(result[0]);
            _code=Util::toInt(result[1]);

#else
            Json::Value root;
            Json::Reader reader; 
            reader.parse(inStr, root);

            _result = root["result"].asUInt();
            _code = root["code"].asUInt();
#endif
            Print();
            return true;
        }

        void Print()
        {
            std::cout << "_result: " << _result << std::endl;
            std::cout << "_code: " << _code << std::endl;
        }

        ~Response()
        {

        }

    public:
        int _result;
        int _code;   // 0 success; 1,2,3,4代表不同错误码
    };
}

完整代码:lesson36/NetCal_v2 · 遇健/Linux - 码云 - 开源中国 (gitee.com)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

协议定制 + Json序列化反序列化 的相关文章

  • 有没有办法让 Linux CLI IO 重定向持久化?

    我有多个管道命令 如下所示 find options grep options xargs grep options 它们中的每一个都可能产生我不感兴趣的错误 权限错误 文件名空格错误等 因此 我想将所有错误重定向到 dev null 我知
  • shell中基于正则表达式的颜色突出显示输出

    我想知道是否可以用颜色突出显示与某些字符串匹配的 shell 命令的输出 例如 如果我运行 myCommand 输出如下 gt myCommand DEBUG foo bar INFO bla bla ERROR yak yak 我希望所有
  • 对于客户端服务器程序,并行接收多个客户端连接请求的最佳方法是什么?

    该程序是在 Linux 上用 C 语言开发的客户端服务器套接字应用程序 每个客户端都连接到一个远程服务器并将其自身记录为在线 在任何给定时间点很可能有多个客户端在线 所有客户端都尝试连接到服务器以将自己记录为在线 忙碌 空闲等 那么服务器如
  • 在 4.x 内核上的 64 位内存中查找系统调用表

    我正在尝试编写一个简单的内核模块来查找 Linux 中的 sys call table 但遇到了一些麻烦 我在这里找到了 32 位 Linux 的基本指南 https memset wordpress com 2011 03 18 sysc
  • 检查 Linux 中给定进程的打开 FD 限制

    我最近有一个 Linux 进程 泄露 了文件描述符 它打开了文件描述符 但没有正确关闭其中一些文件描述符 如果我对此进行监控 我就可以提前得知该过程已达到其极限 有没有一种很好的 Bash 或 Python 方法来检查 Ubuntu Lin
  • 我如何知道 std::map 插入成功还是失败?

    我在多线程应用程序中有一个映射 将名为 uuid 的类映射到指针 我想知道插入操作是否成功或失败 e g mymap insert hint MyMap value type entry uuid itemptr 如果失败的话会抛出异常或者
  • 如何搭建qtwayland?

    我花了一整天的时间尝试使用QtWayland Compositor 1 0在 Qt 创建者中 我已经遵循了从那里开始的所有步骤https wiki qt io QtWayland https wiki qt io QtWayland但我收到
  • Bash 脚本错误 [重复]

    这个问题在这里已经有答案了 我想知道下面的脚本有什么错误 我收到错误为 command not foundh line 1 command not foundh line 2 其连续的 我试过添加 但现在工作请告诉我该怎么做 bin bas
  • “以下软件包将被更高优先级的频道取代”是什么意思?

    我正在尝试将 fuzzywuzzy 安装到 64 位 Linux 中的 Anaconda 发行版上 当我这样做时 它试图改变我的conda and conda env to conda forge渠道 如下 我通过以下方式在 anacond
  • Tomcat 中的 403 访问被拒绝

    我有以下内容tomcat users xml
  • 第一次如何配置postgresql?

    我刚刚安装了 postgresql 并在安装过程中指定了密码 x 当我尝试做的时候createdb并指定我收到消息的任何密码 createdb 无法连接到数据库 postgres 致命 用户密码身份验证失败 同样适用于createuser
  • AMD OpenCL 在 Linux 上工作所需的最小必要文件子集是什么?

    我已经使用 buildroot 构建了 Linux 内核 我已将开源 amdgpu 驱动程序和所需的固件合并到其中 驱动程序很好 检测 GPU 模式设置运行良好 调整 小文本 的分辨率 启动后会显示命令行 现在我需要运行 OpenCL 程序
  • 如何像C99一样使用make和编译?

    我正在尝试使用 Makefile 编译 Linux 内核模块 obj m main o all make C lib modules shell uname r build M PWD modules clean make C lib mo
  • 如何每周日运行 crontab 作业

    我想弄清楚如何每周周日运行 crontab 作业 我认为以下应该可行 但我不确定我是否理解正确 下面的说法正确吗 5 8 6 这是 crontab 格式的解释 1 Entry Minute when the process will be
  • 如何在 .zip 文件中使用 grep

    有 3 个文件 a csv b csv c csv 压缩为 abh zip 现在可以在 abh zip 上执行 grep 命令 是否有任何通配符 仅对里面的 c csv 文件运行 grep压缩 如果你有zipgrep 据我所知 它是随zip
  • 如何在Linux中获取带有图标的活动应用程序

    我想找到一种方法获取活动应用程序的列表及其名称和图标 实际上 我正在使用此命令来获取所有活动进程 wmctrl lp 示例输出 0x03800002 0 3293 user notebook XdndCollectionWindowImp
  • 64位版本的adb和fastboot?

    我在 Debian 7 3 x64 已完全修补 上发现了以下错误 我很确定这是因为adb即使在其 SDK 工具的 64 位发行版中也是 32 位 which adb opt android sdk platform tools adb op
  • 超立方体错误。非法的最小或最大规格

    尝试从这里运行示例代码http tess4j sourceforge net codesample html http tess4j sourceforge net codesample html我收到一条错误消息 Error Illega
  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • 未找到 DEADLINE 调度策略

    我想在 C 中实现 DEADLINE 调度策略 我知道该功能已实现Linux 3 14 10我正在使用 Ubuntu 14 04Linux 3 17 0 031700 lowlatency 201410060605 SMP PREEMPT这

随机推荐

  • 官宣!玖章算术获评“浙江省创新型中小企业”

    近日 浙江省工业和信息化厅开展了 2023 第二季度创新型中小企业评价工作 经企业自评 地市审核 抽查 市经信局审核评价等程序 玖章算术以优秀的自主创新能力通过认定 成为浙江省 2023 年度创新型中小企业 创新型中小企业 是指具有较健全的
  • FISCO BCOS(五)———部署安装jdk1.8

    1 将下载的jdk1 8 0 162 linux x64 tar gz通过远程连接工具 放入 usr local 目录 然后解压 2 解压 tar zxvf jdk1 8 0 162 linux x64 tar gz 3 切换到jdk1 8
  • 闲聊——集成学习理论(Adaboost,随机森林理论与个人实战中的体会)

    本文通过简单的例子来引出算法本质 同时附上证明过程 目的是让感觉直接看证明推导很难的小伙伴们也能理解集成算法是怎样实现的 集成学习通过构建并结合多个学习器来完成学习任务 可获得比单一学习器更好的泛化性能 目前的集成学习方法大致可分为两类 第
  • 程序员常用十大算法之KMP算法

    程序员常用十大算法之KMP算法 一 应用场景 二 暴力匹配算法 2 1思路分析 2 2代码实现 三 算法介绍 四 KMP算法最佳应用 4 1字符串匹配问题 4 2思路分析图解 代码实现 本文是程序员常用十大算法的第一节 后面的算法总结都在博
  • JSON 驼峰转下划线

    import com fasterxml jackson databind PropertyNamingStrategy PropertyNamingStrategyBase public class MyCamemlToUnderline
  • MAC DOCKER无法ping通容器解决方案

    原因 解决方案 原因 先来看下LINUX的docker架构 docker是在linux内核容器基础上实现的 linux安装docker后 会创建一个为docker0的虚拟网卡 linux宿机与docker容器之间的通信 通过docker0虚
  • 高三计算机教案,2017高三信息技术教学计划

    信息技术是一门操作性和实践性强的课程 应注重培养学生的动手操作实践能力 提高学生的学习兴趣 达到学习的目的 下面是学习啦小编带来关于2017高三信息技术教学计划的内容 希望能让大家有所收获 2017高三信息技术教学计划 一 一 基本情况 1
  • violate关键字---java高并发

    内存模型相关概念 计算机在执行程序时 每条指令都是在CPU中执行的 而执行指令过程中 势必涉及到数据的读取和写入 由于程序运行过程中的临时数据是存放在主存 物理内存 当中的 这时就存在一个问题 由于CPU执行速度很快 而从内存读取数据和向内
  • origin账户申请&&安装使用——画图软件

    账户申请 参考https my originlab com forum topic asp TOPIC ID 22328 学生半年免费版官方网站申请链接 用学校提供的以edu cn结尾的邮箱进行注册 https www originlab
  • const的用法

    const是一个关键字 加在变量前 将其声明为常量 简单来说 就是不希望该变量的值发生改变 因此 它必须在声明该变量时就赋初值 const与指针 如果const加在 符号前面 如 const int p a 或 int const p a
  • PHP性能优化--OPCache

    文章目录 前言 OPcache 介绍 启用 配置项说明 opcache preload预加载文件示例 删除缓存 可视化界面opcache gui 总结 参考资料 前言 随着业务的发展 性能优化成为了不可避免的课题 优化后的业务承载能力可以是
  • sh 脚本异常:/bin/sh^M:bad interpreter: No such file or directory

    权限不够 chmod x examples mnist bb sh 在 Linux 中执行 sh 脚本 异常 bin sh M bad interpreter No such file or directory 这是不同系统编码格式引起的
  • Spring不同类型的注入方式,p-namespace,c-namespace

    spring官网代码示例 1 不同类型的注入方式
  • 数据结构系列——链表linklist

    本期主题 数据结构之 链表 往期链接 数据结构系列 先进先出队列queue 数据结构系列 栈 stack 目录 1 链表定义 2 代码实现链表 1 链表定义 定义 链表由多个结点组成 结点不仅包含值 还包含到下一个结点的信息 所以通过这种方
  • 优惠券秒杀(二)

    原创 L1296686146 冗谪 2023 07 27 18 30 发表于陕西 收录于合集 redis7个 库存超卖问题分析 库存超卖问题其本质就是多个线程操作共享数据产生的线程安全问题 即当一个线程在执行操作共享数据的多条代码的过程中
  • 基于信号量的生产者-消费者

    信号量是进化版的互斥锁 互斥锁只能供一个线程使用 信号量可以供多个线程使用 如果希望在多个线程之间对某对象的部分数据共享 互斥锁无法实现 只能将整个数据锁住 这样导致线程并发性下降 信号量既能保持同步 数据又不混乱 又能提高线程并发 主要函
  • Qt

    Qt 信号和槽之间的连接与使用 重载信号和槽的连接 测试环境 Qt Creator 5 14 2 MinGW 7 3 1 信号和槽 在Qt中 QObject是所有的Qt类的基类 如果想要自己实现一个C 类 并且还需要支持信号和槽 那么需要在
  • 用java计算输入工资计算税收_标准作业

    课后作业 第一章 理论 1 java环境搭建的步骤 2 java语言的简介 3 手写代码实现个人信息的输出 姓名 性别 年龄 家庭地址 爱好 座右铭 上机 分别使用记事本和myeclipse编写java程序实现求学经历的输出并写好每行代码的
  • 医学图像的CT值与像素值总结及转换代码

    目录 一 CT图像的调窗 1 Window width 2 Window level center 二 DICOM文件中窗宽窗位对应字段 三 CT值与像素值转换 线性映射 1 itk snap软件和sitk代码示例 2 Mango软件和ni
  • 协议定制 + Json序列化反序列化

    文章目录 协议定制 Json序列化反序列化 1 再谈 协议 1 1 结构化数据 1 2 序列化和反序列化 2 网络版计算器 2 1 服务端 2 2 协议定制 1 网络发送和读取的正确理解 2 协议定制的问题 2 3 客户端 2 4 代码 3