一、引言
本章讲解curl的相关使用、静态资源服务器的搭建以及利用curl连接静态资源服务器下载服务器中的文件
二、curl编程
1、curl库的移植
最方便的就是利用android本身的编译系统直接编译,关于android的编译系统,可以看我这篇文章:Android编译系统之交叉编译器详解。
从官网上下一个和使用android版本相同的curl库,放入external目录下,解压,修改mk文件中的动态库等配置,直接 mm 即可,这里不详细介绍
2、curl的使用
1、一、LibCurl基本编程框架
libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传,代理,cookies,和用户认证。想要知道更多关于libcurl的介绍,可以到官网 http://curl.haxx.se/上去了解,在这里不再详述。
在基于LibCurl的程序里,主要采用callback function (回调函数)的形式完成传输任务,用户在启动传输前设置好各类参数和回调函数,当满足条件时libcurl将调用用户的回调函数实现特定功能。下面是利用libcurl完成传输任务的流程:
//初始化libcurl
curl_global_init();
//函数得到 easy interface型指针
curl_easy_init();
//设置传输选项
curl_easy_setopt();
//设置的传输选项,实现回调函数以完成用户特定任务
curl_easy_setopt();
//函数完成传输任务
curl_easy_perform();
//释放内存
curl_easy_cleanup();
在整过过程中设置curl_easy_setopt()参数是最关键的,几乎所有的libcurl程序都要使用它。
2、一些基本的函数
1.CURLcode curl_global_init(long flags);
描述:
这个函数只能用一次。(其实在调用curl_global_cleanup 函数后仍然可再用)
如果这个函数在curl_easy_init函数调用时还没调用,它讲由libcurl库自动调用,所以多线程下最好主动调用该函数以防止在线程中curl_easy_init时多次调用。
注意:虽然libcurl是线程安全的,但curl_global_init是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。
参数:flags
CURL_GLOBAL_ALL //初始化所有的可能的调用。
CURL_GLOBAL_SSL //初始化支持 安全套接字层。
CURL_GLOBAL_WIN32 //初始化win32套接字库。
CURL_GLOBAL_NOTHING //没有额外的初始化。
2 void curl_global_cleanup(void);
描述:在结束libcurl使用的时候,用来对curl_global_init做的工作清理。类似于close的函数。
注意:虽然libcurl是线程安全的,但curl_global_cleanup是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。
*3 char curl_version( );
描述: 打印当前libcurl库的版本。
*4 CURL curl_easy_init( );
描述:
curl_easy_init用来初始化一个CURL的指针(有些像返回FILE类型的指针一样). 相应的在调用结束时要用curl_easy_cleanup函数清理.
一般curl_easy_init意味着一个会话的开始. 它会返回一个easy_handle(CURL*对象), 一般都用在easy系列的函数中.
*5 void curl_easy_cleanup(CURL handle);
描述:
这个调用用来结束一个会话.与curl_easy_init配合着用.
参数:
CURL类型的指针.
*6 CURLcode curl_easy_setopt(CURL handle, CURLoption option, parameter);
描述: 这个函数最重要了.几乎所有的curl 程序都要频繁的使用它.它告诉curl库.程序将有如何的行为. 比如要查看一个网页的html代码等.(这个函数有些像ioctl函数)参数:
1 CURL类型的指针
2 各种CURLoption类型的选项.(都在curl.h库里有定义,man 也可以查看到)
3 parameter 这个参数 既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量.它用什么这取决于第二个参数.
CURLoption 这个参数的取值很多.具体的可以查看man手册.
*7 CURLcode curl_easy_perform(CURL handle);
描述:这个函数在初始化CURL类型的指针 以及curl_easy_setopt完成后调用. 就像字面的意思所说perform就像是个舞台.让我们设置的
option 运作起来.参数:
CURL类型的指针.
3、curl_easy_setopt函数部分选项介绍
本节主要介绍curl_easy_setopt中跟http相关的参数。该函数是curl中非常重要的函数,curl所有设置都是在该函数中完成的,该函数的设置选项众多,注意本节的阐述的只是部分常见选项。
1、 CURLOPT_URL
设置访问URL
2、 CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
回调函数原型为:size_t function( void *ptr, size_t size, size_t nmemb, void *stream); 函数将在libcurl接收到数据后被调用,因此函数多做数据保存的功能,如处理下载文件。CURLOPT_WRITEDATA 用于表明CURLOPT_WRITEFUNCTION函数中的stream指针的来源。
如果你没有通过CURLOPT_WRITEFUNCTION属性给easy handle设置回调函数,libcurl会提供一个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。你也可以通过 CURLOPT_WRITEDATA属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。
3、 CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
回调函数原型为 size_t function( void *ptr, size_t size,size_t nmemb, void *stream); libcurl一旦接收到http 头部数据后将调用该函数。CURLOPT_WRITEDATA 传递指针给libcurl,该指针表明CURLOPT_HEADERFUNCTION 函数的stream指针的来源。
4、 CURLOPT_READFUNCTION CURLOPT_READDATA
libCurl需要读取数据传递给远程主机时将调用CURLOPT_READFUNCTION指定的函数,函数原型是:size_t function(void *ptr, size_t size, size_t nmemb,void *stream). CURLOPT_READDATA 表明CURLOPT_READFUNCTION函数原型中的stream指针来源。
5、 CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
跟数据传输进度相关的参数。CURLOPT_PROGRESSFUNCTION 指定的函数正常情况下每秒被libcurl调用一次,为了使CURLOPT_PROGRESSFUNCTION被调用,CURLOPT_NOPROGRESS必须被设置为false,CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第一个参数
6、 CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 由于设置传输时间,CURLOPT_CONNECTIONTIMEOUT 设置连接等待时间
7、 CURLOPT_FOLLOWLOCATION
设置重定位URL
8、 CURLOPT_RANGE: CURLOPT_RESUME_FROM:
断点续传相关设置。CURLOPT_RANGE 指定char *参数传递给libcurl,用于指明http域的RANGE头域,例如:
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
CURLOPT_RESUME_FROM 传递一个long参数给libcurl,指定你希望开始传递的 偏移量。
4、curl_easy_perform 函数说明(error 状态码)
该函数是完成curl_easy_setopt指定的所有选项,本节重点介绍curl_easy_perform的返回值。返回0意味一切ok,非0代表错误发生。主要错误码说明:
- CURLE_OK
任务完成一切都好
2 CURLE_UNSUPPORTED_PROTOCOL
不支持的协议,由URL的头部指定
3 CURLE_COULDNT_CONNECT
不能连接到remote 主机或者代理
4 CURLE_REMOTE_ACCESS_DENIED
访问被拒绝
5 CURLE_HTTP_RETURNED_ERROR
Http返回错误
6 CURLE_READ_ERROR
读本地文件错误
要获取详细的错误描述字符串,可以通过const char *curl_easy_strerror(CURLcode errornum ) 这个函数取得.
示例
#include <stdio.h>
#include <curl/curl.h>
bool getUrl(char *filename)
{
CURL *curl;
CURLcode res;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL) // 返回结果用文件存储
return false;
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: Agent-007");
curl = curl_easy_init(); // 初始化
if (curl)
{
//curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");// 代理
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 改协议头
curl_easy_setopt(curl, CURLOPT_URL,"http://www.baidu.com");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); //将返回的http头输出到fp指向的文件
curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); //将返回的html主体数据输出到fp指向的文件
res = curl_easy_perform(curl); // 执行
if (res != 0) {
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
fclose(fp);
return true;
}
}
bool postUrl(char *filename)
{
CURL *curl;
CURLcode res;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
return false;
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86"); // 指定post内容
//curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");
curl_easy_setopt(curl, CURLOPT_URL, " http://mail.sina.com.cn/cgi-bin/login.cgi "); // 指定url
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
fclose(fp);
return true;
}
int main(void)
{
getUrl("/tmp/get.html");
postUrl("/tmp/post.html");
}
三、curl命令
语法格式:
curl [options] [URL…]
常用选项如下所示:
-A/–user-agent :
设置用户代理发送给服务器
-e/–referer :
来源网址
–cacert :
CA证书 (SSL)
-k/–insecure:
允许忽略证书进行 SSL 连接
–compressed:
要求返回是压缩的格式
-H/–header
自定义首部信息传递给服务器
-i:
显示页面内容,包括报文首部信息
-I/–head:
只显示响应报文首部信息
-D/–dump-header :
将url的header信息存放在指定文件中
–basic:
使用HTTP基本认证
-u/–user <user[:password]>:
设置服务器的用户和密码
-L:
如果有3xx响应码,重新发请求到新位置
-O:
使用URL中默认的文件名保存文件到本地
-o :
将网络文件保存为指定的文件中
–limit-rate :
设置传输速度
-0/–http1.0:
数字0,使用HTTP 1.0
-v/–verbose:
更详细
-C:
选项可对文件使用断点续传功能
-c/–cookie-jar :
将url中cookie存放在指定文件中
-x/–proxy <proxyhost[:port]>:
指定代理服务器地址
-X/–request :
向服务器发送指定请求方法
-U/–proxy-user user:password:
代理服务器用户和密码
-T:
选项可将指定的本地文件上传到FTP服务器上
–data/-d:
方式指定使用POST方式传递数据
-b name=data:
从服务器响应set-cookie得到值,返回给服务器
四、静态资源服务器
1、搭建apache web服务器
1、linux下首先用以下命令下载apache服务
apt-get install apache2
2、调整防火墙
在我们测试Apache之前,我们需要修改我们的防火墙以允许外部访问默认的Web端口。 假设您遵循先决条件中的说明,您应该配置一个UFW防火墙来限制对您的服务器的访问。
我们可以通过键入以下内容列出ufw程序配置文件:
sudo ufw app list
获得应用程序配置文件的列表
OutputAvailable applications:
Apache
Apache Full
Apache Secure
您=可以看到,有三种可用于Apache的配置文件:
Apache :此配置文件仅打开端口80(正常,未加密的Web流量)
Apache Full :此配置文件打开端口80(正常,未加密的Web流量)和端口443(TLS / SSL加密流量)
Apache Secure :此配置文件仅打开端口443(TLS / SSL加密流量)
开启Apache服务
sudo ufw allow 'Apache'
3、测试服务器运行状态
xxx@\xxx:~$ service apache2 status
● apache2.service - The Apache HTTP Server
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
Drop-In: /lib/systemd/system/apache2.service.d
└─apache2-systemd.conf
Active: active (running) since Fri 2020-03-20 17:19:25 CST; 5h 56min ago
Process: 11378 ExecStop=/usr/sbin/apachectl stop (code=exited, status=0/SUCCESS)
Process: 11383 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
Main PID: 11387 (apache2)
Tasks: 55 (limit: 4915)
CGroup: /system.slice/apache2.service
├─11387 /usr/sbin/apache2 -k start
├─11388 /usr/sbin/apache2 -k start
└─11389 /usr/sbin/apache2 -k start
3月 20 17:19:25 clzj-QiTianM620-N000 systemd[1]: Starting The Apache HTTP Server...
3月 20 17:19:25 clzj-QiTianM620-N000 apachectl[11383]: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' direc
3月 20 17:19:25 clzj-QiTianM620-N000 systemd[1]: Started The Apache HTTP Server.
同如下命令找到主机的所有IP,打开wei界面输入IP,即可成功
hostname -I
4、常用命令
//停止Web服务器
sudo service apache2 stop
//启动Web服务器
sudo service apache2 start
//再次启动服务
sudo service apache2 restart
//进行配置更改,Apache通常可以重新加载而不会丢弃连接,使用如下命令重新配置服务器
sudo service apache2 reload
5、服务器配置文件
配置文件都在/et/apache2
基本原理
apache2在启动的时候自动读取/etc/apache2/apache2.conf文件的配置信息,不同的配置项按功能分布在不同的文件中,然后被Include包含到apache2.conf这个主配置文件中,方便管理。
就是说事实上apache2主配置文件只有一个,即apache2.conf,其他的都是被include进来的。可以把所有的配置都放在apache2.conf或者任何一个配置文件中,但是划分到不同文件会让我们管理起来方便很多,何乐而不为?
字符集
配置apache网站字符编码, /etc/apache2/conf-available/charset.conf 文件取消注释#AddDefaultCharset UTF-8
/etc/apache2 : Apache配置目录。 所有Apache配置文件驻留在此处。
/etc/apache2/apache2.conf:主配置文件。 这可以修改为对Apache全局配置进行更改。 该文件负责在配置目录中加载许多其他文件。
/etc/apache2/ports.conf : 该文件指定Apache将监听的端口。 默认情况下,当启用提供SSL功能的模块时,Apache会监听端口80,并在端口443上进行监听。
/etc/apache2/sites-available/ : 可以存储每个站点“虚拟主机”的目录。 Apache不会使用此目录中找到的配置文件,除非它们链接到sites-enabled了sites-enabled目录(见下文)。 通常,所有服务器块配置都在此目录中完成,然后通过使用a2ensite命令链接到另一个目录来启用。也可以修改html文件来修改该网点的界面,另外修改站点的对应目录也在该目录的.conf文件中
/etc/apache2/sites-enabled/ : 存储启用了每个站点“虚拟主机”的目录。 通常,这些是通过链接到具有a2ensite的sites-available目录中找到的配置文件创建的。 Apache在启动或重新加载以编译完整配置时读取此目录中找到的配置文件和链接。
/etc/apache2/conf-available/ , /etc/apache2/conf-enabled/ : 这些目录与sites-available和sites-enabled目录具有相同的关系,但用于存储不属于虚拟主机 conf-available目录中的文件可以使用a2enconf命令启用,并使用a2enconf命令禁用。
/etc/apache2/mods-available/ , /etc/apache2/mods-enabled/ : 这些目录分别包含可用和启用的模块。 以.load结尾的文件包含加载特定模块的片段,而以.conf结尾的文件包含这些模块的配置。 可以使用a2enmod和a2dismod命令启用和禁用模块。
注意:修改好配置后,切记重新启动服务器,不然配置不会生效
6、服务器日志
**/var/log/apache2/access.log :**默认情况下,对Web服务器的每个请求都记录在此日志文件中,除非Apache被配置为执行其他操作。
**/var/log/apache2/error.log :**默认情况下,所有错误都记录在此文件中。 Apache配置中的LogLevel指令指定错误日志将包含多少细节。
7、修改服务器网点读写权限
直接修改 apache2.conf 中的Directory
//站点对应的目录
<Directory /var/www/html>
#表示是否允许使用符号链接,默认为禁用
Options Indexes FollowSymLinks
#标记禁止用户对目录配置文件(.htaccess进行修改)重载,普通站点不建议开启
AllowOverride None
#以allow优先处理,未明确说明允许的都拒绝
Order allow,deny
Require all granted
#明确指出允许所有访问
Allow from all
</Directory>
每添加一个站点,在该文件内添加如上模块即可
2、使用http-server搭建静态服务器
使用http-server
如果你安装了node,那么http-server就是个不错的选择,只需要一行命令就可以快速启动。
//下载node
apt-get install nodejs
下载http-server
npm install -g http-server
下载完成后,先进入有目标文件的目录,然后执行如下目录
http-server -a 127.0.0.1 -p 8000
可以不指定地址和端口,系统会默认帮你指定好
执行如下
xxx@xxx:~/curl_test$ sudo http-server -p 80
[sudo] clzj 的密码:
Starting up http-server, serving ./
Available on:
http://127.0.0.1:80
http://192.168.132.96:80
Hit CTRL-C to stop the server
xxx@xxx:~/curl_test$ sudo http-server -a 127.0.0.5 -p 80
[sudo] clzj 的密码:
Starting up http-server, serving ./
Available on:
http://127.0.0.5:80
Hit CTRL-C to stop the server
然后在同一网段的终端上,就可以利用curl下载服务器中的文件
xxxx@xxx:~/curl_test$ sudo curl -O http://127.0.0.2:80/bluetooth/bdroid_buildcfg.h
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 759 100 759 0 0 741k 0 --:--:-- --:--:-- --:--:-- 741k
3、使用static-server搭建静态服务器
static-server很类似http-server,也是基于node,安装和使用方法很相似
npm install -g static-server
使用时只需要在项目目录下指定该项目的入口文件即可:
static-server -i index.html
不同的是,连接此网站时,可直接下载服务器中的文件
xxx@xxx:~/test_curl$ curl http://localhost:9080 --output c03fw.bin
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0Warning: Failed to create the file c03fw.bin: 权限不够
0 274M 0 65536 0 0 12.5M 0 0:00:21 --:--:-- 0:00:21 12.5M
curl: (23) Failed writing body (0 != 16384)
出现权限不足时,加上 sudo 即可