Looyao's Blog

记录一些点滴

在iOS桌面生成网页快捷方式图标(Web Clip)

| Comments

记录下如何在iOS桌面生成网页快捷方式图标。

首先需要在Mac上下载并安装”iPhone配置实用工具”,左侧选中”配置描述文件”后点击菜单中的”新建”,在”通用”中填入基本信息后,向下找到”Web Clip”并选中,点击配置,编辑相关信息,指向的URL和显示的ICON等,编辑完成选择导出,文件名如”webclip.mobileconfig”,文件内容是XML格式数据。

生成的文件不能直接安装到设备,需要进行签名。下边讲下如何给mobileconfig文件签名。

证书使用Domain SSL证书就可以,使用openssl命令进行签名,先贴出命令

1
openssl smime -sign -in webclip.mobileconfig -out webclip_signed.mobileconfig -signer signer.pem -inkey my.pem -certfile ca.pem -outform der -nodetach

文件说明:

1
2
3
4
5
my.pem:申请域名证书时生成的私钥。
signer.pem:证书提供商给到的域名证书。
ca.pem:证书提供商给到的域名中级证书和交叉证书。
webclip.mobileconfig:上边配置导出的配置文件。
webclip_signed.mobileconfig:签名后的配置文件。

如果没有申请和购买Domain SSL证书,那么就需要自己生成证书,命令如下

1
openssl req -x509 -newkey rsa:2048 -keyout my.pem -out signer.pem -days 3650 -nodes

这一步需要填写一些信息,可以参考之前的文章,域名SSL证书申请,有介绍这里怎么填写。这样私钥和证书就有了。

接下来使用如下命令签名

1
openssl smime -sign -in webclip.mobileconfig -out webclip_signed.mobileconfig -signer signer.pem -inkey my.pem -outform der -nodetach

相比于上边少了-certfile参数,由于签名是我们自己生成的,就忽略中级证书这里。

将签名后的文件放在Web服务器,在iOS设备中的Safari中输入地址访问即可安装,或者在iOS App中使用

1
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://example.com/webclip.mobileconfig"]];

来调用即可安装,要注意的是,使用自己生成的签名,安装时会显示”未验证”,使用申请的域名SSL证书签名的会有绿色”已验证”的标识。

安装完成后,桌面即可生成图标,点击图标后会加载显示配置文件中指定的URL。

DONE。

MySQL连接慢,unauthenticated User

| Comments

最近发现我们的一个统计后台WEB页面加载很慢,由于是内部用,也就没有探究原因,最近有空分析了下,发现连接MySQL服务非常慢,在MySQL中执行’SHOW PROCESSLIST’,发现好多’unauthenticated user’状态,google了下,基本确定是DNS反向解析问题。解决方式有两种:

1、MySQL服务配置文件中加上

1
skip-name-resolve

这种方式需要重启MySQL服务,不能动态设置。

2、配置hosts

把要连接到MySQL的机器IP和主机名加入到MySQL服务所在的机器hosts文件中,如要连接到MySQL服务的机器IP是10.10.0.10,主机名是node1,那么直接在MySQL服务所在机器的hosts文件中加入

1
10.10.0.10  node1

由于不方便重启MySQL,所以我们采用了方法2,效果很明显,WEB页面的加载速度快了很多。

小记一下。

优化MySQL Innodb引擎批量insert速度

| Comments

先说下最近遇到的问题,我们的一个项目数据统计用MySQL,为了防止并发insert MySQL撑不住,我们先把数据插入到redis的list里边,然后使用后台脚本读取插入到MySQL中,运行了小半年,基本没问题,但最近由于用户越来越多,导致redis的list中堆积了很多任务,原因是因为MySQL的插入速度很慢,之前没遇到瓶颈也就没有分析过,现在就需要分析和解决问题了。

我们现在的数据量大概是每天250万左右记录,分散插入到8张不同的表中,最大的表一个月大概2500万条记录。首先部署了一个slave,先解决下查询问题,不然插入任务多的时候,查询基本挂掉,非常之慢,slave部署之后,查询问题基本解决。接下来解决插入问题,检查下MySQL的数据目录,发现ibdata1有58G,原因是因为一开始没注意,部署时候没有配置innodb单独表空间,不知道是不是因为这个导致插入慢呢?google之,也没发现ibdata1过大会导致性能瓶颈的问题,不过我还是尝试开启innodb单独表空间选项,

1
innodb_file_per_table = 1

这个需要重启MySQL,不过,只有新创建的表才会使用独立表空间,ibdata1并不会因为这个而减小,需要使用mysqldump导出所有数据,然后重新建立MySQL导入数据后生成新的ibdata1,目前数据太大,不方便尝试这个,为了简单验证下,将本月的所有表都修改为独立表空间,执行下面命令,

1
mysql>ALTER TABLE table_name ENGINE=InnoDB;

这个执行也非常之慢,8张表执行了5个多小时,调整之后发现insert语句的速度并没有明显的改善,接着,正好是月末,马上到月初,那么重新建立MySQL,使用独立表空间,然后新的数据都使用新的数据库,结果发现,效果依然不明显。没办法,继续优化my.cnf参数,google了一些优化文章,又请教了之前的老同事,调整之后效果也并不是特别明显,接着换关键词google,发现了这篇文章http://blog.51yip.com/mysql/1369.html,按照这篇博文的策略修改了一下,发现性能变好了许多,innodb_flush_log_at_trx_commit这个参数设置为0之后,性能好了一些,我们的统计数据,可以容忍丢小小部分数据,所以设置了0,提高性能主要还是关闭autocommit,我们是使用PHP写后台脚本来插入数据到MySQL,1000条数据一个事务提交一次,发现性能好太多了,插入基本没有压力了,类似这样,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$dsn = "mysql:host={$MYSQL_HOST}:{$MYSQL_PORT};dbname={$MYSQL_DB}";
try {
    $db = new PDO($dsn, $MYSQL_USER, $MYSQL_PASS);
} catch (PDOException $e) {
    echo "Failed to get DB handle: " . $e->getMessage() . "\n";
    exit;
}
$db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
$db->exec('set names utf8');
$db->exec('set session wait_timeout=3600');

try {
  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  $db->beginTransaction();
  $db->exec("insert into ...");
  $db->exec("insert into ...");
  ...
  $db->commit();

} catch (Exception $e) {
  $db->rollBack();
  //TODO:回滚处理,没有执行成功的任务要记录重新处理
  echo "Failed: " . $e->getMessage();
}

没有进行准确的测试,不过我们redis的队列不会堆积了,MySQL插入基本不会延迟。

在没有解决问题之前,以为要放弃MySQL换其他策略了,现在发现使用MySQL依然没有问题,当初使用MySQL主要是因为SQL还是比较强大,一些统计数据一个SQL语句搞定,经过这个发现MySQL还是比较强大,认为MySQL性能差大多是自己没用好。

小计一下,顺便感谢上边链接文章博主。

迁移部分redis数据到其他redis实例

| Comments

前一阵子发现我们一个业务服务器的redis内存使用超过了服务器总内存的50%,当触发bgsave的时候,会报错,提示WARNING overcommit_memory is set to 0! ...,然后所有redis的写操作都会失败,这个问题redis官方FAQ中有解决方案,linux下,进行如下操作,

echo 1 > /proc/sys/vm/overcommit_memory

具体可以看官方FAQ,http://redis.io/topics/faq

解决了bgsave的问题之后,我们决定迁移一部分数据出去,分离一些数据到其他的redis实例中,下边记录下迁移过程。两种方案,一种是拷贝rdb文件或者配置一个从redis同步,但是这样所有数据都迁移了,还得做其他处理,所以放弃这个方案。另外一个方案就是写一个迁移脚本,只迁移部分想要迁移的数据。

这里直接贴出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

#redis 源ip
src_ip=192.168.0.10
#redis 源port
src_port=6379

#redis 目的ip
dest_ip=192.168.0.11
#redis 目的port
dest_port=6379

#要迁移的key前缀
key_prefix=com.example.test

i=1

redis-cli -h $src_ip -p $src_port keys "${key_prefix}*" | while read key
do
    redis-cli -h $dest_ip -p $dest_port del $key
    redis-cli -h $src_ip -p $src_port --raw dump $key | perl -pe 'chomp if eof' | redis-cli -h $dest_ip -p $dest_port -x restore $key 0
    echo "$i migrate key $key"
    ((i++))
done

参考资料:

1、http://redis.io/commands/DUMP

2、http://stackoverflow.com/questions/16127682/how-to-use-redis-dump-and-restore-offline

OpenSSL Heartbeat 漏洞检查和修复

| Comments

现在写这个貌似有点晚了,漏洞是4月8号爆出来的,美国时间应该是4月7号,OpenSSL官网的漏洞说明,https://www.openssl.org/news/secadv_20140407.txt,简要来说就是利用这个漏洞可以读到服务器64K内存数据,这个是非常危险的,比如登陆服务器,黑客在不停根据漏洞抓取64K内存值,那么这段时间进行登陆的用户的信息很可能泄漏,用户名密码直接暴露出来。

下边说下这个漏洞如何检测,最简单的方法,直接访问http://filippo.io/Heartbleed/,这个网站可以检测,我使用的时候发现不是很准,比如有漏洞的网站也有可能显示OK,有可能是这个检测网站的服务器负载太高。所以这里说下检测工具的安装,下载地址https://github.com/FiloSottile/Heartbleed,这个需要go语言支持,所以如果机器没有go语言环境需要先安装下,这里简要说下Mac上如何安装go环境,其他系统请自行google。

go 下载 & 安装:https://code.google.com/p/go/downloads/list,这里我下载的是go1.2.1.darwin-amd64-osx10.8.pkg,下载下来安装即可。

go 环境变量:这里需要设置下环境变量,这里在~/.bash_profile中添加即可,先贴出来

export GOROOT=/usr/local/go
export GOPATH=$HOME/Documents/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
  • GOROOT:go安装路径(默认就安装在/usr/local/go)。
  • GOPATH:这个路径是go插件默认存放位置(对go不是很熟悉,可能说的不是那么准确)
  • PATH:这个就是将GOPATH和GOROOT下bin文件夹中的可执行文件加入到默认PATH中。

接下来在终端source一下,让环境变量生效

source ~/.bash_profile

这样,go语言环境就ok了,接下来安装Heartbleed,github上有说明,只需要执行两条命令就可以安装了。

go get github.com/FiloSottile/Heartbleed
go install github.com/FiloSottile/Heartbleed

使用Heartbleed检测https网站是否有漏洞,在终端执行命令,如

Heartbleed www.example.com:443

如果显示SAFE,那么就说明没有Heartbeat漏洞,如果有漏洞,会打印出来64K内存值。

漏洞修复:这里需要升级OpenSSL,目前应该所有Linux发行版都会有OpenSSL更新包(受到漏洞影响的版本),这里拿CentOS为例,

yum update openssl

升级之后,需要重启WEB服务器,重新使用Heartbleed检测,如果未修复,那么检查下是否yum源是否自己修改过,如果有修改,请修改为默认源并重新update openssl试试。接下来就是服务器私钥了,为了安全起见,可以重新生成私钥,并去HTTPS证书提供商那里重新申请证书。

一个简单高效的搜索建议服务实现

| Comments

一个好的搜索建议服务,可以有效的提升搜索体验和搜索结果准确度。本文讲一下实现一个简单高效的搜索建议服务。

1、数据结构&算法准备

先google一番,发现有两个数据结构比较合适,ternary search treedouble-array trie,看了几篇相关的blog,感觉ternary search tree比较容易实现,效率对比貌似没有double-array trie高,但感觉基本已经足够。所以这里数据结构选择ternary search tree。这里就不在赘述ternary search tree,感兴趣的可以google,或者看本文下边参考资料链接。

由于ternary search tree是前缀树,只能进行前缀匹配,为了可以后缀匹配,这里使用的比较粗暴的办法,加入alias,将搜索热门词分解后缀,插入到ternary search tree里边,如字符串愤怒的小鸟,分解为:

怒的小鸟
的小鸟
小鸟
鸟

以上的字符串都是愤怒的小鸟的别名,也就是都对应愤怒的小鸟,搜小鸟也可以显示出愤怒的小鸟,这样的确是浪费了很多空间,但是可以简单解决后缀匹配问题。为了节省空间,可以只让热门词汇插入后缀,可以进行后缀匹配。什么是热门词汇?就是搜索频率较高的,可以根据实际情况来决定数值判定标准。

同理,拼音也是通过alias方案解决。如fennudexiaoniao就是愤怒的小鸟的别名,这样搜fen的时候,愤怒的小鸟就会出现。

结果排序,这个就比较简单,遍历到所有匹配串之后,根据热度(下载量或点击量)来排序,这里使用快速排序算法。

2、如何提供高效服务?

本计划用C或C++实现一个FastCGI服务,nginx调用然后提供WEB服务,后来想着为何不直接做到nginx里边呢,所以决定写一个nginx的扩展,使用nginx的共享内存来存储词典数据和结果缓存,nginx的worker进程间可以使用同一块内存。

项目地址:

https://github.com/looyao/ngx_auto_complete_module

感兴趣的可以下载。

3、安装 & 配置

编译nginx时加入

1
2
./configure --prefix=/path/to/nginx --add-module=/path/to/ngx_auto_complete_module
make && make install

nginx配置文件

1
2
3
4
5
6
7
8
9
10
11
12
http {
    ...
    server {
        listen 80;
        ...
        location /su {
            auto_complete_dict_path /path/to/dictionary.txt shm_zone=auto_complete:1024m;
        }
        ...
    }
    ...
}

配置命令: auto_complete_dict_path

参数1: `/path/to/dictionary.txt`,词典文件路径
参数2: `shm_zone=auto_complete:1024m`,表示共享内存名子`auto_complete`,共享内存大小为`1024m`,也就是1GB,具体可以根据实际情况修改。

字典文件格式说明

1
2
3
4
5
1000||愤怒的小鸟
0||fennudexiaoniao||愤怒的小鸟
512||真实赛车3
0||zhenshisaiche3||真实赛车3
512||real racing 3

||是分隔符,第一栏是热度(下载或者点击量等),目前策略是热度超过100会插入后缀。

如果有两栏,则第二栏就是正常待搜索字串。

如果有三栏,则第二栏是第三栏的别名,如搜索fennu,则会显示出来愤怒的小鸟。

1
2
[looyao@vm ~]$ curl localhost/su?s=fennu
["愤怒的小鸟"]

搜索建议匹配结果使用JSON格式输出。

如何生成字典文件呢?这个需要根据自己实际情况,将统计到的热门词或者其他需要提供搜索建议的词写入到字典文件,这里可以使用脚本语言来生成(perl,python,php等),包括汉字转拼音。

4、性能测试

目前只是简单的ab测试过,测试字典中有297960个词。

我的Mac机配置

CPU: Intel® Core™ i5-2435M CPU @ 2.40GHz

内存: 8GB

虚拟机只分配了1G内存

nginx配置: worker_processes设置为2,worker_connections设置为8192。

贴一下在Mac上的CentOS虚拟机中的测试结果(ab -n 4000 -c 4000)

命中缓存的ab测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Server Software:        nginx/1.4.7
Server Hostname:        localhost
Server Port:            80

Document Path:          /su?s=q
Document Length:        1870 bytes

Concurrency Level:      4000
Time taken for tests:   0.357 seconds
Complete requests:      4000
Failed requests:        0
Write errors:           0
Total transferred:      8188000 bytes
HTML transferred:       7480000 bytes
Requests per second:    11210.95 [#/sec] (mean)
Time per request:       356.794 [ms] (mean)
Time per request:       0.089 [ms] (mean, across all concurrent requests)
Transfer rate:          22410.95 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       73  110  25.6    105     160
Processing:    82  100  10.0    100     127
Waiting:       55   87  19.5     94     127
Total:        173  210  19.1    207     247

Percentage of the requests served within a certain time (ms)
  50%    207
  66%    219
  75%    226
  80%    231
  90%    240
  95%    243
  98%    245
  99%    246
 100%    247 (longest request)

没有命中缓存的测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Server Software:        nginx/1.4.7
Server Hostname:        localhost
Server Port:            80

Document Path:          /su?s=angry
Document Length:        753 bytes

Concurrency Level:      4000
Time taken for tests:   0.846 seconds
Complete requests:      4000
Failed requests:        0
Write errors:           0
Total transferred:      3712000 bytes
HTML transferred:       3012000 bytes
Requests per second:    4727.55 [#/sec] (mean)
Time per request:       846.104 [ms] (mean)
Time per request:       0.212 [ms] (mean, across all concurrent requests)
Transfer rate:          4284.34 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       86  115  17.7    114     155
Processing:    80  313 164.8    308     606
Waiting:       79  312 165.8    308     606
Total:        209  428 147.7    422     692

Percentage of the requests served within a certain time (ms)
  50%    422
  66%    509
  75%    556
  80%    585
  90%    639
  95%    666
  98%    681
  99%    687
 100%    692 (longest request)

是否命中缓存在HTTP响应头中有标识

X-AC-Cached: no

no表示没有缓存,yes表示有缓存。 可以用curl来测试是否有缓存,例如

curl -i http://localhost/su?s=q 

测试比较单一,可能不具备普遍性,但是可以看到基本可以处理比较高的并发请求数。

感兴趣可以试试,有bug或者其他建议的欢迎评论留言。

参考资料

1、http://igoro.com/archive/efficient-auto-complete-with-a-ternary-search-tree/ 2、http://www.drdobbs.com/database/ternary-search-trees/184410528 3、http://www.evanmiller.org/nginx-modules-guide-advanced.html 4、http://www.evanmiller.org/nginx-modules-guide.html

使用php开发一个守护进程

| Comments

php除了编写WEB服务程序以外,也可以编写一些后台脚本程序来处理后台任务,利用crontab来定时调用执行。但是如果需要一些相对实时的任务处理,就不适合用crontab了。本文主要讲下使用php来开发守护进程。

为什么使用php?个人认为主要是方便,丰富的工具库支持,几行代码便能处理一些后台数据。对于一些对性能要求不是特别苛刻的,使用php来编写一个daemon还是可以的,可以满足大部分需求。

思路 & 方法: 使用php编写守护首先要防止内存问题,写WEB逻辑,一个请求处理结束,那么被执行的php脚本所使用内存都会被回收,而后台程序需要一直运行,需要注意内存问题。简单的解决方案是master进程主要负责获取任务,然后分配子进程处理,子进程处理完成结束退出,资源被回收。

运行: 使用nohup或者screen来执行,这样便可以一直后台运行。PS: master进程还需要注意一些异常处理,不然可能会中断退出。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
declare(ticks = 1);

$num_of_child = 0;

function sigchld_handler($signo)
{
    global $num_of_child;
    //回收子进程
    while(($pid = pcntl_waitpid(-1, $status, WNOHANG)) > 0) {
        echo "pcntl_waitpid pid: " . $pid . PHP_EOL;
        $num_of_child--;
    }
}

pcntl_signal(SIGCHLD, 'sigchld_handler');

while (true) {
    //获取任务数据(如从db或者redis队列中读取),同时可以根据$num_of_child控制下子进程数量
    echo "parent: number of child is " . $num_of_child . PHP_EOL;
    $job_info = 'hello world';
    sleep(1);
    $pid = pcntl_fork();
    if ($pid > 0) {
        $num_of_child++;
    } else if ($pid == 0) {
        //子进程,做一些任务处理
        sleep(1);
        echo "child: get job \"" . $job_info . "\"" . PHP_EOL;
        exit(0);
    } else {
        echo "pcntl_fork error" . PHP_EOL;
    }
}
?>

Hello World

| Comments

VPS使用了1年半,就放了一个wordpress,基本浪费了,人越变越懒,工作之余也没太多精力来经营博客。VPS不准备续费了,正赶着这两天有时间,迁移了之前的旧博客文章,感谢Github PagesOctopress

这里闲暇之余还会更新一些内容,也算是记录下工作中的点滴,以后翻阅也能知道自己大概什么时间做了什么东西。

域名SSL证书申请(HTTPS)

| Comments

记录一下域名SSL证书的申请过程.

1、需要选一个SSL证书服务商, 比如GlobalSign, 亚洲诚信等.

2、技术准备, 首先要生成一个CSR(Certificate Signing Request)文件, 用来证书请求. 这里需要使用openssl, 使用linux系统为例: 先生成一个私钥, 一般需要2048位, 这一步需要输入密码.

openssl genrsa -des3 -out domain_ssl.key 2048

接着, 生成CSR, 这一步需要输入一些信息

openssl req -new -key domain_ssl.key -out domain_ssl.csr

信息如下

Country Name (2 letter code): (国家名, 中国填CN)
State or Province Name (full name): (省份, 如:广东)
Locality Name (eg, city):(城市名, 如:深圳)
Organizational Unit Name (eg, section):(这个填部门就可以, 比如IT)
Common Name (eg, your websites domain name):(域名)
Email Address:(选填)
A challenge password:(选填)
An optional company name:(选填)

生成好CSR文件, 就可以去申请证书了. 一般一周以内应该能申请完成, 具体证书信息应该会以邮件形式接收.上边的私钥文件domain_ssl.key要保存着, 下面说下去掉私钥的密码, 重命名原始秘钥文件

mv domain_ssl.key domain_ssl.key.ori
openssl rsa -in domain_ssl.key.ori -out domain_ssl.key

这样密码就去掉了, 不然每次重启web server的时候需要手工输入私钥密码.

3、证书请求完成之后集成到web server.一般情况, 证书请求成功后提供商那里会有相关集成文档, 下边以nginx为例, 将证书提供商分配的SSL证书和域名型SSL中级证书合并到一个文件(CRT), 如domain_ssl.crt, SSL证书在前, 中级证书在后. 将自己生成的私钥文件domain_ssl.key和刚合并好的domain_ssl.crt拷贝到一个文件夹下.

更改nginx配置, 如

1
2
3
4
5
6
7
8
9
10
11
server {
        listen       80;
        listen       443 ssl;
        server_name  yourdomain;
        ssl_certificate     /data/ssl_key/domain_ssl.crt;
        ssl_certificate_key /data/ssl_key/domain_ssl.key;
        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers         HIGH:!aNULL:!MD5;

        ...
}

保存后测试, nginx -t, 成功后重启nginx. 测试访问: https://yourdomain, 完成.

参考资料:

http://articles.slicehost.com/2007/12/19/ubuntu-gutsy-self-signed-ssl-certificates-and-nginx

使用hexdump的format格式化输出

| Comments

hexdump可以显示文件的16进制, 当然也不只16进制, 这篇文主要写下如何使用hexdump的format来格式化输出.

先举个例子

bash$ hexdump -e '4/1 "%02x,"' -e '"\n"' tmp.txt
68,65,6c,6c,
6f,20,77,6f,
72,6c,64,0a,

这里4/1表示每行打印4个(1个字节的16进制表示). 再看一个例子,

bash$ hexdump -v -e '4/2 "%02x,"' -e '"\n"' tmp.txt
6568,6c6c,206f,6f77,
6c72,a64,  ,  ,

这里4/2, 这里4/2表示每行打印4个(2个字节的16进制表示). 而后边的%02x和C语言的printf 格式化字串一样. -v选项是防止输出多余的*字符.

这样, 使用hexdump命令的format就可以很方便的输出我们需要的格式了. 比如现在要把图片数据赋值到程序变量中, 我们可以先使用hexdump 格式化输出图片数据:

bash$ hexdump -v -e '/1 "0x%02x,"' close.png
0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1e,0x08,0x06,0x00,0x00,0x00,0x3b,0x30,0xae,0xa2,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,(...后边省略)

在c代码中

unsigned char img_data[] = {0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1e,0x08,0x06,0x00,0x00,0x00,0x3b,0x30,0xae,0xa2,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,(...后边省略)};