基于http的通信协议――SHCP

 

概述

SHCP――Simple HTTPCommunication Protocol,简单的HTTP通信协议。

本文章提供一种设计思路,能够设计基于http协议的通信层,从安全性、灵活性与数据传输性能上提供一种设计的参考。

在以下场合,本文能提供较好的参考价值:

l  开放平台接口:业界巨头都在做。

l  企业系统服务集成:企业级常见的需求。

l  跨硬件平台统一服务:采用同一套后台通信协议支持PC,移动等设备调用。

 

关于http通信的名称解析

URL参数

是指直接放在url地址上的参数,例如:http://www.123.com?p1=123&p2=abc其中p1p2就是URL参数。

注:只是方便说明在开发过程中的常见概念,该名称并非http报文协议的正式名称。

POST内容

是指存放在http协议报文体中的内容。

注:只是方便说明在开发过程中的常见概念,该名称并非http报文协议的正式名称。

ContentType

用来说明POST内容的类型,http协议分请求和响应两个阶段,并且请求时的ContentType与响应时的ContentType不一定相同,例如:

请求时ContentType= application/x-www-form-urlencoded

响应时ContentType=text/json

 

常见的http调用需求

因为http协议的特殊性,区分了URL参数和POST内容,根据这两种传参方式,能够组合出多中调用传参情况

需求情景

传参方式

返回

通过http调用进行业务调用

URL参数,GET

业务数据(XML/JSON)

URL参数+POST内容,POST

通过http调用加载文件

URL参数,GET

文件流

URL参数+POST内容,POST

通过http调用上传文件

URL参数+POST内容,POST

业务数据(XML/JSON)

 

SHCP关键参数

SHCP协议中预先设计了一些关键的URL参数。URL参数均以下滑线先开头,避免与其他业务URL参数冲突。

_lic――license,授权号

用于调用前的授权验证,URL参数,必须,示例:_lic=1001

 

_valid――validation,验证号

用于请求方的参数签名和响应方的签名验证,该参数不会在URL参数出现。

 

_t――timeStamp,时间戳

用于参数签名验证以及解决浏览器端缓存问题,URL参数,必须,格式:yyyyMMddHHmmss,示例:_t=20131231235959

 

_sign,参数签名

内容是MD5加密字符串,签名生成规则在后文说明,URL参数,必须,示例:_sign={MD5字符串}

 

_sm――signMode,签名方式

用于说明参数签名的验证方式,数值必须是:123URL参数,可选,缺省为1,示例:_sm=3

signMode数值说明:

l  1代表只签名URL参数

l  2代表只签名POST内容

l  2代表同时签名URL参数和POST内容

 

_v――version,版本号

SHCP协议版本号,URL参数,可选,缺省为1.0,示例:_v=1.0

 

SHCPPOST内容

SHCPPOST内容支持业务数据、文件流2大类型数据。

业务数据:一般是XML/JSON,自定义的业务数据格式。

文件流:二进制文件数据流,用于加载/上传文件。

 

POST内容与ContentType的对应关系如下:

POST内容

ContentType

业务数据

zip/data

文件流

application/octet-stream

 

zip/data如何生成?

把业务数据进行zip压缩,并建立一个压缩节点名称为datazip数据流,这样做的目的是减少数据传输量。

下图是解压效果,里面的data存储的就是业务数据。

 

SHCP的安全控制策略

一个http请求接入时要进行的验证如下图:

访问授权验证

URL参数_lic值的有效性进行验证,_lic_valid一般是服务处理方提供,相当于用户/密码的概念。

参数签名验证

URL参数_sign值的有效性进行验证,用于防止数据传输过程中被拦截篡改,_sign的生成校验规则请看下文。

业务数据验证

具体业务级别的验证,根据接口的业务特性进行业务校验。

 

SHCP的参数签名

参数签名_sign的生成

MD5字符串的准备

MD5字符串

生成规则

示例

URL参数MD5字符串

排除关键URL参数_lic_t_v_sign_sm后,对所有业务URL参数按参数名称进行字符串升序排序,每个参数值去掉前后空白,然后把参数值作字符串拼接,拼接后的字符串采用UTF8编码生成MD5字符串。

业务URL参数:

api=Sys.Login&user=hunk&pwd=123

排序拼接后的字符串:

Sys.Login123hunk

UTF8编码生成MD5字符串:

 

POST内容MD5字符串

POST内容是二进制数据流时直接生成MD5字符串。

POST内容是字符串数据时采用UTF8编码生成MD5字符串。

 

_sign的生成规则

签名方式

规则

1

字符串拼接:_lic + _valid + URL参数MD5 + _t

2

字符串拼接:_lic + _valid + POST内容MD5 + _t

3

字符串拼接:_lic + _valid + URL参数MD5 + POST内容MD5 + _t

_sign = 对拼接字符串采用UTF8编码生成MD5字符串

 

参数签名_sign的校验

服务方接收数据后,按上述规则生成_sign2,然后比较_sign_sign2的值,一致时说明接收的数据是合法的,能通过校验。

 

SHCP的异常处理

异常的返回

异常发生的情景

返回

 

通过http调用进行业务调用发生异常

XML

<error>

<code>{错误码}</code>

<message>{错误描述}</message>

<!-- 可以加入自定义的节点 -->

</error>

JSON

{

error:{

code:”{错误码},

message:”{错误描述}”

//可以加入自定义的属性值

}

}

 
 

通过http调用加载文件发生异常

HTTP 404

 

通过http调用上传文件

与通过http调用进行业务调用发生异常一致

 

 

错误码与描述

错误码

错误描述

说明

11

缺少授权码参数

缺少_lic参数

12

无效的授权码

_lic被验证不存在

13

缺少签名参数

缺少_sign参数

14

无效的签名

_sign被验证错误

15

缺少时间戳参数

缺少_t参数

16

无效的时间戳参数

_t参数格式不是yyyyMMddHHmmss

错误码100以上时用于业务级别错误处理。

 

基于FPGA的数字钟设计可以通过以下步骤实现: 1. 确定时钟信号源:使用实验平台晶体振荡器提供的50MHz时钟信号作为时钟信号源。 2. 时钟分频:使用分频器将50MHz时钟信号分频,输出500Hz、1KHz和1Hz三种备用信号。 3. 数码管显示模块:使用数码管显示模块来显示时间。该模块需要将seg和sel数据进行串并转换,并使用系统时钟四分频得到的shcp时钟进行驱动。需要产生stcp、shcp、ds、oe四个信号对74HC595进行控制。其中,ds为串行数据,shcp为移位寄存器时钟,stcp为存储寄存器时钟,oe为存储寄存器数据输出使能信号。 4. 时钟模块:使用时钟模块来控制时间的变化。该模块需要使用分频后的备用信号来计时,并将计时结果传递给数码管显示模块进行显示。 5. 按键模块:使用按键模块来设置时间。该模块需要检测按键输入,并将输入的时间信息传递给时钟模块进行设置。 ```verilog // Verilog代码示例 // 时钟分频模块 module clk_divider( input clk, // 时钟信号源 output reg clk_500hz, // 500Hz备用信号 output reg clk_1khz, // 1KHz备用信号 output reg clk_1hz // 1Hz备用信号 ); reg [23:0] cnt = 0; // 计数器,用于计时 always @(posedge clk) begin cnt <= cnt + 1; // 计数器加1 if (cnt == 49999) begin // 500Hz cnt <= 0; clk_500hz <= ~clk_500hz; // 取反输出 end if (cnt == 24999) begin // 1KHz clk_1khz <= ~clk_1khz; // 取反输出 end if (cnt == 49999999) begin // 1Hz cnt <= 0; clk_1hz <= ~clk_1hz; // 取反输出 end end endmodule // 数码管显示模块 module led_display( input clk, // 时钟信号源 input [13:0] seg_sel_data, // seg和sel数据 output reg [7:0] led_data, // 数码管数据 output reg stcp, // 存储寄存器时钟 output reg shcp, // 移位寄存器时钟 output reg ds, // 串行数据 output reg oe // 存储寄存器数据输出使能信号 ); reg [13:0] data_reg = 0; // 数据寄存器,用于存储seg和sel数据 reg [7:0] led_reg = 0; // 数码管寄存器,用于存储数码管数据 always @(posedge clk) begin // 将seg和sel数据存入数据寄存器 data_reg <= seg_sel_data; // 将数据寄存器中的数据存入数码管寄存器 led_reg <= {data_reg[13], data_reg[11:8], data_reg[7:4], data_reg[2:0]}; // 将数码管寄存器中的数据输出到数码管 led_data <= led_reg; // 控制74HC595 stcp <= 1'b0; shcp <= 1'b0; ds <= 1'b0; oe <= ~rst; // 将复位信号取反的值赋给oe信号 #1; // 延时1个时钟周期 stcp <= 1'b1; #1; shcp <= 1'b1; ds <= 1'b1; end endmodule // 时钟模块 module clock( input clk, // 时钟信号源 input rst, // 复位信号 input [1:0] set_time, // 设置时间 output reg [7:0] led_data, // 数码管数据 output reg stcp, // 存储寄存器时钟 output reg shcp, // 移位寄存器时钟 output reg ds, // 串行数据 output reg oe // 存储寄存器数据输出使能信号 ); reg [23:0] cnt = 0; // 计数器,用于计时 reg [5:0] sec = 0; // 秒 reg [5:0] min = 0; // 分 reg [4:0] hour = 0; // 时 always @(posedge clk) begin if (rst) begin // 复位 cnt <= 0; sec <= 0; min <= 0; hour <= 0; end else begin cnt <= cnt + 1; // 计数器加1 if (cnt == 49999) begin // 500Hz cnt <= 0; sec <= sec + 1; // 秒加1 if (sec == 60) begin // 分钟加1 sec <= 0; min <= min + 1; if (min == 60) begin // 小时加1 min <= 0; hour <= hour + 1; if (hour == 24) begin // 一天结束,小时清零 hour <= 0; end end end end end // 根据设置的时间更新时钟 case (set_time) 2'b00: begin // 设置小时 hour <= hour + 1; if (hour == 24) begin hour <= 0; end end 2'b01: begin // 设置分钟 min <= min + 1; if (min == 60) begin min <= 0; end end 2'b10: begin // 设置秒钟 sec <= sec + 1; if (sec == 60) begin sec <= 0; end end default: begin // 不设置时间 end endcase // 将时钟数据传递给数码管显示模块 led_data <= {7'b0000001, hour[4:0], min[5:0], sec[5:0]}; stcp <= 1'b0; shcp <= 1'b0; ds <= 1'b0; oe <= ~rst; // 将复位信号取反的值赋给oe信号 #1; // 延时1个时钟周期 stcp <= 1'b1; #1; shcp <= 1'b1; ds <= 1'b1; end endmodule // 按键模块 module key( input clk, // 时钟信号源 input rst, // 复位信号 input [1:0] key_data, // 按键数据 output reg [1:0] set_time // 设置时间 ); reg [1:0] key_reg = 2'b00; // 按键寄存器,用于存储按键数据 always @(posedge clk) begin if (rst) begin // 复位 key_reg <= 2'b00; end else begin key_reg <= key_data; // 将按键数据存入按键寄存器 end // 根据按键设置时间 case (key_reg) 2'b01: begin // 按下第一个按键,设置小时 set_time <= 2'b00; end 2'b10: begin // 按下第二个按键,设置分钟 set_time <= 2'b01; end 2'b11: begin // 同时按下两个按键,设置秒钟 set_time <= 2'b10; end default: begin // 没有按键按下,不设置时间 set_time <= 2'b11; end endcase end endmodule // 顶层模块 module top( input clk, // 时钟信号源 input rst, // 复位信号 input [1:0] key_data, // 按键数据 output reg [7:0] led_data, // 数码管数据 output reg stcp, // 存储寄存器时钟 output reg shcp, // 移位寄存器时钟 output reg ds, // 串行数据 output reg oe // 存储寄存器数据输出使能信号 ); wire clk_500hz, clk_1khz, clk_1hz; wire [13:0] seg_sel_data; reg [1:0] set_time = 2'b11; clk_divider clk_divider_inst( .clk(clk), .clk_500hz(clk_500hz), .clk_1khz(clk_1khz), .clk_1hz(clk_1hz) ); led_display led_display_inst( .clk(clk_500hz), .seg_sel_data(seg_sel_data), .led_data(led_data), .stcp(stcp), .shcp(shcp), .ds(ds), .oe(oe) ); clock clock_inst( .clk(clk_1hz), .rst(rst), .set_time(set_time), .led_data(led_data), .stcp(stcp), .shcp(shcp), .ds(ds), .oe(oe) ); key key_inst( .clk(clk_1khz), .rst(rst), .key_data(key_data), .set_time(set_time) ); assign seg_sel_data = {8'b11111111, 6'b000000, 1'b1, 1'b1, 1'b1, 1'b1, 1'b1, 1'b1};// 数码管段
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值