1.前言
本文使用windows平台的socket套接字实现向yeelink平台推送传感器结果。
【相关博文】
【 MinGW安装和使用总结 】
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库,添加的方法如下:
图2 添加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[] = "ffa3826972d6cc7ba5b17e104ec59fa3"; 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用法。