Commit 920f5266 by 杜祥龙

更新代码和说明

Signed-off-by: sdvdxl杜龙少 <sdvdxl@163.com>
parent 87481f59
.vscode .vscode
.idea .idea
a.out
a.out.dSYM
\ No newline at end of file
CC=gcc CC=gcc
CFLAGS=-Wall -Wextra -g -O0 CFLAGS=-Wall -Wextra -g -O0
TARGET=a.out
SRCS=$(wildcard *.c) SRCS=$(wildcard *.c)
LDFLAGS=-lm
ifeq ($(OS),Windows_NT)
LDFLAGS+=-lws2_32
TARGET=a.exe
else
TARGET=a.out
endif
default: default:
$(CC) -o $(TARGET) $(SRCS) $(CFLAGS) $(CC) -o $(TARGET) $(SRCS) $(CFLAGS) $(LDFLAGS)
clean: clean:
rm -rf $(TARGET) rm -rf $(TARGET)
# 氦氪HEKR模块串口透传协议参考实现 # 氦氪HEKR模块串口透传协议参考实现
[氦氪HEKR模块串口透传协议](http://docs.hekr.me/v4/%E7%A1%AC%E4%BB%B6%E5%BC%80%E5%8F%91/%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/%E4%B8%B2%E5%8F%A3%E9%80%8F%E4%BC%A0%E5%8D%8F%E8%AE%AE/) ## 简介
[48透传协议/2G模块协议](http://docs.hekr.me/v4/%E7%A1%AC%E4%BB%B6%E5%BC%80%E5%8F%91/%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/48%E9%80%8F%E4%BC%A0%E5%8D%8F%E8%AE%AE/) 本项目为(氦氪HEKR模块串口透传协议)[http://docs.hekr.me/v4/%E7%A1%AC%E4%BB%B6%E5%BC%80%E5%8F%91/%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/%E4%B8%B2%E5%8F%A3%E9%80%8F%E4%BC%A0%E5%8D%8F%E8%AE%AE/]的参考实现,用于演示使用48协议直接接入HEKR云的设备的具体消息交互流程。
\ No newline at end of file
## 软件需求
1. 主流发行版的GNU/Linux操作系统 或 Microsoft Windows 7/8/8.1/10 或 MacOS
2. GNU Make
3. GCC/Clang
### GNU/Linux
需要安装构建工具,例如基于`Ubuntu/Debian`的发行版本:
`apt-get install build-essential`
### Windows
需要安装(MinGW)[https://mingw-w64.org/]或类似的平台同时安装以上列出的构建工具。
### MacOS
安装Xcode Command Line Tools以及GNU Make。
## 源码结构
- `ra_types.h`:基本类型定义
- `hekr48trans.c``hekr48trans.h`:与云端交互的消息构造接口。
- `md5sum.c``md5sum.h`:构建消息时使用的**MD5**实现
实际项目的开发过程中只需要用到以上几个文件对交互使用的消息进行构造或解析。
- `main.c`:演示程序入口
- `Makefile`:构建文件
- `README.md`:本说明文件
- `usocket.c``usocket.h`:演示程序使用的Socket封装。
以上文件只是给出一个使用消息构建接口的简单说明,实际项目中可以参考。
## 构建
解压缩源码包:
```
unzip hekr48passthrough_sample_c*.zip
cd hekr48passthrough_sample_c*
```
编译:
```
make
```
## demo运行
在HEKR云的(console)[https://console.hekr.me/]平台创建48透传协议的产品,批量生成设备后,填入一组*prodkey**devtid**devprikey*,观察消息收发过程。
```
./a.out <prodkey> <devtid> <devprikey>
```
## 使用说明
### 项目文件
在实际开发过程中,需要将以上提到的:
`ra_types.h``hekr48trans.c``hekr48trans.h``md5sum.c``md5sum.h`文件通过恰当的方式引入你的项目,可能是某个IDE的项目文件也可能是Makefile。
之后在项目的代码中包含`hekr48trans.h`头文件:
```
#include "hekr48trans.h"
```
即可使用该头文件提供的接口进行设备和云端交互所使用的消息的构造和解析。
### 接口概述
以下接口用于构造设备主动发给云端的消息:
```
h48_build_frame_verify_request
h48_build_frame_verify
h48_build_frame_heartbeat
h48_build_frame_devsend
h48_build_frame_appsendresp
```
`h48_build_frame_verify_request`为例,该函数签名为:
```
int h48_build_frame_verify_request(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, \
const char *prodkey, const char *devtid);
```
其中`buf``buf_size`分别为存放构造出的消息的缓冲区和缓冲区大小,`frame_num`为这条消息的帧序号,而`prodkey``devtid`分别为产品密钥和devtid。
传入所需参数并调用`h48_build_frame_verify_request`后就会在`buf`缓冲区中获得所需的帧的数据,并且该帧的长度作为该接口的返回值以`int`类型获得,此时调用设备平台上发送数据的接口(例如socket的send接口)发送数据即可。
### 通常程序流程
程序基本流程为**登陆****消息循环**两个步骤。
```
login(); // 登陆
while (true) { // 消息循环
// 处理消息
}
```
其中登陆流程包括了使用从云端统一生成的几个值进行鉴权,消息循环则包括了处理从云端获取的消息以及根据需要主动上报消息。
### 登陆流程
```
// 登陆流程开始
data = h48_build_frame_verify_request(prodkey, devtid); // 构造登陆鉴权请求
send(data); // 发送登陆鉴权请求
data = recv() // 接收云端下发返回帧
randomkey = h48_extract_randomkey(data) // 从返回帧提取随机码
data = h48_build_frame_verify(randomkey, devtid, devprikey); // 构造鉴权
send(data); // 发送鉴权
data = recv(); // 接收鉴权返回
// 登陆流程完毕
```
### 消息循环流程
消息循环根据业务需求不同和具体平台不同而不同,一个典型的消息循环如下:
```
while (true) { // 消息循环
if (need_heartbeat()) { // 如果需要心跳(默认心跳间隔为每30秒内需要一次心跳)
data = h48_build_frame_heartbeat(); // 构造心跳帧
send(data); // 发送心跳帧
}
if (cloud_data_available()) { // 如果云端有消息下发
data = recv(); // 接收消息
data_type = h48_frame_decode_type(); // 解析消息类型
if (data_type == H48_FRAME_TYPE_APPSEND) { // 如果消息类型为云端下发的控制帧
data = h48_build_frame_appsendresp(); // 构造云端控制帧确认帧
send(data); // 发送云端控制帧确认帧
} else { // 如果消息类型为其它
// 处理其它消息,消息类型包括如下:
// H48_FRAME_TYPE_UNKNOWN 未知
// H48_FRAME_TYPE_VERIFY_REQUEST_RESP 鉴权请求回复帧
// H48_FRAME_TYPE_VERIFY_RESP 鉴权回复帧
// H48_FRAME_TYPE_REPORTDEVINFO_RESP 设备信息上报回复帧
// H48_FRAME_TYPE_APPSEND 云端下发控制帧
// H48_FRAME_TYPE_DEVSEND_RESP 设备信息上报回复帧
// H48_FRAME_TYPE_HEARTBEAT_RESP 心跳回复帧
// 其中回复帧可以忽略
process(data); // 解析和处理消息
}
}
if (device_data_available()) { // 如果设备有消息上报
data = h48_build_frame_devsend(dev_data); // 构造设备消息上报帧
send(data); // 发送设备需要上报的消息
}
}
```
### 分帧
因为和云端建立的TCP连接属于一种流式的连接,多个消息从云端下发时可能会导致多帧粘连,在消息发送密集程度不高的时候直接把每次收到的数据作为完整一帧进行解析通常情况下可以,但是在消息密集的时候可能需要手工分帧。
使用`h48_try_split_frame`接口对以收到的数据进行尝试解析,当解析出一个完整的帧则进行处理。
一个通常的从云端接收消息的模式类似如下:
```
while (true)
{
data = recv_data_from_cloud(); // 从云端接收数据
buffer = buffer + data; // 缓冲区追加新接收的数据
if (h48_try_split_frame(buffer) > 0) // 如果数据包含了一个合法帧
{
process(buffer) // 处理该帧包含的命令
pop_buffer(); // 从缓冲中去除已经处理的帧
}
}
```
### 异常处理
因为网络不稳定或者下发的数据有误等原因可能会导致网络连接被断开,需要根据各自平台的特性自行处理异常(包括重发或重新登录等)。
**注意**:若以上描述有疑惑请参照示例代码`main.c`文件中各接口调用的流程。
## 示例程序操作
运行程序后进入登录流程,如果登录成功则会进入交互状态,在不输入任何数据至按回车的情况下会自动发送心跳,否则如果有数据则会发送数据上报帧。
\ No newline at end of file
...@@ -144,6 +144,16 @@ int h48_build_frame_verify_request(ra_u8 *buf, const ra_size_t buf_size, \ ...@@ -144,6 +144,16 @@ int h48_build_frame_verify_request(ra_u8 *buf, const ra_size_t buf_size, \
return 8 + PRODKEY_LEN * 2 + DEVTID_LEN * 2 + 2; return 8 + PRODKEY_LEN * 2 + DEVTID_LEN * 2 + 2;
} }
int h48_extract_randomkey(char *randomkey_buf, const ra_size_t randomkey_buf_size, \
ra_u8 *data, const ra_size_t data_size)
{
if (data_size < 8 + 32) return -1;
if (randomkey_buf_size <= 32) return -1;
_memcpy(randomkey_buf, data + 8, 32);
randomkey_buf[32] = '\0';
return 0;
}
int h48_build_frame_verify(ra_u8 *buf, const ra_size_t buf_size, \ int h48_build_frame_verify(ra_u8 *buf, const ra_size_t buf_size, \
const ra_u8 frame_num, const char *randomkey, const char *devtid, const char *devprikey) const ra_u8 frame_num, const char *randomkey, const char *devtid, const char *devprikey)
{ {
...@@ -269,15 +279,44 @@ h48_frame_type h48_frame_decode_type(ra_u8 *frame, const ra_size_t frame_size) ...@@ -269,15 +279,44 @@ h48_frame_type h48_frame_decode_type(ra_u8 *frame, const ra_size_t frame_size)
{ {
h48_frame_type ret; h48_frame_type ret;
ra_u8 v; ra_u8 v;
if (frame_size < 7) return H48_FRAME_TYPE_UNKNOWN; if (frame_size < 6 * 2) return H48_FRAME_TYPE_UNKNOWN;
hexlit_to_raw(&v, (const char *)frame + 4, 2); hexlit_to_raw(&v, (const char *)frame + 4, 2);
switch (v) switch (v)
{ {
case 0x02: ret = H48_FRAME_TYPE_VERIFY_REQUEST_RESP; break;
case 0x04: ret = H48_FRAME_TYPE_VERIFY_RESP; break;
case 0x06: ret = H48_FRAME_TYPE_REPORTDEVINFO_RESP; break;
case 0x07: ret = H48_FRAME_TYPE_APPSEND; break; case 0x07: ret = H48_FRAME_TYPE_APPSEND; break;
case 0x0A: ret = H48_FRAME_TYPE_DEVSEND_RESP; break;
case 0x0C: ret = H48_FRAME_TYPE_HEARTBEAT_RESP; break;
default: ret = H48_FRAME_TYPE_UNKNOWN; break; default: ret = H48_FRAME_TYPE_UNKNOWN; break;
} }
return ret; return ret;
} }
int h48_try_split_frame(ra_u8 *data, const ra_size_t data_size)
{
ra_u8 v;
ra_u8 required_frame_len;
if (data_size >= 2)
{
hexlit_to_raw(&v, (const char *)data, 2);
if (v != 0x48) { return H48_TRY_SPLIT_FRAME_INVALID; }
}
if (data_size < 6 * 2)
{
return H48_TRY_SPLIT_FRAME_INCOMPLETE;
}
hexlit_to_raw(&required_frame_len, (const char *)data + 2, 2);
if (required_frame_len * 2 < data_size) return H48_TRY_SPLIT_FRAME_INCOMPLETE;
hexlit_to_raw(&v, (const char *)data, 2);
if (v != 0x48) { return H48_TRY_SPLIT_FRAME_INVALID; }
return required_frame_len;
}
...@@ -8,31 +8,158 @@ ...@@ -8,31 +8,158 @@
typedef enum typedef enum
{ {
H48_FRAME_TYPE_UNKNOWN, H48_FRAME_TYPE_UNKNOWN = 0xFF,
H48_FRAME_TYPE_APPSEND, H48_FRAME_TYPE_VERIFY_REQUEST_RESP = 0x02,
H48_FRAME_TYPE_VERIFY_RESP = 0x04,
H48_FRAME_TYPE_REPORTDEVINFO_RESP = 0x06,
H48_FRAME_TYPE_APPSEND = 0x07,
H48_FRAME_TYPE_DEVSEND_RESP = 0x0A,
H48_FRAME_TYPE_HEARTBEAT_RESP = 0x0C,
} h48_frame_type; } h48_frame_type;
/*******************
* Utilities
*******************/
/**
* Convert raw data to hex-literal
* @param[out] dst buffer for the output data
* @param[in] src input data
* @param[in] len input data length
* @return Length of the converted data
* @detail Convert raw data to hex-literal
*/
int raw_to_hexlit(char *dst, const ra_u8 *src, ra_size_t len); int raw_to_hexlit(char *dst, const ra_u8 *src, ra_size_t len);
/**
* Convert hex-literal to raw data
* @param[out] dst buffer for the output data
* @param[in] src input data
* @param[in] len input data length
* @return Length of the converted data
* @detail Convert hex-literal to raw data
*/
int hexlit_to_raw(ra_u8 *dst, const char *src, const ra_size_t len); int hexlit_to_raw(ra_u8 *dst, const char *src, const ra_size_t len);
/**
* Convert hex-literal to raw data (16-bit)
* @param[out] dst buffer for the output data
* @param[in] src input data
* @param[in] len input data length
* @return Length of the converted data
* @detail Convert hex-literal to raw data
*/
int hexlit_to_raw16(ra_u16 *dst, const char *src, const ra_size_t len); int hexlit_to_raw16(ra_u16 *dst, const char *src, const ra_size_t len);
/*******************
* Frame Building
*******************/
/**
* Build frame for verify request (the 1st step of login)
* @param[out] buf Buffer for the built frame
* @param[in] buf_size Buffer size
* @param[in] frame_num Frame number
* @param[in] prodkey Product Key
* @param[in] devtid Device TID
* @return The length of built frame
*/
int h48_build_frame_verify_request(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, \ int h48_build_frame_verify_request(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, \
const char *prodkey, const char *devtid); const char *prodkey, const char *devtid);
/**
* Extract randomkey from verify request response frame
* @param[out] buf Randomkey buffer
* @param[in] buf Randomkey buffer size
* @param[in] buf Verify request response frame data
* @param[in] buf Verify request response frame data size
* @param[in] buf_size Buffer size
* @return The length of randomkey
*/
int h48_extract_randomkey(char *randomkey_buf, const ra_size_t randomkey_buf_size, \
ra_u8 *data, const ra_size_t data_size);
/**
* Build frame for verify (the 2nd step of login)
* @param[out] buf Buffer for the built frame
* @param[in] buf_size Buffer size
* @param[in] frame_num Frame number
* @param[in] prodkey Product Key
* @param[in] devtid Device TID
* @return The length of built frame
*/
int h48_build_frame_verify(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, \ int h48_build_frame_verify(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, \
const char *randomkey, const char *devtid, const char *devprikey); const char *randomkey, const char *devtid, const char *devprikey);
/**
* Build frame for heartbeat
* @param[out] buf Buffer for the built frame
* @param[in] buf_size Buffer size
* @param[in] frame_num Frame number
* @return The length of built frame
* @detail Send this frame every 30 seconds
*/
int h48_build_frame_heartbeat(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num); int h48_build_frame_heartbeat(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num);
/**
* Build frame for data upload from device to cloud
* @param[out] buf Buffer for the built frame
* @param[in] buf_size Buffer size
* @param[in] frame_num Frame number
* @param[in] msgid Message ID
* @param[in] data Data
* @param[in] data_len Data length
* @return The length of built frame
* @detail Send data when have some state changes to report
*/
int h48_build_frame_devsend(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, const ra_u16 msgid, \ int h48_build_frame_devsend(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, const ra_u16 msgid, \
const ra_u8 *data, const ra_size_t data_len); const ra_u8 *data, const ra_size_t data_len);
/**
* Build frame for data upload from device to cloud
* @param[out] buf Buffer for the built frame
* @param[in] buf_size Buffer size
* @param[in] frame_num Frame number
* @param[in] msgid Message ID
* @param[in] data Data
* @param[in] data_len Data length
* @return The length of built frame
* @detail Confirm the appsend from cloud
*/
int h48_build_frame_appsendresp(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, const ra_u16 msgid, \ int h48_build_frame_appsendresp(ra_u8 *buf, const ra_size_t buf_size, const ra_u8 frame_num, const ra_u16 msgid, \
const ra_u8 *apptid); const ra_u8 *apptid);
/**
* Get the type of a frame
* @param[out] buf Buffer for the built frame
* @param[in] frame Data
* @param[in] frame_len Data length
* @return The type of frame
*/
h48_frame_type h48_frame_decode_type(ra_u8 *frame, const ra_size_t frame_size); h48_frame_type h48_frame_decode_type(ra_u8 *frame, const ra_size_t frame_size);
enum
{
H48_TRY_SPLIT_FRAME_INCOMPLETE = -1,
H48_TRY_SPLIT_FRAME_INVALID = -2,
};
/**
* Test if a frame is complete
* @param[out] buf Buffer for the built frame
* @param[in] data Data
* @param[in] data_len Data length
* @return > 0 Frame length (if a complete frame available)
* @return -1 Data incomplete
* @return -2 Invalid data
*/
int h48_try_split_frame(ra_u8 *data, const ra_size_t data_size);
#endif #endif
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "platform.h"
#if defined(PLATFORM_WINDOWS)
#include <Windows.h>
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif
#else
#include <unistd.h>
#endif
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include "usocket.h" #include "usocket.h"
#include "hekr48trans.h" #include "hekr48trans.h"
#define HUB_HOST "hub.hekr.me" #define HUB_HOST "hub.hekr.me"
// #define HUB_HOST "test-hub.hekr.me"
#define HUB_PORT 87 #define HUB_PORT 87
#define FRAME48_BUF_LEN 255 #define FRAME48_BUF_LEN 255
...@@ -19,10 +32,56 @@ typedef struct ...@@ -19,10 +32,56 @@ typedef struct
char *devprikey; char *devprikey;
} session; } session;
static int appsendresp(session *sess, ra_u8 frame_num, ra_u16 msgid, const ra_u8 *apptid);
static int decode_received_data(session *sess, char *data, int len)
{
int ret = 0;
h48_frame_type type = h48_frame_decode_type((ra_u8 *)data, len);
switch (type)
{
case H48_FRAME_TYPE_UNKNOWN:
printf("unknown frame\n");
break;
case H48_FRAME_TYPE_VERIFY_REQUEST_RESP:
printf("verify request resp\n");
break;
case H48_FRAME_TYPE_VERIFY_RESP:
printf("verify resp\n");
break;
case H48_FRAME_TYPE_REPORTDEVINFO_RESP:
printf("report devinfo resp\n");
break;
case H48_FRAME_TYPE_APPSEND:
printf("appsend\n");
/* Response */
{
ra_u8 frame_num;
ra_u16 msgid;
const ra_u8 *apptid = (const ra_u8 *)data + 5 * 2;
hexlit_to_raw(&frame_num, (const char *)data + 3 * 2, 2);
hexlit_to_raw16(&msgid, (const char *)data + 4 * 2, 4);
appsendresp(sess, frame_num, msgid, apptid);
}
/* TODO: Do things with the data sent from cloud */
break;
case H48_FRAME_TYPE_DEVSEND_RESP:
printf("devsend resp\n");
break;
case H48_FRAME_TYPE_HEARTBEAT_RESP:
printf("heartbeat resp\n");
break;
}
return ret;
}
static int login(session *sess) static int login(session *sess)
{ {
ra_u8 buf[FRAME48_BUF_LEN]; ra_u8 buf[FRAME48_BUF_LEN];
char randomkey[32 + 1];
int len; int len;
/* Send Verify Request */ /* Send Verify Request */
...@@ -36,19 +95,27 @@ static int login(session *sess) ...@@ -36,19 +95,27 @@ static int login(session *sess)
printf("D->C: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n"); printf("D->C: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n");
if (usocket_send(sess->fd, buf, (ra_size_t)len) < len) if (usocket_send(sess->fd, buf, (ra_size_t)len) < len)
{ fprintf(stderr, "error: failed to send login frame\n"); return -1; } { fprintf(stderr, "error: failed to send verify request frame\n"); return -1; }
if ((len = usocket_recv(sess->fd, buf, (ra_size_t)FRAME48_BUF_LEN)) < 0) if ((len = usocket_recv(sess->fd, buf, (ra_size_t)FRAME48_BUF_LEN)) < 0)
{ fprintf(stderr, "error: failed to receive login frame response\n"); return -1; } { fprintf(stderr, "error: failed to receive verify request frame response\n"); return -1; }
if (h48_frame_decode_type((ra_u8 *)buf, len) != H48_FRAME_TYPE_VERIFY_REQUEST_RESP)
{ fprintf(stderr, "error: invalid login frame response\n"); return -1; }
printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n"); printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n");
decode_received_data(sess, (char *)buf, len);
} }
/* Verify */ /* Verify */
{ {
char randomkey[32 + 1];
/* Extract RandomKey */ /* Extract RandomKey */
memcpy(randomkey, buf + 8, 32); /* memcpy(randomkey, buf + 8, 32); */
randomkey[32] = '\0'; /* randomkey[32] = '\0'; */
h48_extract_randomkey(randomkey, 32 + 1, buf, len);
if ((len = h48_build_frame_verify(buf, FRAME48_BUF_LEN, sess->frame_num++, randomkey, sess->devtid, sess->devprikey)) < 0) if ((len = h48_build_frame_verify(buf, FRAME48_BUF_LEN, sess->frame_num++, randomkey, sess->devtid, sess->devprikey)) < 0)
{ fprintf(stderr, "error: failed to build login frame\n"); return -1; } { fprintf(stderr, "error: failed to build login frame\n"); return -1; }
...@@ -56,12 +123,16 @@ static int login(session *sess) ...@@ -56,12 +123,16 @@ static int login(session *sess)
printf("D->C: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n"); printf("D->C: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n");
if (usocket_send(sess->fd, buf, (ra_size_t)len) < len) if (usocket_send(sess->fd, buf, (ra_size_t)len) < len)
{ fprintf(stderr, "error: failed to send login frame\n"); return -1; } { fprintf(stderr, "error: failed to send verify frame\n"); return -1; }
if ((len = usocket_recv(sess->fd, buf, (ra_size_t)FRAME48_BUF_LEN)) < 0) if ((len = usocket_recv(sess->fd, buf, (ra_size_t)FRAME48_BUF_LEN)) < 0)
{ fprintf(stderr, "error: failed to receive login frame response\n"); return -1; } { fprintf(stderr, "error: failed to receive verify frame response\n"); return -1; }
if (h48_frame_decode_type((ra_u8 *)buf, len) != H48_FRAME_TYPE_VERIFY_RESP)
{ fprintf(stderr, "error: invalid verify response frame\n"); return -1; }
printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n"); printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n");
decode_received_data(sess, (char *)buf, len);
} }
return 0; return 0;
...@@ -80,10 +151,11 @@ static int heartbeat(session *sess) ...@@ -80,10 +151,11 @@ static int heartbeat(session *sess)
if (usocket_send(sess->fd, buf, (ra_size_t)len) < len) if (usocket_send(sess->fd, buf, (ra_size_t)len) < len)
{ fprintf(stderr, "error: failed to send heartbeat frame\n"); return -1; } { fprintf(stderr, "error: failed to send heartbeat frame\n"); return -1; }
if ((len = usocket_recv(sess->fd, buf, (ra_size_t)FRAME48_BUF_LEN)) < 0) if ((len = usocket_recv(sess->fd, buf, (ra_size_t)FRAME48_BUF_LEN)) <= 0)
{ fprintf(stderr, "error: failed to receive heartbeat frame response\n"); return -1; } { fprintf(stderr, "error: failed to receive heartbeat frame response\n"); return -1; }
printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n"); printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n");
decode_received_data(sess, (char *)buf, len);
return 0; return 0;
} }
...@@ -107,6 +179,7 @@ static int reportdevinfo(session *sess) ...@@ -107,6 +179,7 @@ static int reportdevinfo(session *sess)
{ fprintf(stderr, "error: failed to receive devsend frame response\n"); return -1; } { fprintf(stderr, "error: failed to receive devsend frame response\n"); return -1; }
printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n"); printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n");
decode_received_data(sess, (char *)buf, len);
return 0; return 0;
} }
...@@ -142,38 +215,14 @@ static int handle_appsend(session *sess) ...@@ -142,38 +215,14 @@ static int handle_appsend(session *sess)
{ fprintf(stderr, "error: failed to receive devsend frame response\n"); return -1; } { fprintf(stderr, "error: failed to receive devsend frame response\n"); return -1; }
printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n"); printf("C->D: "); fwrite(buf, (size_t)len, 1, stdout); printf("\n");
decode_received_data(sess, (char *)buf, len);
{
h48_frame_type type = h48_frame_decode_type(buf, len);
switch (type)
{
case H48_FRAME_TYPE_UNKNOWN:
printf("unknown frame\n");
break;
case H48_FRAME_TYPE_APPSEND:
printf("appsend\n");
/* Response */
{
ra_u8 frame_num;
ra_u16 msgid;
const ra_u8 *apptid = buf + 5 * 2;
hexlit_to_raw(&frame_num, (const char *)buf + 3 * 2, 2);
hexlit_to_raw16(&msgid, (const char *)buf + 4 * 2, 4);
appsendresp(sess, frame_num, msgid, apptid);
}
/* TODO: Do things you should do */
break;
}
}
return 0; return 0;
} }
static int loop(session *sess) static int loop(session *sess)
{ {
int tick = 0; // int tick = 0;
if (reportdevinfo(sess) != 0) { return -1; } if (reportdevinfo(sess) != 0) { return -1; }
...@@ -181,16 +230,50 @@ static int loop(session *sess) ...@@ -181,16 +230,50 @@ static int loop(session *sess)
for (;;) for (;;)
{ {
int outbound_data_len;
char outbound_data[256];
fgets(outbound_data, 256, stdin);
outbound_data_len = strlen(outbound_data);
if (outbound_data_len == 0)
{
if (heartbeat(sess) != 0) { return -1; }
}
else
{
if (outbound_data[outbound_data_len - 1] == '\n')
{
outbound_data[outbound_data_len - 1] = '\0';
outbound_data_len--;
}
if (outbound_data_len == 0)
{
if (heartbeat(sess) != 0) { return -1; }
}
else
{
printf("D->C: "); fwrite(outbound_data, (size_t)outbound_data_len, 1, stdout); printf("\n");
if (usocket_send(sess->fd, outbound_data, (size_t)outbound_data_len) < outbound_data_len)
{ fprintf(stderr, "error: failed to send devsend frame\n"); return -1; }
}
}
/*
tick += 1; tick += 1;
if (tick == 5) if (tick == 20)
{ {
if (heartbeat(sess) != 0) { return -1; } if (heartbeat(sess) != 0) { return -1; }
tick = 0; tick = 0;
} }
*/
handle_appsend(sess); handle_appsend(sess);
#if defined(PLATFORM_WINDOWS)
Sleep(1000);
#else
sleep(1); sleep(1);
#endif
} }
return 0; return 0;
...@@ -201,6 +284,16 @@ int main(int argc, char *argv[]) ...@@ -201,6 +284,16 @@ int main(int argc, char *argv[])
usocket_t fd; usocket_t fd;
session sess; session sess;
#if defined(PLATFORM_WINDOWS)
{
int ret;
WSADATA wsaData;
ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (ret != 0)
{ fprintf(stderr, "WSAStartup failed: %d\n", ret); return -1; }
}
#endif
if (argc != 4) if (argc != 4)
{ {
fprintf(stderr, "usage: %s <prodkey> <devtid> <devprikey>\n", argv[0]); fprintf(stderr, "usage: %s <prodkey> <devtid> <devprikey>\n", argv[0]);
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
/* Platform */
#ifndef PLATFORM_H
#define PLATFORM_H
#if (defined(__GNUC__) && (defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__))) || \
defined(_MSC_VER)
# define PLATFORM_WINDOWS
#elif defined(__linux)
# define PLATFORM_LINUX
#elif defined(__APPLE__)
# define PLATFORM_MACOS
#else
# define PLATFORM_UNKNOWN
#endif
#endif
#ifndef RA_TYPES_H #ifndef RA_TYPES_H
#define RA_TYPES_H #define RA_TYPES_H
typedef enum
{
ra_false = 0,
ra_true = 1,
} ra_bool;
typedef unsigned long long ra_u64; typedef unsigned long long ra_u64;
typedef unsigned int ra_u32; typedef unsigned int ra_u32;
typedef unsigned short int ra_u16; typedef unsigned short int ra_u16;
......
/* Hekr Gateway : Universal Socket
* Copyright(c) 2018 Hangzhou District Nine Technology Co., Ltd. */
#include "platform.h"
#if defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
#include <sys/time.h> #include <sys/time.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/types.h> #include <sys/types.h>
...@@ -5,6 +10,17 @@ ...@@ -5,6 +10,17 @@
#include <netdb.h> #include <netdb.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#elif defined(PLATFORM_WINDOWS)
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# ifndef WINVER
# define WINVER 0x0501
# endif
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#endif
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
...@@ -12,6 +28,9 @@ ...@@ -12,6 +28,9 @@
#include <stdio.h> #include <stdio.h>
#include "ra_types.h" #include "ra_types.h"
#ifdef WITHSSL
#include "ssladapter.h"
#endif
#include "usocket.h" #include "usocket.h"
...@@ -43,6 +62,19 @@ static int _writable(int fd, const int timeout) ...@@ -43,6 +62,19 @@ static int _writable(int fd, const int timeout)
return 1; return 1;
} }
static int set_block(int fd, ra_bool enabled)
{
#if defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
int flags = fcntl(fd, F_GETFL, 0);
flags = enabled ? (flags &~ O_NONBLOCK) : (flags | O_NONBLOCK);
return (fcntl(fd, F_SETFL, flags) == 0) ? 0 : -1;
#elif defined(PLATFORM_WINDOWS)
ULONG NonBlock = enabled ? 0 : 1;
if (ioctlsocket(fd, FIONBIO, &NonBlock) == SOCKET_ERROR) return -1;
return 0;
#endif
}
/* Connect to a plain host /* Connect to a plain host
* @return 0 success * @return 0 success
* @return -1 fail */ * @return -1 fail */
...@@ -73,7 +105,7 @@ int usocket_connect3( \ ...@@ -73,7 +105,7 @@ int usocket_connect3( \
/* Fill address */ /* Fill address */
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_port = htons((uint16_t)port); addr.sin_port = htons((ra_u16)port);
memcpy(&addr.sin_addr, &((struct sockaddr_in *)servinfo->ai_addr)->sin_addr, sizeof(struct in_addr)); memcpy(&addr.sin_addr, &((struct sockaddr_in *)servinfo->ai_addr)->sin_addr, sizeof(struct in_addr));
/* Create socket */ /* Create socket */
...@@ -89,15 +121,12 @@ int usocket_connect3( \ ...@@ -89,15 +121,12 @@ int usocket_connect3( \
{ ret = -1; goto fail; } { ret = -1; goto fail; }
/* Non-block */ /* Non-block */
if (blocking == 0) if (blocking == 0) { set_block(fd, 0); }
{
fcntl(fd, F_SETFL, O_NONBLOCK);
}
} }
else else
{ {
/* Non-block */ /* Non-block */
fcntl(fd, F_SETFL, O_NONBLOCK); if (blocking == 0) { set_block(fd, 0); }
/* Connect */ /* Connect */
if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
...@@ -106,7 +135,11 @@ int usocket_connect3( \ ...@@ -106,7 +135,11 @@ int usocket_connect3( \
if (errno != EINPROGRESS) { ret = -1; goto fail; } if (errno != EINPROGRESS) { ret = -1; goto fail; }
if (!(_writable(fd, timeout) == 1)) { ret = -1; goto fail; } if (!(_writable(fd, timeout) == 1)) { ret = -1; goto fail; }
{ {
#if defined(PLATFORM_WINDOWS)
char err;
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
int err; int err;
#endif
socklen_t len = sizeof(int); socklen_t len = sizeof(int);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
{ ret = -1; goto fail; } { ret = -1; goto fail; }
...@@ -116,11 +149,7 @@ int usocket_connect3( \ ...@@ -116,11 +149,7 @@ int usocket_connect3( \
} }
/* Non-block */ /* Non-block */
if (blocking != 0) if (blocking != 0) { set_block(fd, 1); }
{
const int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags & (~O_NONBLOCK));
}
} }
usocket->type = USOCKET_TYPE_PLAIN; usocket->type = USOCKET_TYPE_PLAIN;
...@@ -152,11 +181,26 @@ int usocket_connect( \ ...@@ -152,11 +181,26 @@ int usocket_connect( \
return usocket_connect2(usocket, host, port, 0); return usocket_connect2(usocket, host, port, 0);
} }
/* Connect to a SSL host */
#ifdef WITHSSL
int usocket_connect_ssl( \
usocket_t *usocket, \
const char *host, const ra_size_t port, \
const ssladapter_options_t *options)
{
if (ssladapter_connect( \
&usocket->u.as_ssl.ssl_adapter, host, port, options) != 0)
{ return -1; }
usocket->type = USOCKET_TYPE_SSL;
return 0;
}
#endif
/* If the connection is alive */ /* If the connection is alive */
int usocket_alive( \ ra_bool usocket_alive( \
usocket_t *usocket) usocket_t *usocket)
{ {
return usocket->type == USOCKET_TYPE_NONE ? 0 : 1; return usocket->type == USOCKET_TYPE_NONE ? ra_false : ra_true;
} }
/* Close */ /* Close */
...@@ -169,10 +213,24 @@ void usocket_close( \ ...@@ -169,10 +213,24 @@ void usocket_close( \
break; break;
case USOCKET_TYPE_PLAIN: case USOCKET_TYPE_PLAIN:
if (usocket->u.as_plain.fd != -1) close(usocket->u.as_plain.fd); if (usocket->u.as_plain.fd != -1)
{
#if defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
close(usocket->u.as_plain.fd);
#elif defined(PLATFORM_WINDOWS)
closesocket(usocket->u.as_plain.fd);
#endif
}
usocket->u.as_plain.fd = -1; usocket->u.as_plain.fd = -1;
usocket->type = USOCKET_TYPE_NONE; usocket->type = USOCKET_TYPE_NONE;
break; break;
#ifdef WITHSSL
case USOCKET_TYPE_SSL:
ssladapter_close(&usocket->u.as_ssl.ssl_adapter);
usocket->type = USOCKET_TYPE_NONE;
break;
#endif
} }
} }
...@@ -212,6 +270,13 @@ int usocket_readable( \ ...@@ -212,6 +270,13 @@ int usocket_readable( \
case USOCKET_TYPE_PLAIN: case USOCKET_TYPE_PLAIN:
ret = _readable(usocket->u.as_plain.fd); ret = _readable(usocket->u.as_plain.fd);
break; break;
#ifdef WITHSSL
case USOCKET_TYPE_SSL:
ret = ssladapter_readable( \
&usocket->u.as_ssl.ssl_adapter);
break;
#endif
} }
return ret; return ret;
...@@ -233,6 +298,13 @@ int usocket_recv( \ ...@@ -233,6 +298,13 @@ int usocket_recv( \
case USOCKET_TYPE_PLAIN: case USOCKET_TYPE_PLAIN:
ret = (int)recv(usocket->u.as_plain.fd, buf, len, 0); ret = (int)recv(usocket->u.as_plain.fd, buf, len, 0);
break; break;
#ifdef WITHSSL
case USOCKET_TYPE_SSL:
ret = ssladapter_recv( \
&usocket->u.as_ssl.ssl_adapter, buf, len);
break;
#endif
} }
return ret; return ret;
...@@ -252,8 +324,23 @@ int usocket_send( \ ...@@ -252,8 +324,23 @@ int usocket_send( \
break; break;
case USOCKET_TYPE_PLAIN: case USOCKET_TYPE_PLAIN:
ret = (int)send(usocket->u.as_plain.fd, buf, len, MSG_NOSIGNAL); /* MSG_NOSIGNAL: SIGPIPE rised when the remote site closes the connection which
* may causes the termination of the process */
#if defined(PLATFORM_LINUX)
ret = (int)send( \
usocket->u.as_plain.fd, buf, len, MSG_NOSIGNAL);
#elif defined(PLATFORM_WINDOWS) || defined(PLATFORM_MACOS)
ret = (int)send( \
usocket->u.as_plain.fd, buf, len, 0);
#endif
break;
#ifdef WITHSSL
case USOCKET_TYPE_SSL:
ret = ssladapter_send( \
&usocket->u.as_ssl.ssl_adapter, buf, len);
break; break;
#endif
} }
return ret; return ret;
......
/* Hekr Gateway : Universal Socket
* Copyright(c) 2016 Hangzhou District Nine Technology Co., Ltd. */
#ifndef USOCKET_H #ifndef USOCKET_H
#define USOCKET_H #define USOCKET_H
#include "ra_types.h" #include "ra_types.h"
#ifdef WITHSSL
#include "ssladapter.h"
#endif
typedef enum typedef enum
{ {
USOCKET_TYPE_NONE, USOCKET_TYPE_NONE,
USOCKET_TYPE_PLAIN, USOCKET_TYPE_PLAIN,
#ifdef WITHSSL
USOCKET_TYPE_SSL,
#endif
} usocket_type_t; } usocket_type_t;
struct usocket struct usocket
...@@ -18,6 +28,12 @@ struct usocket ...@@ -18,6 +28,12 @@ struct usocket
{ {
int fd; int fd;
} as_plain; } as_plain;
#ifdef WITHSSL
struct
{
ssladapter_t ssl_adapter;
} as_ssl;
#endif
} u; } u;
}; };
typedef struct usocket usocket_t; typedef struct usocket usocket_t;
...@@ -47,8 +63,18 @@ int usocket_connect( \ ...@@ -47,8 +63,18 @@ int usocket_connect( \
usocket_t *usocket, \ usocket_t *usocket, \
const char *host, const ra_size_t port); const char *host, const ra_size_t port);
/* Connect to a SSL host
* @return 0 success
* @return -1 fail */
#ifdef WITHSSL
int usocket_connect_ssl( \
usocket_t *usocket, \
const char *host, const ra_size_t port, \
const ssladapter_options_t *options);
#endif
/* Test if the connection is established. */ /* Test if the connection is established. */
int usocket_alive( \ ra_bool usocket_alive( \
usocket_t *usocket); usocket_t *usocket);
/* Close /* Close
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment