Android中的HTTP通信

前言:近期在慕课网学习了慕课网课程Android中的HTTP通信,就自己总结了一下,其中参考了不少博文,感谢大家的分享。

文章内容包括:
1.HTTP简介
2.HTTP/1.0和HTTP/1.1之间的区别
3.HTTP的请求头、响应头和状态码
4.Android中的HttpUrlConnection

1.Http简介
Http(Hypertext transfer protocol)定义了浏览器怎么向万维网服务器发送万维网文档,以及服务器怎么将文档发送给服务器。从层次上看,http是面向应用层协议的,它是万维网能够可靠交换文档的基础。
http的工作流程
当用户点击一个链接(假设URL为http://www.tsinghua.edu.cn/chn/yxsz/index.html ),所发生的事件流程:
(1)浏览器分析连接所指向的页面的URL。
(2)浏览器向DNS请求解析www.tsinghua.edu.cn的IP地址。
(3)浏览器解析出服务器的IP地址。
(4)浏览器与服务器建立TCP连接。
(5)浏览器发出取文件指令:GET /chn/yxsz/index.html。
(5)服务器给出响应,将文件index.html发送给浏览器。
(6)释放TCP连接。
(7)浏览器显示index.html的所用信息。
Http的特点
(1)支持客服/服务器(C/S)模式
(2)简单快速,客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST、HEAD。每种方法规定了与服务器的连接不同,由于HTTP协议简单,使得HTTP服务器的程序规模更小、因而通信更快。
(3)HTTP是无连接的。无连接意味者HTTP每次只处理一个请求,服务器处理完处理完客户的请求,并且收到客户的应答后,即断开连接,可以节省传输时间。
(4)HTTP是无状态的。无状态意味者HTTP协议对于事务没有记忆能力,缺少状态表示后续处理需要前面的信息,则它必须重传。这可能使它没次连接传输的信息量增大,另一方面、服务器在不需要先前信息就表现的非常快,同时是服务器更容易支持大量的并发的HTTP请求。
PS:虽然HTTP是无连接的协议,但HTTP使用了面向连接的运输层协议TCP,因此保证了数据的可靠传输,HTTP不用考虑数据在传输过程中被丢弃了如何重传。
2.HTTP/1.0和HTTP/1.1之间的区别
HTTP/1.0的主要缺点是它使用非持续连接每请求一个文档需要两倍的RTT的开销。这时的协议如果一个主页有很多链接的对象(如图片),每个链接都需要建立新的TCP连接,那么每一次链接下载都会导致2×RTT的开销。
HTTP/1.1协议很好的解决了这个问题,它使用了持续连接,万维网服务器在发送响应后的一段时间内仍然保持着这个连接,是同一个客户可以和该服务器传送后续的HTTP请求报文和响应报文。
3.HTTP的请求头、响应头和状态码
请求头(进入简书的请求头,可以通过Firfox浏览器通过开发者选项打开网络查看(快捷键ctrl+shift+Q))。

GET http://www.jianshu.com/
Host: www.jianshu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: https://www.google.com.hk
Cookie: (略)
Connection: keep-alive
If-None-Match: W/"b4e2a47d84be2df34bb1d5b79be9c040"
Cache-Control: max-age=0

下面说下具体的含义:
GET定义了请求的方法。同样的方法共有8种(下面会列出)。
Host:初始URL中的主机和端口。
User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
Accept:浏览器可接受的MIME类型。
Accept-Charset:浏览器可接受的字符集。
Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。
Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
Cookie:这是最重要的请求头信息之一,HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。
Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep- Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一 点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入 ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。
Cache-Control:If-None-Match:如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改。
Cache-Control:指定请求和响应遵循的缓存机制。
8中请求方法解释(摘自:http://itbilu.com/other/relate/EkwKysXIl.html)
GET
请求会显示请求指定的资源。一般来说GET方法应该只用于数据的读取,而不应当用于会产生副作用的非幂等的操作中。GET方法请求指定的页面信息,并返回响应主体,GET被认为是不安全的方法,因为GET方法会被网络蜘蛛等任意的访问。
HEAD
方法与GET方法一样,都是向服务器发出指定资源的请求。但是,服务器在响应HEAD
请求时不会回传资源的内容部分,即:响应主体。这样,我们可以不传输全部内容的情况下,就可以获取服务器的响应头信息。HEAD方法常被用于客户端查看服务器的性能。
POST
请求会向指定资源提交数据,请求服务器进行处理,如:表单数据提交、文件上传等,请求数据会被包含在请求体中。POST方法是非幂等的方法,因为这个请求可能会创建新的资源或/和修改现有资源。
PUT
请求会身向指定资源位置上传其最新内容,PUT方法是幂等的方法。通过该方法客户端可以将指定资源的最新数据传送给服务器取代指定的资源的内容。
DELETE
请求用于请求服务器删除所请求URI(统一资源标识符,Uniform Resource Identifier)所标识的资源。DELETE请求后指定资源会被删除,DELETE方法也是幂等
的。
CONNECT
该方法是HTTP/1.1协议预留的,能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接与非加密的HTTP代理服务器的通信。
OPTIONS
请求与HEAD类似,一般也是用于客户端查看服务器的性能。这个方法会请求服务器返回该资源所支持的所有HTTP请求方法,该方法会用'*'来代替资源名称,向服务器发送OPTIONS请求,可以测试服务器功能是否正常。JavaScript的XMLHttpRequest对象进行CORS跨域资源共享时,就是使用OPTIONS方法发送嗅探请求,以判断是否有对指定资源的访问权限。允许
TRACE
请求服务器回显其收到的请求信息,该方法主要用于HTTP请求的测试或诊断。
***响应头(同样是在请求简书首页的响应头

Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Sun, 19 Jun 2016 15:29:41 GMT
Etag: W/"e9a43aabd740855cd3fe0097faf6180d"
Server: nginx
Set-Cookie: (略)
Vary: Accept-Encoding
X-Request-Id: ce26a795-7e99-4959-a498-45f689471d7f
X-Runtime: 0.596683
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block

Cache-Control指定请求和响应遵循的缓存机制。
Connection:表示是否需要持久连接。
Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档 的下载时间。
Content- Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置 Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep。
Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Etag 请求变量的实体标签的当前值。
Server 服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。
Set-Cookie 设置和页面关联的Cookie。
Vary 告诉下游代理是使用缓存响应还是从原始服务器请求
**更为详细的请求头与响应头信息,请参考:HTTP Header 详解
***状态码
1XX :表示通知信息的,如请求收到了或正在处理。
2XX :表示成功,如接收或知道了。
3XX :表示重定向,如要完成还需采取进一步处理。
4XX :表示客户的差错,如请求中有错误的语法或不能完成。
5XX :表示服务器的差错,如服务器失效无法完成请求。

4.Android中的HttpUrlConnection
Android中的连接主要是通过HttpUrlConnection来完成的,下面将要从HttpUrlConnection使用、get和post传递参数、多线程下载三个方面来看HttpUrlClient的用法:
(1)HttpUrlConnection的使用格式:

URL url = new URL("http://localhost:8080/TestHttpURLConnectionPro/index.jsp");  //将地址转换为URL `
     ` URLConnection rulConnection = url.openConnection(); // 此处的urlConnection对象实际上是根据URL的请求协议(此处是http)生成的URLConnection类的子类HttpURLConnection,故此处最好将其转化为HttpURLConnection类型的对象  
HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;    `
**设置HttpUrlClient的连接参数:**
`// 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在。http正文内,因此需要设为true, 默认情况下是false;   
 httpUrlConnection.setDoOutput(true);   
 //设置是否从httpUrlConnection读入,默认情况下是true;   
 httpUrlConnection.setDoInput(true);   
 // Post 请求不能使用缓存   
 httpUrlConnection.setUseCaches(false);   
// 设定传送的内容类型是可序列化的java对象   
// (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)   
 httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");   
  // 设定请求的方法为"POST",默认是GET   
 httpUrlConnection.setRequestMethod("POST");   
  // 连接,从上述第2条中url.openConnection()至此的配置必须要在connect之前完成,   
httpUrlConnection.connect();

对于HttpUrlConnection在代码中的具体用法,看下面都是一样的用法,看过就懂了。
(2)get和post方式传递参数
get方式
使用get方式传递参数关键在于URl,在代码中可以看出我们在url中附加了一些数据,实际上get方式就是在通过在url中附加数据来传递参数的,因此采用这种方式是很不安全的。

private void doGet(){
        try {
            url = url + "?name=" + URLEncoder.encode(name,"utf-8") + "&age=" + age;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        try {
            URL httpUrl = new URL(url); //新建URL对象
            HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();//打开一个连接
            conn.setRequestMethod("GET");//设置请求方法为GET
            conn.setReadTimeout(5000);//设置从服务器读取数据的超时限制为5秒
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));//获取服务器传递的数据输入流
            String str;
            StringBuffer sb = new StringBuffer();  //存储读取的数据
            while((str = reader.readLine()) != null){//读取数据
                sb.append(str);
            }
            System.out.println("result:"+sb.toString());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

POST方式
post传递参数的方式与get是不同的,它会将传递的数据写入到请求的正文中。

        try {
            URL HttpUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) HttpUrl.openConnection();
            conn.setRequestMethod("POST");
            conn.setReadTimeout(5000);
            OutputStream out = conn.getOutputStream(); //新建输出流对象
            String content = "name="+name+"&age="+age;//传递对象
            out.write(content.getBytes());//将传递对象转为字符流写入输出流中
            //下面是对于服务器返回数据的处理
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            StringBuffer sb = new StringBuffer();
            String str;
            while((str=reader.readLine())!=null){
                sb.append(str);
            }
            System.out.println(sb.toString());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

(3)多线程下载

public class DownLoad {

//创建一个线程池
private Executor threadPool = Executors.newFixedThreadPool(3);

private Handler handler;//用于发送消息,在主线程中更新UI

public DownLoad(Handler handler){
    this.handler = handler;
}

static class DownLoadRunnable implements Runnable {

    private String url;

    private String fileName; //下载存储位置的名字

    private long start;

    private long end;

    private Handler handler;

    public DownLoadRunnable(String url,String fileName,long start,long end,Handler handler){
        this.url = url;
        this.fileName = fileName;
        this.start = start;
        this.end = end;
        this.handler = handler;
    }

    @Override
    public void run() {
        try {
            URL httpUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Range","bytes="+start+"-"+end);
            conn.setReadTimeout(5000);

            //RandomAccessFile是用来访问那些保存数据记录的文件的。第一个参数为文件,第二个为权限
            RandomAccessFile access = new RandomAccessFile(new File(fileName),"rwd");
            access.seek(start);//将读写头移动到start位置,写入由start开始
            InputStream in = conn.getInputStream(); //得到输入流
            byte[] b = new byte[1024*4];//用于读取的数组
            int len=0;
            while((len=in.read(b))!=-1){
                access.write(b,0,len);
            }
            //关闭资源
            if (access!=null){
                access.close();
            }
            if (in!=null){
                in.close();
            }
            //向handler中发送信息
            Message msg = new Message();
            msg.what = 1;
            handler.sendMessage(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public void loadFile(String url) {
    try {
        URL httpUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
        conn.setRequestMethod("GET");
        conn.setReadTimeout(5000);
        int count = conn.getContentLength();//得到请求对象的内容长度
        int block = count / 3; //准备用三个线程下载。这里将下载对象分为三份
        String fileName = getFileName(url);
        File parent = Environment.getExternalStorageDirectory();//得到存储卡根目录
        File download = new File(parent,fileName);//在parent目录中创建fileName文件

        //现在范围的设置。这里开启三个线程下载,算法是这样的,根据线程将下载的数据分为三份,
        // 可能最后一个数据没有其他线程的数据多,无论最后的数据多少,都交给最后一个线程处理
        for (int i=0;i<3;i++){
            long start = i*block;
            long end = (i+1)*block-1;
            if (i==2){
                end = count;
            }
            //循环开启线程
            DownLoadRunnable runnable = new DownLoadRunnable(url,download.getAbsolutePath(),start,end,handler);
            threadPool.execute(runnable);//在线程池中执行线程
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
/**
 * @name getFileName
 * @param url
 * @return String
 * 截取url最后一个‘/’号之后的数据
 */
public String getFileName(String url){
    return url.substring(url.lastIndexOf("/")+1);
}

}
参考:
(1)慕课网课程Android中的HTTP通信
(2)HTTP请求方法:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE
(3)HTTP响应头信息和请求头信息详解
(4)HTTP Header 详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,547评论 4 374
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,787评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,175评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,752评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,169评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,056评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,195评论 2 321
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,980评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,724评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,872评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,344评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,667评论 3 264
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,379评论 3 245
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,202评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,992评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,189评论 2 286
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,987评论 2 279

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,112评论 18 139
  • 一、概念(载录于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434阅读 8,300评论 6 152
  • Http协议详解 标签(空格分隔): Linux 声明:本片文章非原创,内容来源于博客园作者MIN飞翔的HTTP协...
    Sivin阅读 5,157评论 3 82
  • http协议有http0.9,http1.0,http1.1和http2三个版本,但是现在浏览器使用的是htt...
    一现_阅读 1,820评论 0 3
  • (一) 开心的是,副业又一个新的动作准备开始。 虽然,有太多未知,甚至当请教专业人士才发现,原来有这么多的空白和无...
    大蓓Power阅读 170评论 0 0