基于Linux、C、JSON、Socket的编程实例(附代码)

一、前言

链接:https://pan.baidu.com/s/1wiJmdlqTwCxvJlxOY-lAOw
提取码:ngqe
复制这段内容后打开百度网盘手机App,操作更方便哦

之前分享Windows版的时候已经有详细说明整个实现过程了,详细的内容可移步至那篇笔记进行查看。现摘抄一些要点:

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

1、秘钥

心知天气:www.seniverse.com

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

私钥 SMEieQjde1C9eXnbE

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

2、IP和端口

我们与服务端通信,需要知道三个重要的信息,分别是:

  1. IP地址

  2. 端口

  3. 传输方式

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

左右滑动查看全部代码>>>

/* 设置要访问的服务器的信息 */
struct 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);                 // 端口

这里的WEATHER_IP_ADDR对应的就是116.62.81.138WEATHER_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

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

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

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

左右滑动查看全部代码>>>

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

对应代码如下:

左右滑动查看全部代码>>>

/* 秘钥,注意!!如果要用这一份代码,这个一定要改为自己的,因为这个我已经故意改错了,防止有人与我公用一个KEY */
#define  KEY    "2owqvhhd2dd9o9f8"        // 这是在心知天气注册后,每个用户自己的一个key

/* GET请求包 */
#define  GET_REQUEST_PACKAGE     \
         "GET https://api.seniverse.com/v3/weather/%s.json?key=%s&location=%s&language=zh-Hans&unit=c\r\n\r\n"

/* JSON数据包 */    
#define  NOW_JSON     "now"
#define  DAILY_JSON   "daily"
//....还用更多其他的天气数据包可查阅心知天气

/* 组合GET请求包 */
sprintf(GetRequestBuf, GET_REQUEST_PACKAGE, weather_json, KEY, location);

/* 发送数据到服务端 */
write(ClientSock, GetRequestBuf, strlen(GetRequestBuf));

这里简单复习一下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"num5201314,则此时buf中的内容为:hello,5201314,需要注意的是buf的容量要足够大。

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

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

这个cJson库的下载链接为:

链接:https://pan.baidu.com/s/1DQynsdlNyIvsVXmf4W5b8Q
提取码:ww4z

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

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

左右滑动查看全部代码>>>

/* 天气数据结构体 */
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];       //最后一次更新的时间

/* 今天、明天、后天天气数据 */
    char date[3][32];           //日期
    char text_day[3][64];       //白天天气现象文字
    char code_day[3][32];       //白天天气现象代码
    char code_night[3][64];     //晚间天气现象代码
    char high[3][32];           //最高温
    char low[3][32];            //最低温
    char wind_direction[3][64]; //风向
    char wind_speed[3][32];     //风速,单位km/h(当unit=c时)
    char wind_scale[3][32];     //风力等级
}Weather;

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

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

now.json:

左右滑动查看全部代码>>>

{
  "results": [
    {
      "location": {
        "id": "C23NB62W20TF",
        "name": "西雅图",
        "country": "US",
        "path": "西雅图,华盛顿州,美国",
        "timezone": "America/Los_Angeles",
        "timezone_offset": "-07:00"
      },
      "now": {
        "text": "多云", //天气现象文字
        "code": "4", //天气现象代码
        "temperature": "14", //温度,单位为c摄氏度或f华氏度
        "feels_like": "14", //体感温度,单位为c摄氏度或f华氏度
        "pressure": "1018", //气压,单位为mb百帕或in英寸
        "humidity": "76", //相对湿度,0~100,单位为百分比
        "visibility": "16.09", //能见度,单位为km公里或mi英里
        "wind_direction": "西北", //风向文字
        "wind_direction_degree": "340", //风向角度,范围0~360,0为正北,90为正东,180为正南,270为正西
        "wind_speed": "8.05", //风速,单位为km/h公里每小时或mph英里每小时
        "wind_scale": "2", //风力等级,请参考:http://baike.baidu.com/view/465076.htm
        "clouds": "90", //云量,单位%,范围0~100,天空被云覆盖的百分比 #目前不支持中国城市#
        "dew_point": "-12" //露点温度,请参考:http://baike.baidu.com/view/118348.htm #目前不支持中国城市#
      },
      "last_update": "2015-09-25T22:45:00-07:00" //数据更新时间(该城市的本地时间)
    }
  ]
}

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

左右滑动查看全部代码>>>

"text": "多云", //天气现象文字
"code": "4", //天气现象代码
"temperature": "14", //温度,单位为c摄氏度或f华氏度

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]\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)//获取父对象内容
            {
                /* 匹配子对象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;
}

5、获取天气数据并解析

实现的总体框图(这是Window版的框图,Linux版的也是类似这样的思路):

6、编译、运行

左右滑动查看全部代码>>>

gcc -std=c99 weather_client.c cJSON.c -o weather_client

(0)

相关推荐

  • cJSON库的安装与使用

    介绍 cJSON 库是C语言中的最常用的 JSON 库.github 地址是 https://github.com/DaveGamble/cJSON . 安装 环境是 Ubuntu 16.04.需要先 ...

  • SQL字符串拼接

    不同的数据库,相应的字符串拼接方式不同,通过对比加深一下记忆. 一.MySQL字符串拼接 1.CONCAT函数 语法格式:CONCAT(char c1, char c2, ..., char cn) ...

  • 博途SCL编程实例:滚动数据记录

    博途SCL编程实例:滚动数据记录

  • 由浅入深的30个PLC精品电路编程实例

    PLC电气自动化 PLC实用干货.编程技巧,一触即达! 公众号 99%工控人会关注的公众号▲ PLC在学习的过程中,除了需要掌握必备的基础理论知识以外,更需要亲身设计电路来实践,刚开始学习PLC编程的 ...

  • 30个PLC编程实例,带你从小白进阶电气大神!

    发现更多电气知识 电气达人 电气达人 电气人择一业,终一生! 13篇原创内容 公众号 PLC在学习的过程中,除了需要掌握必备的基础理论知识以外,更需要亲身设计电路来实践,刚开始学习PLC编程的时候,可 ...

  • 7个基础指令4个编程实例,带你学好PLC!

    在PLC学习的过程中,逻辑指令是PLC编程中一个非常重要的环节,其中基础指令是PLC可以识别的语言,在使用PLC之前,我们需要针对现场工况的需求对PLC进行编程,之后PLC才能按照事先写入的程序进行自 ...

  • S7-400 CPU间的通讯编程实例

    厚度异常标记系统编程总结 序言: 在钢带轧制过程中,偶尔会出现厚度波动超出工艺允许范围的情况,如不能在出厂前得到处理,将产生质量异议,影响公司产品形象.为此,将本工序的在线测厚仪测量值进行采集处理,经 ...

  • 通达信编程实例

    通达信编程实例

  • 干货 | 单片机编程实例大全

    干货 | 单片机编程实例大全

  • (7条消息) Visual Studio 2019 基于Linux平台的C++开发

    由于很多unix特有的函数无法在Windows上使用,而Vim又用的不太顺手,突然想到最初用vs的时候有一个基于Linux的C++开发.在网上找了很多教程后,发现还是官方的教程比较详细,不过其中也有一 ...

  • 研发5年后,不再基于Linux,谷歌低调推送Fuchsia OS正式版系统

    启动5年后,谷歌自研的Fuchsia OS操作系统终于落地. 谷歌方面确认,即日起,第一代Nest Hub智能显示屏的用户将接收系统更新提醒,升级后,系统将从之前基于Linux的Cast OS迁移为F ...