1.前言
本文使用windows平台的socket套接字实现向yeelink平台推送传感器结果。
【相关博文】
【 MinGW安装和使用总结 】
【Yeelink Http请求格式分析】
【Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32 】
【Yeelink平台查询开关量——套接字编程 Windows平台 】
2.运行
【工作流程】
先看运行结果,大致的过程分为三块。第一部分,进行DNS地址解析;第二部分,发送HTTP请求;第三部分,接收HTTP响应。
【HTTP请求】
POST /v1.0/device/1949/sensor/2510/datapoints HTTP/1.1
Host: api.yeelink.net
U-ApiKey: ffa3826972d6cc7ba5b17e104ec59fa3
Content-Length:15
{"value":20.30}
【HTTP响应】省略其他部分
HTTP/1.1 200 OK
图1 运行结果
【运行设置】
若使用minGW+eclipse开发方式,需要加入wsock32库,添加的方法如下:
3.参考代码
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <windows.h>
// 请求缓冲区和响应缓冲区
char http_request[1024] = {0,};
char http_response[1024] = {0,};
// 主机IP地址,也可以是主机域名
char remote_server[] = "api.yeelink.net";
// 文件地址 RESTFUL形式
char remote_path[] = "/v1.0/device/1949/sensor/2510/datapoints";
// 用户密码 请修改
char apikey[] = "ffa3826972d6cc7ba5b17e104ec5XXXX";
int main(int argc, char **argv)
{
WSADATA wsaData;
int result;
int socket_id;
// 传感器检测结果
double value = 20.3;
// Http属性
char http_attribute[64] = {0,};
// Http内容,表单内容
char http_content[64] = {0,};
// 确定HTTP表单提交内容 {"value":20}
sprintf( http_content , "{\"value\":%.2f}" , value);
// 确定 HTTP请求首部
// 例如POST /v1.0/device/1949/sensor/2510/datapoints HTTP/1.1\r\n
char http_header[64] = {0,};
sprintf( http_header , "POST %s HTTP/1.1\r\n",remote_path);
strcpy( http_request , http_header ); // 复制到请求缓冲区中
// 增加属性 例如 Host: api.yeelink.net\r\n
sprintf( http_attribute , "Host:%s\r\n" , remote_server);
strcat( http_request , http_attribute);
memset( http_attribute , 0 , sizeof(http_attribute));
// 增加密码 例如 U-ApiKey: ffa3826972d6cc7ba5b17e104ec59fa3
sprintf( http_attribute , "U-ApiKey:%s\r\n" , apikey);
strcat( http_request , http_attribute);
memset( http_attribute , 0 , sizeof(http_attribute));
// 增加提交表单内容的长度 例如 Content-Length:12\r\n
sprintf( http_attribute , "Content-Length:%d\r\n" ,strlen(http_content) );
strcat( http_request , http_attribute);
memset( http_attribute , 0 , sizeof(http_attribute));
// 增加表单编码格式 Content-Type:application/x-www-form-urlencoded\r\n
// 该步骤可省略
strcat( http_request , "Content-Type:application/x-www-form-urlencoded\r\n");
memset( http_attribute , 0 , sizeof(http_attribute));
// HTTP首部和HTTP内容 分隔部分
strcat( http_request , "\r\n");
// HTTP负载内容
strcat( http_request , http_content);
// Winsows下启用socket
result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
printf("WSAStartup failed: %d\n", result);
return 1;
}
// DNS解析 获得远程IP地址
struct hostent *remote_host;
remote_host = gethostbyname(remote_server);
if( remote_host == NULL )
{
printf("DNS failed\r\n");
return 1;
}
// 创建套接字
socket_id = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in remote_sockaddr;
remote_sockaddr.sin_family = AF_INET;
remote_sockaddr.sin_port = htons(80);
remote_sockaddr.sin_addr.s_addr = *(u_long *) remote_host->h_addr_list[0];
memset(&(remote_sockaddr.sin_zero), 0, sizeof(remote_sockaddr.sin_zero));
// 连接远程主机
result = connect( socket_id, (struct sockaddr *)&remote_sockaddr, sizeof(struct sockaddr));
if( result == SOCKET_ERROR )
{
printf("connect ok\r\n");
}
// 发送请求
printf("Http request:\r\n%s\r\n",http_request);
printf("--------------------\r\n");
send(socket_id , http_request, strlen(http_request), 0);
// 获得响应
int bytes_received = 0;
bytes_received = recv( socket_id , http_response, 1024, 0);
http_response[ bytes_received ] = '\0';
printf("Receive Message:\r\n%s\r\n",http_response );
printf("--------------------\r\n");
// 判断是否收到HTTP OK
char* presult = strstr( http_response , "200 OK\r\n");
if( presult != NULL ) printf("Http response OK\r\n");
// 关闭套接字
closesocket(socket_id);
WSACleanup();
// 输入任何字符则关闭程序
printf("Finish\r\n");getchar();
return 0;
}
4.总结和展望
socket是对TCP或者UDP通信的抽象,但是对于HTTP通信而言socket就显得比较麻烦了。以后的博文还会总结socket用法。