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是不同的,它会将传递的数据写入到请求的正文中。
private void doPost(){ 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;

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 access = new RandomAccessFile(new File(fileName),"rwd");
            access.seek(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();
            }
            
            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);

        
        
        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();
    }
}

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 详解

 
posted @ 2016-06-20 11:22  千个太阳在手中  阅读(2062)  评论(0编辑  收藏  举报