汽车
socket编程实验(「socket应用」基于C语言的TCP天气客户端的实现)

一、前言

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

1、秘钥

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

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

2、IP和端口

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

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

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

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

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

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

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

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

(3)使用示例:

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

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

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

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

now.json:

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

now.json的解析函数:

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]\n",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\n",size); #endif			if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容			{								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)); 	// 保存数据供外部调用					}				}								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));   // 保存数据供外部调用					}					}								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:

daily.json解析函数:

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]\n",cJSON_GetErrorPtr()); //打印数据包语法错误的位置		return 1;	}	else	{		if((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL); //匹配字符串"results",获取数组内容		{			int size = cJSON_GetArraySize(arrayItem);     //获取数组中对象个数#if DEBUG			printf("Get Array Size: size=%d\n",size); #endif			if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容			{								if((subobject = cJSON_GetObjectItem(object,"location")) != NULL)				{					if((item = cJSON_GetObjectItem(subobject,"name")) != NULL) //匹配子对象1成员"name"					{						memcpy(result->name, item->valuestring,strlen(item->valuestring)); 		// 保存数据供外部调用					}				}								if((subobject = cJSON_GetObjectItem(object,"daily")) != NULL)				{					int sub_array_size = cJSON_GetArraySize(subobject);#if DEBUG					printf("Get Sub Array Size: sub_array_size=%d\n",sub_array_size);#endif					for(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)); // 保存数据							}						}					}				}								if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL)				{					//printf("%s:%s\n",subobject->string,subobject->valuestring);				}			} 		}	}		cJSON_Delete(json); //释放cJSON_Parse()分配出来的内存空间		return 0;}

5、获取天气数据并解析

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

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

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

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

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

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

7、运行结果示例




顶一下()     踩一下()

热门推荐

发表评论
0评