我是靠谱客的博主 坚强小蜜蜂,最近开发中收集的这篇文章主要介绍zmq是基于tcp实现的吗_「socket应用」基于C语言的TCP天气客户端的实现一、前言二、天气客户端实现的要点,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、前言

上一篇笔记分享了TCP、UDP通信总结,这一篇分享一个用C语言写的、基于TCP的一个HTTP天气客户端的实现,这个一个控制台应用程序,最终的界面如下:

236a6b4877e93d902c9311f743d16d3a.png

二、天气客户端实现的要点

首先,需要说明的是,这份代码是在windows系统下使用gcc6.3.0进行编译的。

1、秘钥

心知天气:www.seniverse.com

我们完成这个实验必须得到这个天气网站(或者其它天气网站)上面去注册一个账号才能使用它的天气数据,注册之后每个账户都会有一个私钥,例如:

私钥 SMEieQjde1C9eXnbE

这个是我们程序中需要用到。

2、IP和端口

上一节分享了socket的笔记,我们与服务端通信,需要知道三个重要的信息,分别是:

  1. IP地址
  2. 端口
  3. 传输方式

我这里的心知天气的IP是116.62.81.138,端口是80,传输方式是TCP,对应的代码如下:

f93e5164b3d24e1a4d636c9553ef620a.png

这里的WEATHER_IP_ADDR对应的就是116.62.81.138,WEATHER_PORT对应的就是80。

3、GET请求

HTTP有几种请求方法,我们这里使用的是GET请求。查看心知天气API文档可知,请求地址示例为:

https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c

这是一个天气实况的请求地址示例,其有几个重要的参数:

2ff1d9d1107593fedc46edb603b67794.png

这里的key是个很重要的参数,就是我们前面说的私钥。

我们的天气客户端就是要往天气服务端发送类似这样的GET请求来获取天气数据,具体的请求方法示例为:

GET https://api.seniverse.com/v3/weather/now.json?key=2owqvhhd2dd9o9f8&location=beijing&language=zh-Hans&unit=c

对应代码如下:

4cb473ddeb500d2a24f8f9d7db9bef26.png

这里简单复习一下sprintf函数的用法:

(1)函数功能:字符串格式化

(2)函数原型:int sprintf(char *string, char *format [,argument,...]);

string: 这是指向一个字符数组的指针,该数组存储了 C 字符串。

format : 这是字符串,包含了要被写入到字符串 str 的文本。

[argument]...:根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。

(3)使用示例:

sprintf(buf, "%s,%d", str, num);

假如此时str为"hello",num为5201314,则此时buf中的内容为:hello,5201314,需要注意的是buf的容量要足够大。

4、天气服务端返回的数据

天气服务端给我们天气客户端返回的数据为JSON格式数据,可查阅往期笔记JSON的简单认识。我们这个天气客户端只是实现了查询此刻天气(对应的数据包为now.json)及近三天天气情况(对应的数据包为daily.json),如要查询其他信息,可模仿我们这里处理now.json和daily.json的方法,我们用cJson库进行解析。

只要把cJSON.c与cJSON.h放到工程主程序所在目录,然后在主程序中包含头文件JSON.h即可引入该库。如:

82304a73298a6a62d59c6afc7cb6412f.png

为了解析now.json和daily.json中的有用数据,我们建立如下结构体:

3dea532b78b635cd8b05c61368e27a69.png

现在看一下now.json和daily.json的内容是怎样的:

(1)now.json示例及解析:

now.json:

7797d1eabd9d2137f7335ed3378378ba.png

这里实测了一下,我们普通用户(因为没充钱,哈哈~)申请的now.json数据中,now对象中只有如下三个键值对:

0649c37577f1175eb204f609ead13c75.png

now.json的解析函数:

/********************************************************************************************************* 函数: 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 DEBUGprintf("cJSON_GetArraySize: size=%d",size); #endifif((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容{/* 匹配子对象1:城市地区相关 */if((subobject = cJSON_GetObjectItem(object,"location")) != NULL){// 匹配idif((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;}

(2)daily.json示例及解析:

daily.json:

a2babfc59a5b479bb243c3ffd8e88163.png

daily.json解析函数:

/********************************************************************************************************* 函数: cJSON_DailyWeatherParse,解析近三天天气数据**------------------------------------------------------------------------------------------------------** 参数: JSON:天气数据包   result:数据解析的结果** 返回: void** 公号:嵌入式大杂烩********************************************************************************************************/static int cJSON_DailyWeatherParse(char *JSON, Weather *result){cJSON *json,*arrayItem,*object,*subobject,*item,*sub_child_object,*child_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 DEBUGprintf("Get Array Size: size=%d",size); #endifif((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容{/* 匹配子对象1------结构体location */if((subobject = cJSON_GetObjectItem(object,"location")) != NULL){if((item = cJSON_GetObjectItem(subobject,"name")) != NULL) //匹配子对象1成员"name"{memcpy(result->name, item->valuestring,strlen(item->valuestring)); // 保存数据供外部调用}}/* 匹配子对象2------数组daily */if((subobject = cJSON_GetObjectItem(object,"daily")) != NULL){int sub_array_size = cJSON_GetArraySize(subobject);#if DEBUGprintf("Get Sub Array Size: sub_array_size=%d",sub_array_size);#endiffor(int i = 0; i < sub_array_size; i++){if((sub_child_object = cJSON_GetArrayItem(subobject,i))!=NULL){// 匹配日期if((child_Item = cJSON_GetObjectItem(sub_child_object,"date")) != NULL){memcpy(result->date[i], child_Item->valuestring,strlen(child_Item->valuestring)); // 保存数据}// 匹配白天天气现象文字if((child_Item = cJSON_GetObjectItem(sub_child_object,"text_day")) != NULL){memcpy(result->text_day[i], child_Item->valuestring,strlen(child_Item->valuestring)); // 保存数据}// 匹配白天天气现象代码if((child_Item = cJSON_GetObjectItem(sub_child_object,"code_day")) != NULL){memcpy(result->code_day[i], child_Item->valuestring,strlen(child_Item->valuestring)); // 保存数据}// 匹配夜间天气现象代码if((child_Item = cJSON_GetObjectItem(sub_child_object,"code_night")) != NULL){memcpy(result->code_night[i], child_Item->valuestring,strlen(child_Item->valuestring)); // 保存数据}// 匹配最高温度if((child_Item = cJSON_GetObjectItem(sub_child_object,"high")) != NULL){memcpy(result->high[i], child_Item->valuestring,strlen(child_Item->valuestring)); //保存数据}// 匹配最低温度if((child_Item = cJSON_GetObjectItem(sub_child_object,"low")) != NULL){memcpy(result->low[i], child_Item->valuestring,strlen(child_Item->valuestring)); // 保存数据}// 匹配风向if((child_Item = cJSON_GetObjectItem(sub_child_object,"wind_direction")) != NULL){memcpy(result->wind_direction[i],child_Item->valuestring,strlen(child_Item->valuestring)); //保存数据}// 匹配风速,单位km/h(当unit=c时)if((child_Item = cJSON_GetObjectItem(sub_child_object,"wind_speed")) != NULL){memcpy(result->wind_speed[i], child_Item->valuestring,strlen(child_Item->valuestring)); // 保存数据}// 匹配风力等级if((child_Item = cJSON_GetObjectItem(sub_child_object,"wind_scale")) != NULL){memcpy(result->wind_scale[i], child_Item->valuestring,strlen(child_Item->valuestring)); // 保存数据}}}}/* 匹配子对象3------最后一次更新的时间 */if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL){//printf("%s:%s",subobject->string,subobject->valuestring);}} }}cJSON_Delete(json); //释放cJSON_Parse()分配出来的内存空间return 0;}

5、获取天气数据并解析

这个函数就涉及到我们上一节笔记中的socket编程的知识了,先看一下这个函数实现的总体框图:

1e7a9069dfebb3fe7c0c2f4aaa6154dc.png

下面是函数实现的细节过程:

/********************************************************************************************************* 函数: GetWeather,获取天气数据并解析**------------------------------------------------------------------------------------------------------** 参数: weather_json:需要解析的json包   location:地名   result:数据解析的结果** 返回: void** 公号:嵌入式大杂烩********************************************************************************************************/static void GetWeather(char *weather_json, char *location, Weather *result){SOCKET ClientSock;WSADATA wd;char GetRequestBuf[256] = {0};char WeatherRecvBuf[2*1024] = {0};char GbkRecvBuf[2*1024] = {0};int  gbk_recv_len = 0;int  connect_status = 0;/* 初始化操作sock需要的DLL */WSAStartup(MAKEWORD(2,2),&wd);  /* 设置要访问的服务器的信息 */    SOCKADDR_IN  ServerSockAddr;    memset(&ServerSockAddr, 0, sizeof(ServerSockAddr));    // 每个字节都用0填充    ServerSockAddr.sin_family = PF_INET;  // IPv4    ServerSockAddr.sin_addr.s_addr = inet_addr(WEATHER_IP_ADDR);  // 心知天气服务器IP    ServerSockAddr.sin_port = htons(WEATHER_PORT);     // 端口/* 创建客户端socket */if (-1 == (ClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))){printf("socket error!");exit(1);}/* 连接服务端 */if (-1 == (connect_status = connect(ClientSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR)))){printf("connect error!");exit(1);}/* 组合GET请求包 */sprintf(GetRequestBuf, GET_REQUEST_PACKAGE, weather_json, KEY, location);/* 发送数据到服务端 */send(ClientSock, GetRequestBuf, strlen(GetRequestBuf), 0);/* 接受服务端的返回数据 */recv(ClientSock, WeatherRecvBuf, 2*1024, 0);/* utf-8转为gbk */SwitchToGbk((const unsigned char*)WeatherRecvBuf, strlen((const char*)WeatherRecvBuf), (unsigned char*)GbkRecvBuf, &gbk_recv_len);#if DEBUGprintf("服务端返回的数据为:%s", GbkRecvBuf);#endif/* 解析天气数据并保存到结构体变量weather_data中 */if (0 == strcmp(weather_json, NOW_JSON))// 天气实况{cJSON_NowWeatherParse(GbkRecvBuf, result);}else if(0 == strcmp(weather_json, DAILY_JSON)) // 未来三天天气{cJSON_DailyWeatherParse(GbkRecvBuf, result);}/* 清空缓冲区 */memset(GetRequestBuf, 0, 256);   memset(WeatherRecvBuf, 0, 2*1024);   memset(GbkRecvBuf, 0, 2*1024); /* 关闭套接字 */closesocket(ClientSock);  /* 终止使用 DLL */WSACleanup();  }

6、编译

如何编译这份代码(可在文末进行获取)呢?

这份C代码工程的文件如下:

f3282945e4207f2003a71dc6acddaa21.png

在windows系统下使用gcc编译器编译,编译命令为:

gcc weather_client.c cJSON.c utf8togbk.c -o weather_client.exe -lwsock32

如:

153d981f781705df6c78aafa86699394.png

这里的weather_client.exe就是我们编译生成的可执行文件:天气客户端,双击就可以运行了。此外,-lwsock32参数上一节也有讲过,这个参数用于链接windows下socket编程必须的winsock2这个库。若是使用集成开发环境,则需要把wsock32.lib放在工程目录下,并在我们代码中#include 下面加上一行 #pragma comment(lib, "ws2_32.lib")代码(在IDE里编译本人未验证,有兴趣的朋友可尝试)。

需要说明的是,Windows下默认是没有装gcc的,需要自己进行配置,关于mingw的配置及使用之后再做分享。

7、运行结果示例

85621597917374f45e8d41904fe1b66b.png

此处,只能使用拼音进行搜索,其实也可以做输入汉字进行搜索的功能,只是要进行转码处理。


最后

以上就是坚强小蜜蜂为你收集整理的zmq是基于tcp实现的吗_「socket应用」基于C语言的TCP天气客户端的实现一、前言二、天气客户端实现的要点的全部内容,希望文章能够帮你解决zmq是基于tcp实现的吗_「socket应用」基于C语言的TCP天气客户端的实现一、前言二、天气客户端实现的要点所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(25)

评论列表共有 0 条评论

立即
投稿
返回
顶部