zoukankan      html  css  js  c++  java
  • 移远4G模组拨号+socket获取天气数据

    上一篇分享了4G开发板与我们PC进行通信(需要进行内网穿透),这一篇笔记我们直接使用4G开发板访问天气服务器获取天气数据。

    我们要使用移远4G模块进行网络通信,要经历 3 个主要过程:网络注册网络激活socket 创建

    网络注册是自动完成的,无需用户代码干预(网络注册前,请确认 SIM 卡是否正常识别且 SIM 卡无欠费等业务异常)。只有在网络注册成功以后,才可进行网络激活,即本文所述的拨号(下文皆称为拨号)。只有拨号成功后,才可进行 socket 网络通信。

    拨号+socket 通信基本流程

    确保拨号成功的必要条件:SIM 卡没欠费; 硬件良好,天线匹配;拨号前,调用 ql_network_register_wait 等待网络注册成功;拨号时,向 ql_start_data_call 传递正确的参数。

    Socket 通信流程

    EC100 模块 Socket 通信基本流程如上图:
    1、在进行 socket 通信之前,必须确保拨号已经成功,可以通过 ql_get_data_call_info()查询需要进行 socket 通信的网络通道是否拨号成功。
    2、拨号成功后进行 socket 通信时,必须对需要通信的网络通道进行 bind 操作。不管是 UDP 还是 TCP,都必须执行 Bind 操作之后才可以正常进行 socket 通信。

    sdk中给我们提供了一个tcp_client的demo,这个demo的设计思路完全按照上面两张图流程图来设计。我们在这个demo进行修改,获取天气数据。

    我们修改后的example_tcp_weather_client.c代码如下:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include "ql_type.h"
    #include "ql_rtos.h"
    #include "ql_application.h"
    #include "ql_data_call.h"
    #include "sockets.h"
    #include "netdb.h"
    #include "cJSON.h"
    
    #define  DEBUG   0		
    
    /* 心知天气(www.seniverse.com)IP及端口 */
    #define  WEATHER_IP_ADDR   "116.62.81.138"
    #define  WEATHER_PORT	   80
    
    #define TCP_CONNECT_TIMEOUT_S 10
    #define TCP_RECV_TIMEOUT_S 10
    #define TCP_CLOSE_LINGER_TIME_S 10
    #define TCP_CLIENT_SEND_STR "tcp client send string"
    #define PROFILE_IDX 1
    #define PROFILE_IDX 1
    
    /* 秘钥,注意!!如果要用这一份代码,这个一定要改为自己的,因为这个我已经故意改错了,防止有人与我公用一个KEY */
    #define  KEY    "2owqvhhd2dd9o9f9"		// 这是在心知天气注册后,每个用户自己的一个key
    
    /* GET请求包 */
    #define  GET_REQUEST_PACKAGE     
             "GET https://api.seniverse.com/v3/weather/%s.json?key=%s&location=%s&language=zh-Hans&unit=c
    
    "
    	
    /* JSON数据包 */	
    #define  NOW_JSON     "now"
    //....还用更多其他的天气数据包可查阅心知天气
    
    /* 天气数据结构体 */
    typedef struct
    {
    	/* 实况天气数据 */
    	char id[32];				//id
    	char name[32];				//地名
    	char country[32];			//国家
    	char path[32];				//完整地名路径
    	char timezone[32];			//时区
    	char timezone_offset[32];   //时差
    	char text[32];				//天气预报文字
    	char code[32];				//天气预报代码
    	char temperature[32];   	//气温
    	char last_update[32];		//最后一次更新的时间
    }Weather;
    
    // 全局变量
    static struct in_addr ip4_addr = {0};
    
    // 函数声明
    static void GetWeather(char *weather_json, char *location, Weather *result);
    static int cJSON_NowWeatherParse(char *JSON, Weather *result);
    static int cJSON_DailyWeatherParse(char *JSON, Weather *result);
    static void DisplayWeather(Weather *weather_data);
    static void ql_nw_status_callback(int profile_idx, int nw_status);
    static void datacall_satrt(void);
    
    /*******************************************************************************************************
    ** 函数: sockets_weather_test
    **------------------------------------------------------------------------------------------------------
    ** 参数: void
    ** 返回: void
    ********************************************************************************************************/
    static void sockets_weather_test(void * argv)
    {
    	struct ql_data_call_info info = {0};
    	char ip4_addr_str[16] = {0};
    	Weather weather_data = {0};
    	char *location = "shenzhen";
    
    	printf("========== sockets tcp test will start ...
    ");
    
    	/* 拨号开始 */
    	datacall_satrt();
    
    	/* 获取拨号信息 */
    	ql_get_data_call_info(1, 0, &info);
    
    	printf("info.profile_idx: %d
    ", info.profile_idx);
    	printf("info.ip_version: %d
    ", info.ip_version);
    	printf("info.v4.state: %d
    ", info.v4.state);
    	printf("info.v4.reconnect: %d
    ", info.v4.reconnect);
    
    	inet_ntop(AF_INET, &info.v4.addr.ip, ip4_addr_str, sizeof(ip4_addr_str));
    	printf("info.v4.addr.ip: %s
    ", ip4_addr_str);
    
    	inet_ntop(AF_INET, &info.v4.addr.pri_dns, ip4_addr_str, sizeof(ip4_addr_str));
    	printf("info.v4.addr.pri_dns: %s
    ", ip4_addr_str);
    
    	inet_ntop(AF_INET, &info.v4.addr.sec_dns, ip4_addr_str, sizeof(ip4_addr_str));
    	printf("info.v4.addr.sec_dns: %s
    ", ip4_addr_str);
    
    	ip4_addr = info.v4.addr.ip;
    
    	if(info.v4.state)
    	{
    		memset(&weather_data, 0, sizeof(weather_data));  // weather_data清零 
    		GetWeather(NOW_JSON, location, &weather_data);   // GET 并解析实况天气数据
    		DisplayWeather(&weather_data);					 // 显示天气结果
    	}
    
    	printf("========== sockets tcp test finished
    ");
    	
    	return 0;
    }
    	
    static void ql_nw_status_callback(int profile_idx, int nw_status)
    {
    	printf("profile(%d) status: %d
    ", profile_idx, nw_status);
    }
    
    static void datacall_satrt(void)
    {
    	printf("wait for network register done
    ");
    
    	/* 等待网络注册结果 */
    	if(ql_network_register_wait(120) != 0)
    	{
    		printf("*** network register fail ***
    ");
    	}
    	else
    	{
    		printf("doing network activing ...
    ");
    		
    		ql_wan_start(ql_nw_status_callback);			/* 拨号初始化:注册拨号回调函数 */
    		ql_set_auto_connect(1, TRUE);					/* 设置拨号掉线是否自动重连 */
    		ql_start_data_call(1, 0, NULL, NULL, NULL, 0);  /* 开始拨号 */
    	}
    }
    
    static void GetWeather(char *weather_json, char *location, Weather *result)
    {
    	int				sock_nbio	= 1;
    	int				ret			= 0;
    	int				sock_fd     = -1;
    	int				sock_error  = 0;
    	socklen_t		optlen = 0;
    	fd_set 			read_fds, write_fds;
    	struct timeval	t;
    	struct addrinfo		* res, hints;
    	struct sockaddr_in	* ip4_svr_addr;
    	struct sockaddr_in	ip4_local_addr = {0};
    	u8 dns_success = 0;
    	u8 recv_buf[128] = {0};
    	u8 GetRequestBuf[256] = {0};
    	u8 WeatherRecvBuf[2*1024] = {0};
    
    	/*  */
    	memset(&hints, 0, sizeof(struct addrinfo));
    	hints.ai_family = AF_INET;
    	hints.ai_socktype = SOCK_STREAM;
    	if(getaddrinfo_with_pcid(WEATHER_IP_ADDR, NULL, &hints, &res, PROFILE_IDX) != 0)
    	{
    		printf("*** DNS fail ***
    ");
    		goto exit;
    	}
    
    	dns_success = 1;
    	
    	ret = socket(AF_INET, SOCK_STREAM, 0);
    	if(ret < 0)
    	{
    		printf("*** socket create fail ***
    ");
    		goto exit;
    	}
    
    	sock_fd = ret;
    
    	ioctl(sock_fd, FIONBIO, &sock_nbio);
    
    	ip4_local_addr.sin_family = AF_INET;
    	ip4_local_addr.sin_port = htons(ql_soc_generate_port());
    	ip4_local_addr.sin_addr = ip4_addr;
    	
    	ret = bind(sock_fd, (struct sockaddr *)&ip4_local_addr, sizeof(ip4_local_addr));
    	if(ret < 0)
    	{
    		printf("*** bind fail ***
    ");
    		goto exit;
    	}
    	
    	ip4_svr_addr = (struct sockaddr_in *)res->ai_addr;
    	ip4_svr_addr->sin_port = htons(WEATHER_PORT);
    
    	ret = connect(sock_fd, (struct sockaddr *)ip4_svr_addr, sizeof(struct sockaddr));
    
    	printf("connect ret: %d, errno: %u
    ", ret, errno);
    
    	if(ret == -1 && errno != EINPROGRESS)
    	{
    		printf("*** connect fail ***
    ");
    		goto exit;
    	}
    
    	t.tv_sec = TCP_CONNECT_TIMEOUT_S;
    	t.tv_usec = 0;
    
    	FD_ZERO(&read_fds);
    	FD_ZERO(&write_fds);
    
    	FD_SET(sock_fd, &read_fds);
    	FD_SET(sock_fd, &write_fds);
    
    	ret = select(sock_fd + 1, &read_fds, &write_fds, NULL, &t);
    
    	printf("select ret: %d
    ", ret);
    
    	if(ret <= 0)
    	{
    		printf("*** select timeout or error ***
    ");
    		goto exit;
    	}
    
    	if(!FD_ISSET(sock_fd, &read_fds) && !FD_ISSET(sock_fd, &write_fds))
    	{
    		printf("*** connect fail ***
    ");
    		goto exit;
    	}
    	else if(FD_ISSET(sock_fd, &read_fds) && FD_ISSET(sock_fd, &write_fds))
    	{
    		optlen = sizeof(sock_error);
    		ret = getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &sock_error, &optlen);
    		if(ret == 0 && sock_error == 0)
    		{
    			printf("connect success
    ");
    		}
    		else
    		{
    			printf("*** connect fail, sock_err = %d, errno = %u ***
    ", sock_error, errno);
    			goto exit;
    		}
    	}
    	else if(!FD_ISSET(sock_fd, &read_fds) && FD_ISSET(sock_fd, &write_fds))
    	{
    		printf("connect success
    ");
    	}
    	else if(FD_ISSET(sock_fd, &read_fds) && !FD_ISSET(sock_fd, &write_fds))
    	{
    		printf("*** connect fail ***
    ");
    		goto exit;
    	}
    	else
    	{
    		printf("*** connect fail ***
    ");
    		goto exit;
    	}
    
    	/* 组合GET请求包 */
    	sprintf(GetRequestBuf, GET_REQUEST_PACKAGE, weather_json, KEY, location);
    
    	/* 发送数据到服务端 */
    	ret = send(sock_fd, (const void*)GetRequestBuf, strlen(GetRequestBuf), 0);
    	if(ret < 0)
    	{
    		printf("*** send fail ***
    ");
    		goto exit;
    	}
    
    _recv_:
    	t.tv_sec = TCP_RECV_TIMEOUT_S;
    	t.tv_usec = 0;
    
    	FD_ZERO(&read_fds);
    	FD_SET(sock_fd, &read_fds);
    
    	ret = select(sock_fd + 1, &read_fds, NULL, NULL, &t);
    
    	printf("select ret: %d
    ", ret);
    
    	if(ret <= 0)
    	{
    		printf("*** select timeout or error ***
    ");
    		goto exit;
    	}
    
    	if(FD_ISSET(sock_fd, &read_fds))
    	{
    		ret = recv(sock_fd, WeatherRecvBuf, sizeof(WeatherRecvBuf), 0);
    		if(ret > 0)
    		{
    			printf("recv data: [%d]%s
    ", ret, WeatherRecvBuf);
    			/* 解析天气数据并保存到结构体变量weather_data中 */
    			if (0 == strcmp(weather_json, NOW_JSON))		// 天气实况
    			{
    				cJSON_NowWeatherParse(WeatherRecvBuf, result);	
    			}
    		}
    		else if(ret == 0)
    		{
    			printf("*** peer closed ***
    ");
    			goto exit;
    		}
    		else
    		{
    			if(!(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN))
    			{
    				printf("*** error occurs ***
    ");
    				goto exit;
    			}
    			else
    			{
    				printf("wait for a while
    ");
    				ql_rtos_task_sleep_ms(20);
    				goto _recv_;
    			}
    		}
    
    	}
    
    exit:
    	if(dns_success) freeaddrinfo(res);
    
    	if(sock_fd >= 0)
    	{
    		struct linger linger = {0};
    
    		linger.l_onoff = 1;
    		linger.l_linger = TCP_CLOSE_LINGER_TIME_S;
    
    		setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
            setsockopt(sock_fd, IPPROTO_TCP, TCP_CLOSE_TIMEROUT, &linger.l_linger, sizeof(linger.l_linger));
    
    		/* 清空缓冲区 */
    		memset(GetRequestBuf, 0, 256);   
    		memset(WeatherRecvBuf, 0, 2*1024);    
    		close(sock_fd);
    	}
    }
    
    /*******************************************************************************************************
    ** 函数: cJSON_NowWeatherParse,解析天气实况数据
    **------------------------------------------------------------------------------------------------------
    ** 参数: JSON:天气数据包   result:数据解析的结果
    ** 返回: void
    ********************************************************************************************************/
    static int cJSON_NowWeatherParse(char *JSON, Weather *result)
    {
    	cJSON *json,*arrayItem,*object,*subobject,*item;
    	
    	json = cJSON_Parse(JSON); //解析JSON数据包
    	if(json == NULL)		  //检测JSON数据包是否存在语法上的错误,返回NULL表示数据包无效
    	{
    		printf("Error before: [%s]
    ",cJSON_GetErrorPtr()); //打印数据包语法错误的位置
    		return 1;
    	}
    	else
    	{
    		if((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL); //匹配字符串"results",获取数组内容
    		{
    			int size = cJSON_GetArraySize(arrayItem);     //获取数组中对象个数
    #if DEBUG
    			printf("cJSON_GetArraySize: size=%d
    ",size); 
    #endif
    			if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容
    			{
    				/* 匹配子对象1:城市地区相关 */
    				if((subobject = cJSON_GetObjectItem(object,"location")) != NULL)
    				{
    					// 匹配id
    					if((item = cJSON_GetObjectItem(subobject,"id")) != NULL)   
    					{
    						memcpy(result->id, item->valuestring,strlen(item->valuestring)); 		// 保存数据供外部调用
    					}
    					// 匹配城市名
    					if((item = cJSON_GetObjectItem(subobject,"name")) != NULL) 
    					{
    						memcpy(result->name, item->valuestring,strlen(item->valuestring)); 		// 保存数据供外部调用
    					}
    					// 匹配城市所在的国家
    					if((item = cJSON_GetObjectItem(subobject,"country")) != NULL)
    					{
    						memcpy(result->country, item->valuestring,strlen(item->valuestring)); 	// 保存数据供外部调用
    					}
    					// 匹配完整地名路径
    					if((item = cJSON_GetObjectItem(subobject,"path")) != NULL)  
    					{
    						memcpy(result->path, item->valuestring,strlen(item->valuestring)); 		// 保存数据供外部调用	
    					}
    					// 匹配时区
    					if((item = cJSON_GetObjectItem(subobject,"timezone")) != NULL)
    					{
    						memcpy(result->timezone, item->valuestring,strlen(item->valuestring)); 	// 保存数据供外部调用	
    					}
    					// 匹配时差
    					if((item = cJSON_GetObjectItem(subobject,"timezone_offset")) != NULL)
    					{
    						memcpy(result->timezone_offset, item->valuestring,strlen(item->valuestring)); 	// 保存数据供外部调用
    					}
    				}
    				/* 匹配子对象2:今天的天气情况 */
    				if((subobject = cJSON_GetObjectItem(object,"now")) != NULL)
    				{
    					// 匹配天气现象文字
    					if((item = cJSON_GetObjectItem(subobject,"text")) != NULL)
    					{
    						memcpy(result->text, item->valuestring,strlen(item->valuestring));  // 保存数据供外部调用
    					}
    					// 匹配天气现象代码
    					if((item = cJSON_GetObjectItem(subobject,"code")) != NULL)
    					{
    						memcpy(result->code, item->valuestring,strlen(item->valuestring));  // 保存数据供外部调用
    					}
    					// 匹配气温
    					if((item = cJSON_GetObjectItem(subobject,"temperature")) != NULL) 
    					{
    						memcpy(result->temperature, item->valuestring,strlen(item->valuestring));   // 保存数据供外部调用
    					}	
    				}
    				/* 匹配子对象3:数据更新时间(该城市的本地时间) */
    				if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL)
    				{
    					memcpy(result->last_update, subobject->valuestring,strlen(subobject->valuestring));   // 保存数据供外部调用
    				}
    			} 
    		}
    	}
    	
    	cJSON_Delete(json); //释放cJSON_Parse()分配出来的内存空间
    	
    	return 0;
    }
    
    /*******************************************************************************************************
    ** 函数: DisplayWeather,显示天气数据
    **------------------------------------------------------------------------------------------------------
    ** 参数: weather_data:天气数据
    ** 返回: void
    ********************************************************************************************************/
    static void DisplayWeather(Weather *weather_data)
    {
    	printf("============%s today weather===========
    ", weather_data->name);
    	printf("weather_data->text: %s
    ", weather_data->text);		
    	printf("weather_data->temperature: %s
    ", weather_data->temperature);	
    	printf("weather_data->timezone: %s
    ", weather_data->timezone);	
    	printf("weather_data->timezone_offset: %s
    ", weather_data->timezone_offset);
    	printf("weather_data->last_update: %s
    ", weather_data->last_update);
    }
    
    application_init(sockets_weather_test, "sockets_weather_test", 8, 4);
    

    关于天气获取、解析代码之前已经有详细分享过,这里不再解释。相关文章:

    socket应用】基于C语言的天气客户端的实现

    测试结果:

  • 相关阅读:
    ubuntu17.10 源
    _nl_intern_locale_data: Assertion `cnt < (sizeof (_nl_value_type_LC_TIME) / sizeof (_nl_value_type_LC_TIME[0]))' failed
    shell | crontab 定时任务
    python将负数转为16进制无符号数
    vooya --- a YUV player and a generic raw data player
    clion 查看代码 多次查看后如何一步一步回退到最初查看的代码位置
    Unity3d XmlException: Text node cannot appear in this state的方案
    Unity2017灯光烘焙知识点
    unity加载ab后,场景shader不起效问题(物件表现黑色)
    BMFont制作美术字体
  • 原文地址:https://www.cnblogs.com/zhengnian/p/13254214.html
Copyright © 2011-2022 走看看