Android网络请求

1,Android网络编程系列

链接:http://liuwangshu.cn/application/network/1-http.html

1.HTTP简介

HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。

HTTP协议的主要特点
  1. 支持C/S(客户/服务器)模式。
  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST,每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  3. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  4. 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  5. 无状态:HTTP协议是无状态协议,无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTP URL 的格式如下
 
    
http://host[":"port][abs_path]

http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用默认端口80;abs_path指定请求资源的URI(Web上任意的可用资源)。HTTP有两种报文分别是请求报文和响应报文,让我们先来看看请求报文。

2.HTTP的请求报文

先来看看请求报文的一般格式:

这里写图片描述

通常来说一个HTTP请求报文由请求行、请求报头、空行、和请求数据4个部分组成。

请求行

请求行由请求方法,URL字段和HTTP协议的版本组成,格式如下:

 
    
Method Request-URI HTTP-Version CRLF

其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。

HTTP请求方法有8种,分别是GET、POST、DELETE、PUT、HEAD、TRACE、CONNECT 、OPTIONS。其中PUT、DELETE、POST、GET分别对应着增删改查,对于移动开发最常用的就是POST和GET了。

  1. GET:请求获取Request-URI所标识的资源
  2. POST:在Request-URI所标识的资源后附加新的数据
  3. HEAD:请求获取由Request-URI所标识的资源的响应消息报头
  4. PUT: 请求服务器存储一个资源,并用Request-URI作为其标识
  5. DELETE :请求服务器删除Request-URI所标识的资源
  6. TRACE : 请求服务器回送收到的请求信息,主要用于测试或诊断
  7. CONNECT: HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
  8. OPTIONS :请求查询服务器的性能,或者查询与资源相关的选项和需求

例如我去访问我的CSDN博客地址请求行是:

 
    
GET http://blog.csdn.net/itachi85 HTTP/1.1
请求报头

在请求行之后会有0个或者多个请求报头,每个请求报头都包含一个名字和一个值,它们之间用“:”分割。请求头部会以一个空行,发送回车符和换行符,通知服务器以下不会有请求头。关于请求报头,会在后面的消息报头一节做统一的解释。

请求数据

请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合,与请求数据相关的最常用的请求头是Content-Type和Content-Length。

3.HTTP的响应报文

先来看看响应报文的一般格式:这里写图片描述

HTTP的响应报文由状态行、消息报头、空行、响应正文组成。响应报头后面会讲到,响应正文是服务器返回的资源的内容,先来看看状态行。

状态行

1、状态行格式如下:

 
    
HTTP-Version Status-Code Reason-Phrase CRLF

其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:

  • 100~199:指示信息,表示请求已接收,继续处理
  • 200~299:请求成功,表示请求已被成功接收、理解、接受
  • 300~399:重定向,要完成请求必须进行更进一步的操作
  • 400~499:客户端错误,请求有语法错误或请求无法实现
  • 500~599:服务器端错误,服务器未能实现合法的请求

常见的状态码如下:

  • 200 OK:客户端请求成功
  • 400 Bad Request:客户端请求有语法错误,不能被服务器所理解
  • 401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
  • 403 Forbidden:服务器收到请求,但是拒绝提供服务
  • 500 Internal Server Error:服务器发生不可预期的错误
  • 503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常

例如访问我的CSDN博客地址响应的状态行是:

 
    
HTTP/1.1 200 OK

4.HTTP的消息报头

消息报头分为通用报头、请求报头、响应报头、实体报头等。消息头由键值对组成,每行一对,关键字和值用英文冒号“:”分隔。

通用报头

既可以出现在请求报头,也可以出现在响应报头中

  • Date:表示消息产生的日期和时间
  • Connection:允许发送指定连接的选项,例如指定连接是连续的,或者指定“close”选项,通知服务器,在响应完成后,关闭连接
  • Cache-Control:用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制)
请求报头

请求报头通知服务器关于客户端求求的信息,典型的请求头有:

  • Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机
  • User-Agent:发送请求的浏览器类型、操作系统等信息
  • Accept:客户端可识别的内容类型列表,用于指定客户端接收那些类型的信息
  • Accept-Encoding:客户端可识别的数据编码
  • Accept-Language:表示浏览器所支持的语言类型
  • Connection:允许客户端和服务器指定与请求/响应连接有关的选项,例如这是为Keep-Alive则表示保持连接。
  • Transfer-Encoding:告知接收端为了保证报文的可靠传输,对报文采用了什么编码方式。
响应报头

用于服务器传递自身信息的响应,常见的响应报头:

  • Location:用于重定向接受者到一个新的位置,常用在更换域名的时候
  • Server:包含可服务器用来处理请求的系统信息,与User-Agent请求报头是相对应的
实体报头

实体报头用来定于被传送资源的信息,既可以用于请求也可用于响应。请求和响应消息都可以传送一个实体,常见的实体报头为:

  • Content-Type:发送给接收者的实体正文的媒体类型
  • Content-Lenght:实体正文的长度
  • Content-Language:描述资源所用的自然语言,没有设置则该选项则认为实体内容将提供给所有的语言阅读
  • Content-Encoding:实体报头被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。
  • Last-Modified:实体报头用于指示资源的最后修改日期和时间
  • Expires:实体报头给出响应过期的日期和时间

5.应用举例

要想查看网页或者手机请求网络的请求报文和响应报文有很多种方法,这里推荐采用Fiddler,在Android利用Fiddler进行网络数据抓包这篇文章中详尽介绍了如何使用Fiddler,在这里就不赘述了。打开Fiddler,然后用浏览器访问我的CSDN博客网站:这里写图片描述点击红色画笔的区域就可以看到请求报文和响应报文了

请求报文:

 
    
GET http://blog.csdn.net/itachi85 HTTP/1.1 //请求行
Host: blog.csdn.net //请求报头
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36 QQBrowser/9.3.6872.400
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1443768140949; uuid_tt_dd=5028529250430960147_20151002;
...省略

很容易看出访问的是我的博客地址http://blog.csdn.net/itachi85,请求的方法是GET,因为是GET方法所以并没有请求数据。

响应报文:

 
    
HTTP/1.1 200 OK //状态行
Server: openresty //响应报头
Date: Sun, 27 Mar 2016 08:26:54 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Vary: Accept-Encoding
Cache-Control: private
X-Powered-By: PHP 5.4.28
Content-Encoding: gzip
//不能省略的空格
28b5
}ysI 1ߡFsgl n- ]{^_ { 'z! C , m# 0 !l ` 4x ly .ݪ*
ڴzAt_Xl * 9'O ɬ ' ק 3 ^1a
...省略

响应报文没什么可说的,接下来我们配置好手机网络代理,访问一个应用的界面

请求报文:

 
    
POST http://patientapi.shoujikanbing.com/api/common/getVersion HTTP/1.1 //请求行
Content-Length: 226 //请求报头
Content-Type: application/x-www-form-urlencoded
Host: patientapi.shoujikanbing.com
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; MI NOTE LTE Build/KTU84P) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Accept-Encoding: gzip
//不能省略的空格,下面是请求数据
clientversion=2_2.0.0&time=1459069342&appId=android&channel=hjwang&sessionId=0d1cee1f31926ffa8894c64804efa855101d56eb21caf5db5dcb9a4955b7fbc9&token=b191944d680145b5ed97f2f4ccf03058&deviceId=869436020220717&type=2&version=2.0.0

从请求报文的请求行来看,请求的方法是POST,请求地址为http://patientapi.shoujikanbing.com/api/common/getVersion,很显然是获取版本信息的接口。

响应报文:

 
    
HTTP/1.1 200 OK //状态行
Server: nginx //响应报头
Date: Sun, 27 Mar 2016 09:02:20 GMT
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Set-Cookie: sessionId=0d1cee1f31926ffa8894c64804efa855101d56eb21caf5db5dcb9a4955b7fbc9; expires=Mon, 28-Mar-2016 09:02:20 GMT; Max-Age=86400; path=/; domain=.shoujikanbing.com
Set-Cookie: PHPSESSID=0d1cee1f31926ffa8894c64804efa855101d56eb21caf5db5dcb9a4955b7fbc9; path=/; domain=.shoujikanbing.com
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Encoding: gzip
//不能省略的空格
17f //实体报文编码格式为gzip所以显示在这里的响应数据是乱码
mP N @ "E ? n m 1
w ( HL (1^ P nK E ѷ93'3gNLH 7P $c \ T 4a6 L:+ 1dY%$g h H +
...省略

响应报文的实体采用的编码格式为为gzip,所以在Fiddler软件中显示的是乱码。

上一篇我们了解了HTTP协议原理,这一篇我们来讲讲Apache的HttpClient和Java的HttpURLConnection,这两种都是我们平常请求网络会用到的。无论我们是自己封装的网络请求类还是第三方的网络请求框架都离不开这两个类库。

1.HttpClient

Android SDK中包含了HttpClient,在Android6.0版本直接删除了HttpClient类库,如果仍想使用则解决方法是:

  • 如果使用的是eclipse则在libs中加入org.apache.http.legacy.jar这个jar包在:**sdk\platforms\android-23\optional目录中(需要下载android6.0的SDK)
  • 如果使用的是android studio则 在相应的module下的build.gradle中加入:
 
    
android {
useLibrary 'org.apache.http.legacy'
}
HttpClient的GET请求

首先我们来用DefaultHttpClient类来实例化一个HttpClient,并配置好默认的请求参数:

 
    
//创建HttpClient
private HttpClient createHttpClient() {
HttpParams mDefaultHttpParams = new BasicHttpParams();
//设置连接超时
HttpConnectionParams.setConnectionTimeout(mDefaultHttpParams, 15000);
//设置请求超时
HttpConnectionParams.setSoTimeout(mDefaultHttpParams, 15000);
HttpConnectionParams.setTcpNoDelay(mDefaultHttpParams, true);
HttpProtocolParams.setVersion(mDefaultHttpParams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(mDefaultHttpParams, HTTP.UTF_8);
//持续握手
HttpProtocolParams.setUseExpectContinue(mDefaultHttpParams, true);
HttpClient mHttpClient = new DefaultHttpClient(mDefaultHttpParams);
return mHttpClient;
}

接下来创建HttpGet和HttpClient,请求网络并得到HttpResponse,并对HttpResponse进行处理:

 
    
private void useHttpClientGet(String url) {
HttpGet mHttpGet = new HttpGet(url);
mHttpGet.addHeader( "Connection", "Keep-Alive");
try {
HttpClient mHttpClient = createHttpClient();
HttpResponse mHttpResponse = mHttpClient.execute(mHttpGet);
HttpEntity mHttpEntity = mHttpResponse.getEntity();
int code = mHttpResponse.getStatusLine().getStatusCode();
if ( null != mHttpEntity) {
InputStream mInputStream = mHttpEntity.getContent();
String respose = converStreamToString(mInputStream);
Log.i( "wangshu", "请求状态码:" + code + "\n请求结果:\n" + respose);
mInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

converStreamToString方法将请求结果转换成String类型:

 
    
private String converStreamToString(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader( new InputStreamReader(is));
StringBuffer sb = new StringBuffer();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
String respose = sb.toString();
return respose;
}

最后我们开启线程访问百度:

 
    
new Thread( new Runnable() {
@Override
public void run() {
useHttpClientGet( "http://www.baidu.com");
}
}).start();

请求的返回结果,请求状态码为200,结果就是个html页,这里只截取了部分html代码:
这里写图片描述

GET请求的参数暴露在URL中,这有些不大妥当,而且URL的长度也有限制:长度在2048字符之内,在HTTP 1.1后URL长度才没有限制。一般情况下POST可以替代GET,接下来我们来看看HttpClient的POST请求。

HttpClient的POST请求

post请求和get类似就是需要配置要传递的参数:

 
    
private void useHttpClientPost(String url) {
HttpPost mHttpPost = new HttpPost(url);
mHttpPost.addHeader("Connection", "Keep-Alive");
try {
HttpClient mHttpClient = createHttpClient();
List<NameValuePair> postParams = new ArrayList<>();
//要传递的参数
postParams.add(new BasicNameValuePair("username", "moon"));
postParams.add(new BasicNameValuePair("password", "123"));
mHttpPost.setEntity(new UrlEncodedFormEntity(postParams));
HttpResponse mHttpResponse = mHttpClient.execute(mHttpPost);
HttpEntity mHttpEntity = mHttpResponse.getEntity();
int code = mHttpResponse.getStatusLine().getStatusCode();
if (null != mHttpEntity) {
InputStream mInputStream = mHttpEntity.getContent();
String respose = converStreamToString(mInputStream);
Log.i("wangshu", "请求状态码:" + code + "\n请求结果:\n" + respose);
mInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

2.HttpURLConnection

Android 2.2版本之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:

 
    
private void disableConnectionReuseIfNecessary() {
// 这是一个2.2版本之前的bug
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
System.setProperty("http.keepAlive", "false");
}
}

所以在Android 2.2版本以及之前的版本使用HttpClient是较好的选择,而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择,它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。另外在Android 6.0版本中,HttpClient库被移除了,HttpURLConnection则是以后我们唯一的选择。

HttpURLConnection的POST请求

因为会了HttpURLConnection的POST请求那GET请求也就会了,所以我这里只举出POST的例子
首先我们创建一个UrlConnManager类,然后里面提供getHttpURLConnection()方法用于配置默认的参数并返回HttpURLConnection:

 
    
public static HttpURLConnection getHttpURLConnection(String url){
HttpURLConnection mHttpURLConnection= null;
try {
URL mUrl= new URL(url);
mHttpURLConnection=(HttpURLConnection)mUrl.openConnection();
//设置链接超时时间
mHttpURLConnection.setConnectTimeout( 15000);
//设置读取超时时间
mHttpURLConnection.setReadTimeout( 15000);
//设置请求参数
mHttpURLConnection.setRequestMethod( "POST");
//添加Header
mHttpURLConnection.setRequestProperty( "Connection", "Keep-Alive");
//接收输入流
mHttpURLConnection.setDoInput( true);
//传递参数时需要开启
mHttpURLConnection.setDoOutput( true);
} catch (IOException e) {
e.printStackTrace();
}
return mHttpURLConnection ;
}

因为我们要发送POST请求,所以在UrlConnManager类中再写一个postParams()方法用来组织一下请求参数并将请求参数写入到输出流中:

 
    
public static void postParams(OutputStream output,List<NameValuePair>paramsList) throws IOException{
StringBuilder mStringBuilder= new StringBuilder();
for (NameValuePair pair:paramsList){
if(!TextUtils.isEmpty(mStringBuilder)){
mStringBuilder.append( "&");
}
mStringBuilder.append(URLEncoder.encode(pair.getName(), "UTF-8"));
mStringBuilder.append( "=");
mStringBuilder.append(URLEncoder.encode(pair.getValue(), "UTF-8"));
}
BufferedWriter writer= new BufferedWriter( new OutputStreamWriter(output, "UTF-8"));
writer.write(mStringBuilder.toString());
writer.flush();
writer.close();
}

接下来我们添加请求参数,调用postParams()方法将请求的参数组织好传给HttpURLConnection的输出流,请求连接并处理返回的结果:

 
    
private void useHttpUrlConnectionPost(String url) {
InputStream mInputStream = null;
HttpURLConnection mHttpURLConnection = UrlConnManager.getHttpURLConnection(url);
try {
List<NameValuePair> postParams = new ArrayList<>();
//要传递的参数
postParams.add( new BasicNameValuePair( "username", "moon"));
postParams.add( new BasicNameValuePair( "password", "123"));
UrlConnManager.postParams(mHttpURLConnection.getOutputStream(), postParams);
mHttpURLConnection.connect();
mInputStream = mHttpURLConnection.getInputStream();
int code = mHttpURLConnection.getResponseCode();
String respose = converStreamToString(mInputStream);
Log.i( "wangshu", "请求状态码:" + code + "\n请求结果:\n" + respose);
mInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

最后开启线程请求网络:

 
    
private void useHttpUrlConnectionGetThread() {
new Thread( new Runnable() {
@Override
public void run() {
useHttpUrlConnectionPost( "http://www.baidu.com");
}
}).start();
}

这里我们仍旧请求百度,看看会发生什么?
这里写图片描述

mInputStream = mHttpURLConnection.getInputStream() 这句代码报错了,找不到文件。打开Fiddler来分析一下,不了解Fiddler和HTTP协议原理的请查看Android网络编程(一)HTTP协议原理这篇文章。

我们的请求报文:
这里写图片描述

看来请求报文没有问题,再来看看响应报文:

这里写图片描述

报504错误,读取响应的数据报错,对于我们这次请求服务端不能返回完整的响应,返回的数据为0 bytes,所以mHttpURLConnection.getInputStream() 也读不到服务端响应的输入流。当然这次错误是正常的,百度没理由处理我们的这次POST请求。


文章内容包括:
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); }

}



简单介绍

看了深入理解Android网络编程感觉不错。今天对Android网络编程进行了要点记录。


内容

Android基于网络技术和编程实践

要点 定义 描写叙述



 IP协议
用于报文交换网络的一种面向数据的协议 
TCP协议传输控制协议,传输层通信协议。 
UDP协议用户数据报协议。传输层协议。 
SMTP协议简单邮件传输协议 
SOCKET 套接字应用层与TCP/IP协议族通信的中间软件抽象层。

类型有两种:TCP套接字和UDP套接字。

TCP套接字 在保证可靠性上,採用超时重传和捎带确认机制,在流量控制上,採用滑动窗体协议;在拥塞控制上。採用慢启动算法。
UDP套接字 将网络数据流量压缩成数据包的形式。
FTP协议文件传输协议,传输协议的应用层。

可直接操作Apache的包
Telnet协议远程登陆服务的标准协议可直接操作Apache的包
C++和JAVA 套接字的差别Socket是Winsock里的原始套接字开发接口Api。1、接口不同,java本身无socket通讯底层实现,而是调用系统底层的winsock api二次封装;C++更接近系统层面的winsock,能够进行很多其他的底层扩展和控制。
2、java开发出来的能够在支持java虚拟机上执行。
UDP和TCP差别 1、tcp可靠,udp不可靠
2、tcp支持的应用协议有:telnet,ftp,smtp,tcp可用户数据传输量大
3、udp支持的应用协议:nfs(网络文件系统),snmp(简单网络管理系统),dns(主域名称系统),tftp通用文件传输协议等,用户数据传输量少。


4、tcp是java.net.ServerSocket(用于server端)和java.net.Socket(用于client);UDP是java.net.DatagramSocket。
5、流模式与数据报模式:TCP保证数据正确性,UDP可能丢包;TCP 保证数据顺序。UDP不保证。


   


Android基本Web技术和编程实践

要点 定义 描写叙述
HTTP协议分布式超媒体信息系统的应用层协议。
http://主机[":"port][路径]
特点:
1、支持C/S模式
2、简单高速
3、灵活
4、无连接
5、无状态
两类报文:请求报文,响应报文。

HTTP编程
支持的功能有:自己主动转向、https协议、代理server,方法(get、post、put、head、opption、connect)
实现:数据请求,数据上传,数据删除等操作。
可操作的类:HttpClient和URLConnection。

JSONJavaScript对象表示法。轻量级的文本数据交换格式。

可操作的类:
JSONObject、JSonArray、JSONArray、JSONTokener、JSONStringer。
SOAP简单对象訪问协议。应用层协议。

包括的元素:Envelope、Header、Body、Fault。
HTML超文本标记语言解析Html文档的方法有:
1、正則表達式
2、字符串定位
3、HTML Parser解析器
4、Jsoup解析器
怎样加密?数据传输可加入某加密算法进行加密加密算法有:自己定义位移(其他)、MD5(不可逆)、对称加密(DES、AES)、非对称加密(RSA)
可引入crypto包。
   

Android常见网络接口编程

要点 定义 描写叙述
XML扩展性标记语音使用XmlSerializer创建XML文件
DOM解析一种解析XML的方式,特点:将XML文件的全部内容以文档树的方式存放在内存中,然后用户使用DOM API遍历XML树、检索所需的数据。

所需的类:
DocumentBuilderFactory、Document、Element、NodeList.
SAX解析一种解析XML的方式,特点:基于事件驱动,边载入边解析。所需的类:
XMLReader、ContentHandler、DTDHandler、ErrorHandler、EntityResolver
PULL解析同上 
RSS简易信息聚合。一种描写叙述和同步站点内容的格式。订阅:博客、新闻。 
Email编程顾名思义:发送邮件。1、使用系统自带的邮件服务。
2、採用javamail功能包进行发送邮件。

加解密加密是通过加密算法和加密密钥将明文转变为密文的过程,解密相反。1、Base64加密(字符表示二进制数据)
2、DES 是数据加密标准
3、AES是高级加密标准
4、MD5使用确保信息传输完整性
OAuth认证一个开放标准。同意用户第三方使用。四个步骤:
1、获取未授权的请求token
2、获取用户授权的请求token
3、用授权的请求token换认证token
4、使用认证token訪问
DOM和SAX的差别载入方式不同。DOM一次性载入全部内容。SAX边载入边读取。

 
   


Android网络模块编程

要点 定义 描写叙述
USB编程通用串行总线,一种主从结构。涉及的类:
UsbManager、UsbAccessory、UsbDevice、UsbEndpoint
WIFI编程802.11b标准,无线通信的工业标准,特点:速度快、可靠、距离远。
wifi直连技术是通过wifi直接互通。

相关类:WirelessSettings、WifiSettings、AccessPointDialog、WifiEnabler、WifiLayer、WifiManager、WifiState、WifiService、WifiMonitor、WifiNativie、ScanResult、WifiInfo、wifiConfiguration。


用户角度看五层:硬件驱动程序、wpa_supplicant、JNI、Wifi Api、WifiSettings应用程序。
直连涉及的类:WifiP2pManager

蓝牙编程一种支持设备短距离通信的无线技术涉及的类:BluetoothAdapter、BluetoothDevice、BluetoothSocket
NFC编程近距离无线通信的技术。3种工作模式:主动、被动、双向。应用:接触通过、接触支付、接触连接、接触浏览。涉及的类:NfcAdapter、NdefMessage、NdefRecord
   

基于Sip协议的Voip应用

要点 定义 描写叙述
sip协议会话发起协议。用于建立、改动和终止包括视频、语音、即时通信、在线游戏和虚拟现实等多媒体元素在内的交互会话。应用层的信令控制协议。
使用什么协议传输的?UDP协议和TCP协议 
H.323协议和SIp协议的比較1、H.323採用ASN.1和压缩编码规则的二进制方法表示其消息,sip基于文本协议。
2、h.323由多点控制单元,全部终端都向mcu发送控制消息。sip是分布式的呼叫模型,具备组播功能。
 
sip会话的构成?四个组件:
1、SIP用户代理(终端设备)
2、SIP注冊server(包括域中全部用户代理的位置的数据库)
3、SIP代理server(接口UA的会话请求并查询SIP注冊server,获取收件方UA的地址信息)
4、SIP重定向server(同意Sip代理server讲sip会话邀请信息定向到外部域)
 
   

基于XMPP协议的即时通信

要点 定义 描写叙述
XMPP协议可扩展消息处理现场协议一种以xml为基础的开放式实时通信协议。

Android的XMPPclient地址http://asmack.freakempire.de/ 
   


Android对HTML的处理

要点 定义 描写叙述
HTML处理关键类Android HTML处理关键类在源代码frameworks/base/core/java/android/webkit文件夹下 
关键类以及描写叙述1、AccessibilityInjector 为WebView注入能够訪问的javaScript和其相关内容。


2、AutoCompletePopup 依据输入内容自己主动不全
3、BrowserFrame  对WebCore中Frame对象的java层封装。
4、CacheManager Cache管理对象。
5、CallbackProxy 处理WebCore与UI线程消息的代理类
6、CerTool  WebView证书工具
7、ClientCerRequestHandler  处理client证书请求
8、ConsoleMessage  javaScript控制台消息
9、Cookiemanager  Cookies管理对象
10、CookieManagerClassic  Cookiemanager  扩展实现类
11、CookieSyncManager  Cookies同步管理对象,负责同步RAM和FLASH之间的Cookies数据。


12、DataSorter 日期排序
13、DebugFlags 调试标志
14、DevicemotionAndOrientationManager 实现DeviceMotion和DeviceOrientation
15、DeviceMotionService 实现SensorEventListener接口。处理方向变化。
16、Downloadlistener 下载监听
17、FindActionModeCallback 搜索动作回调
18、GeolocationPermissions WebView的地理位置JavaScript Api的权限管理
19、GeolocationPermissionsClassic  GeolocationPermissions 扩展类。处理UI线程中调用的WebKit线程。


20、GeolocationService 封装位置监听。
21、HTML5Audio 音频支持类
22、HTML5VideoFullScreen  全屏视频视图
23、HTML5VideoInline 内嵌视频视图
24、HTML5VideoView 浏览器视频视图
25、HTML5VideoViewProxy 视频视图处理类
26、HttpAuthHandler 处理http认证请求
27、JWebCoreJavaBridge 用于java与webcore库中timer和Cookies对象交互的桥接
28、JniUtil  JNI使用的有用类,用于获取cache文件夹等C代码无法直接获取的信息、以及读取资源包中的文件。
29、JsPromptResult js结果提示,提示javascript执行结果。
30、JsResult  js结果,用户交互。
31、KeyStoreHandler 负责证书安装到系统密钥存储区。从网络读取证书传给CertTool。
32、L10nUtils 负责字符串国际化

33、MimeTypeMap MIME类型映射
34、MockGeolocation 模拟地理位置信息
36、MustOverrideException 扩展执行时错误
37、OverScrollGlow 用于实现OverScroller结果
38、Plugin  定义插件的类
39、PluginData  插件数据
40、PluginFullScreenHolder  获取插件视图的容器的大小。负责显示等操作。
41、PluginList  维护插件列表
42、PluginManager 插件管理类
43、PluginStub WebView的实现插件的接口
44、QuadF 定义一个四边形
45、SearchBox 搜索对话框接口
46、SearchBoxImpl 搜索对话框接口实现
47、SelectActionModeCallback 选择动作回调
48、SslCertLookupTable 存储用户是否使用一个证书的决定
49、SslClientCertLookupTable 保存client证书的用户选择
50、SslErrorHandler 处理SSL错误
51、URLUtil URL使用处理类
52、valueCallback 异步返回数据值的回调接口
53、ViewManager 视图管理类,管理插件视图
54、ViewStateSerializer WebView视图序列化和反序列化
55、WebBackForwardList WebView对象中显示的历史数据列表
56、WebBackForwardListClient 浏览历史处理的client接口
57、WebChromeClient Chromeclient
58、WebCoreThreadWatchdog WebCore看门狗
59、WebHistoryItem 一条网页数据
60、WebIconDatabase 图标数据库管理对象
61、WebIconDatabaseClassic  WebIconDatabase 扩展类
62、WebResourceResponse 封装资源的响应信息
63、WebSettings WebView的管理设置数据。通过JNI接口从底层获取。
64、WebStorage 数据库
65、WebSyncManager 数据同步管理对象。用于RAM数据和FLASH数据的同步操作。
66、WebTextView 在HTML文本输入控件激活时显示系统原生编辑组件。
67、WebView 视图对象
68、WebViewClient Web视图客户对象。在web视图中有事件产生时,该对象获得通知。


69、WebViewDatabase 数据库
70、WebViewFactory WebView嵌入Fragment中。
71、WebViewFragment ebView嵌入Fragment中。


72、ZoomControlBase 缩放控件接口。
73、ZoomControlEmbedded 内置缩放控件。
74、ZoomManager 维护WebView的缩放状态。

   


Android 网络处理分析

要点 定义 描写叙述
网络处理关键类Android HTML网络处理关键类在源代码frameworks/base/core/java/android/net文件夹下 
关键类以及描写叙述1、ConnectivityManager 用于查询网络连接的状态。
2、Credentials UNIX身份信息
3、DhcpInfo  一个Dhcp请求结果的对象
4、LocalServerSocket  创建下行unix域套接字的非标准类
5、LocalSocket  在unix域命名空间创建一个非serversocket
6、LocalSocketAddress  socket地址
7、MailTo  解析mailto协议格式的url
8、NetworkInfo 描写叙述网络接口状态
9、Proxy 訪问用户及默认代理设置的类
10、SSLCertificateSocketFactory  实现了ssl操作的额外细节
11、SSLSessionCache  ssl会话缓存
12、TrafficStats  网络流量统计
13、Uri   一个不可变的url引用
14、Uri.Builder  创建url引用的辅助类
15、UrlQuerySanitizer  过滤url查询
16、UrlQuerySanitizer.IllegalCharacterValueSanitizer 用它们包括的字符过滤非法值
17、UrlQuerySanitizer.ParameterValuePair 參数值对的简单元组
18、VpnService  应用扩展和建立自己的vpn解决方式的基类
19、VpnService.Builder  创建一个vpn接口的辅助类
 
Android封装的http处理类1、AndroidHttpClient(本质上是Apache DefaultHttpClient类在Android上的实现)
2、DefaultHttpClient(一个Httpclient)
3、SSL认证信息处理类(SSL协议的实现与数字证书相关,涉及的类:SslCertificate、SslError)
1、AndroidHttpClient不能在主线程中执行,DefaultHttpClient在主线程中执行。
2、AndroidHttpClient通过静态方法newInstance获得实例。而DefaultHttpClient创建对象。
3、DefaultHttpClient默认启动Cookie,但默认不保留Cookie,如要保留需添加CookieStore到HttpContext中。

Android RTP协议实时传输协议。用来为ip网络的语音、图像、传真等多种实时传输的多媒体数据提供端到端的实时传输服务。


RTP协议具体说明了传递音频和视频的标准数据包格式。

涉及的类:AudioCodec、AudioStream、AudioGroup、RtpStream。




一、TCP/IP协议介绍

在介绍HTTP协议之前,先简单说一下TCP/IP协议的相关内容

TCP/IP协议是分层的,从底层至应用层分别为:物理层,链路层,网络层,传输层和应用层

\

从应用层至物理层,数据是一层层封装,封装的方式一般都是在原有的数据的前面加一个数据控制头,数据封装格式如下:

\

其中,对于TCP传输协议,客户端在与服务器建立连接前需要经过三次握手,过程如下:

\

其中,

SYN是请求同步的意思,synchronize(同步)的缩写

ACK是确认同步的意思,acknowledgement(确认)的缩写

---TCP(Transmission Control Protocol)传输控制协议---

TCP是主机对主机层的传输控制协议,提供可靠的连接服务

TCP的三次握手

第一次握手:

建立连接时,客户端A发生SYN包(SYN=j)到服务器B

并进入SYN_SEND状态,等待服务器B确认

第二次握手:

服务器B收到SYN包,必须确认客户A的SYN,ACK=j+1

同时自己也发送一个SYN包,SYN=k

即,SYN+ACK包,此时服务器进入SYN_RECV状态

第三次握手:

客户端A收到服务器B的SYN+ACK包

向服务器B发送确认包ACK(ACK=k+1)

此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手

TCP的四次挥手

客户端A发送一个FIN.用来关闭客户A到服务器B的数据传送(报文段4)

服务器B收到这个FIN. 它发回一个ACK,确认序号为收到的序号+1(报文段5)。和SYN一样,一个FIN将占用一个序号

服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)

客户端A发回ACK报文确认,并将确认序号设置为序号加1(报文段7)

二、HTTP协议

超文本传输协议(Hypertext Transfer Protocol,简称HTTP协议)是应用层协议

HTTP协议格式比较简单,格式如下

\

HTTP最基本的方法有4种

GET,查,一般用于获取/查询资源信息

GET是向服务器发索取数据的一种请求

安全的,该操作用于获取信息而非修改信息

冪等的,对同一URL的多个请求应该返回同样的结果

GET请求会附在URL之后,就是把数据放置在HTTP协议头中

以?分隔URL和传输数据,参数之间以&相连,空格转换为+,中文字符改为该符号以16进制表示的ASCII

POST,改,一般用于更新资源信息

POST是向服务器提交数据的一种请求,在FORM(表单)中,Method默认为“GET”

实质上,GET和POST只是发送机制不同,并不是一个取一个发!

POST把提交的数据放置在HTTP包的包体中

POST的安全性比GET要高。通过GET提交数据,用户名密码将明文出现在URL上

PUT,增

DELETE,删




4、Android代码实现WAP方式联网

0.  前言

无论是移动、联通还是电信,都至少提供了两种类型的的APNWAP方式和NET方式

其中NET方式跟WIFI方式一样,无需任何设置,可自由访问所有类型网站,而WAP方式需要手机先设置代理服务器和端口号等信息,并且只能访问HTTP协议类型的网站。本篇将介绍如何在代码中实现WAP方式联网。

 

1.  准备知识

国内三大运营商关于WAPNET的信息如下:

1移动的WAP名称是CMWAPNET名称是CMNET

2联通的WAP名称是UNIWAPNET名称是UNINET;联通3GWAP名称是3GWAPNET名称是3GNET

3电信的WAP名称是CTWAPNET名称是CTNET

其中三家运营商WAP端口均为80,移动和联通的WAP代理服务器都是10.0.0.172,电信的WAP代理服务器是10.0.0.200

 

在Android系统中,对于获取手机的APN设置,需要通过ContentProvider来进行数据库查询,查询的URI地址如下。

[java]  view plain  copy
  1. //取得全部的APN列表:content://telephony/carriers;  
  2. //取得当前设置的APN:content://telephony/carriers/preferapn;  
  3. //取得current=1的APN:content://telephony/carriers/current;  

2.  代码中实现WAP联网

下面我们的代码就是使用APNManager类获取当前首选的APN设置,并继承HttpClient实现我们自己的代理ProxyHttpClient

首先来看下APNManager类的实现,这个类的主要功能是获得APN的代理服务器和端口号

[java]  view plain  copy
  1. import android.content.ContentResolver;    
  2. import android.content.Context;    
  3. import android.database.Cursor;    
  4. import android.net.ConnectivityManager;    
  5. import android.net.NetworkInfo;    
  6. import android.net.Uri;    
  7.     
  8. public class APNManager {    
  9.     
  10.     public static final Uri PREFERRED_APN_URI;    
  11.     
  12.     private String mApn; // 接入点名称    
  13.     
  14.     private String mPort; // 端口号    
  15.     
  16.     private String mProxy; // 代理服务器    
  17.     
  18.     private boolean mUseWap; // 是否正在使用WAP    
  19.     
  20.     static {  //取得当前设置的APN  
  21.         PREFERRED_APN_URI = Uri.parse("content://telephony/carriers/preferapn");   
  22.     }    
  23.     
  24.     public APNManager(Context context) {    
  25.         checkNetworkType(context);    
  26.     }    
  27.     
  28.     /**  
  29.      * 获得当前设置的APN相关参数  
  30.      * @param context  
  31.      */    
  32.     private void checkApn(Context context) {    
  33.         ContentResolver contentResolver = context.getContentResolver();    
  34.         Uri uri = PREFERRED_APN_URI;    
  35.         String[] apnInfo = new String[3];    
  36.         apnInfo[0] = "apn";    
  37.         apnInfo[1] = "proxy";    
  38.         apnInfo[2] = "port";    
  39.         //使用ContentResolver获得游标对象,之后就是查询操作了  
  40.         //分别查询当前手机所设置的APN、Proxy和Port  
  41.         //如果手机的Proxy没有设置,则需要根据APN来决定当前应该连接的代理服务器地址和端口号  
  42.         Cursor cursor = contentResolver.query(uri, apnInfo, nullnullnull);    
  43.         if (cursor != null) {    
  44.             while (cursor.moveToFirst()) {    
  45.                 this.mApn = cursor.getString(cursor.getColumnIndex("apn"));    
  46.                 this.mProxy = cursor.getString(cursor.getColumnIndex("proxy"));    
  47.                 this.mPort = cursor.getString(cursor.getColumnIndex("port"));    
  48.     
  49.                 // 代理为空    
  50.                 if ((this.mProxy == null) || (this.mProxy.length() <= 0)) {    
  51.                     String apn = this.mApn.toUpperCase();    
  52.                         
  53.                     // 中国移动WAP设置:APN:CMWAP;代理:10.0.0.172;端口:80    
  54.                     // 中国联通WAP设置:APN:UNIWAP;代理:10.0.0.172;端口:80    
  55.                     // 中国联通WAP设置(3G):APN:3GWAP;代理:10.0.0.172;端口:80    
  56.                     if ((apn.equals("CMWAP")) || (apn.equals("UNIWAP")) || (apn.equals("3GWAP"))) {    
  57.                         this.mUseWap = true;    
  58.                         this.mProxy = "10.0.0.172";    
  59.                         this.mPort = "80";    
  60.                         break;    
  61.                     }    
  62.                         
  63.                     // 中国电信WAP设置:APN(或者接入点名称):CTWAP;代理:10.0.0.200;端口:80    
  64.                     if (apn.equals("CTWAP")) {    
  65.                         this.mUseWap = true;    
  66.                         this.mProxy = "10.0.0.200";    
  67.                         this.mPort = "80";    
  68.                         break;    
  69.                     }    
  70.                         
  71.                 }    
  72.                 this.mPort = "80";    
  73.                 this.mUseWap = true;    
  74.                 break;    
  75.             }    
  76.     
  77.         }    
  78.     
  79.         this.mUseWap = false;    
  80.         cursor.close();    
  81.     }    
  82.     
  83.     /**  
  84.      * 检测当前使用的网络类型是WIFI还是WAP  
  85.      * @param context  
  86.      */    
  87.     private void checkNetworkType(Context context) {    
  88.         NetworkInfo networkInfo = ((ConnectivityManager) context    
  89.                 .getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();    
  90.         if (networkInfo != null) {    
  91.             if (!"wifi".equals(networkInfo.getTypeName().toLowerCase())) {    
  92.                 checkApn(context);    
  93.                 return;    
  94.             }    
  95.             this.mUseWap = false;    
  96.         }    
  97.     }    
  98.     
  99.     /**  
  100.      * 判断当前网络连接状态  
  101.      * @param context  
  102.      * @return  
  103.      */    
  104.     public static boolean isNetworkConnected(Context context) {    
  105.         NetworkInfo networkInfo = ((ConnectivityManager) context    
  106.                 .getApplicationContext().getSystemService("connectivity"))    
  107.                 .getActiveNetworkInfo();    
  108.         if (networkInfo != null) {    
  109.             return networkInfo.isConnectedOrConnecting();    
  110.         }    
  111.         return false;    
  112.     }    
  113.     
  114.     public String getApn() {    
  115.         return this.mApn;    
  116.     }    
  117.     
  118.     public String getProxy() {    
  119.         return this.mProxy;    
  120.     }    
  121.     
  122.     public String getProxyPort() {    
  123.         return this.mPort;    
  124.     }    
  125.     
  126.     public boolean isWapNetwork() {    
  127.         return this.mUseWap;    
  128.     }    
  129. }    

通过APNManager类获取到或者设置好当前手机的WAP设置的代理和端口之后,就可以构造我们自己的代理类了,这个类定义为ProxyHttpClient,在该类的构造函数中,首先获得APNManager的实例,然后获取到APNManager类中的信息,即获取代理服务器proxy和端口值port通过这两个参数构造HttpHost实例,并将host实例设置为ConnRouteParams.DEFAULT_PROXY的值。具体实现如下:

[java]  view plain  copy
  1. import android.content.Context;    
  2. import android.text.TextUtils;    
  3. import android.util.Log;    
  4.     
  5. import org.apache.http.HttpHost;    
  6. import org.apache.http.conn.params.ConnRouteParams;    
  7. import org.apache.http.impl.client.DefaultHttpClient;    
  8. import org.apache.http.params.HttpConnectionParams;    
  9. import org.apache.http.params.HttpParams;    
  10. import org.apache.http.params.HttpProtocolParams;    
  11.     
  12. public class ProxyHttpClient extends DefaultHttpClient {    
  13.         
  14.     private static final int HTTP_TIMEOUT_MS = 30 * 1000;    
  15.         
  16.     private static final int BUFFER_SIZE = 1024 * 8;    
  17.     
  18.     private static final String TAG = ProxyHttpClient.class.getSimpleName();    
  19.     
  20.     private RuntimeException mLeakedException = new IllegalStateException("ProxyHttpClient created and never closed");    
  21.     
  22.     private String mPort;    
  23.     
  24.     private String mProxy;    
  25.     
  26.     private boolean mUseWap;    
  27.     
  28.     public ProxyHttpClient(Context context) {    
  29.         this(context, nullnull);    
  30.     }    
  31.     
  32.     public ProxyHttpClient(Context context, APNManager manager) {    
  33.         this(context, null, manager);    
  34.     }    
  35.     
  36.     public ProxyHttpClient(Context context, String userAgent) {    
  37.         this(context, userAgent, null);    
  38.     }    
  39.     
  40.     public ProxyHttpClient(Context context, String userAgent, APNManager manager) {    
  41.         if (manager == null) {    
  42.             manager = new APNManager(context);    
  43.         }    
  44.             
  45.         this.mUseWap = manager.isWapNetwork();    
  46.         this.mProxy = manager.getProxy();    
  47.         this.mPort = manager.getProxyPort();    
  48.         if (this.mUseWap) {    
  49.             HttpHost host = new HttpHost(this.mProxy, Integer.valueOf(this.mPort).intValue());    
  50.             getParams().setParameter(ConnRouteParams.DEFAULT_PROXY, host); // 设置代理    
  51.         }    
  52.         HttpConnectionParams.setConnectionTimeout(getParams(), HTTP_TIMEOUT_MS);    
  53.         HttpConnectionParams.setSoTimeout(getParams(), HTTP_TIMEOUT_MS);    
  54.         HttpConnectionParams.setSocketBufferSize(getParams(), BUFFER_SIZE);    
  55.         if (!TextUtils.isEmpty(userAgent)) {    
  56.             HttpProtocolParams.setUserAgent(getParams(), userAgent);    
  57.         }    
  58.     }    
  59.     
  60.     public void close() {    
  61.         if (this.mLeakedException != null) {    
  62.             getConnectionManager().shutdown();    
  63.             this.mLeakedException = null;    
  64.         }    
  65.     }    
  66.     
  67.     protected HttpParams createHttpParams() {    
  68.         HttpParams params = super.createHttpParams();    
  69.         HttpProtocolParams.setUseExpectContinue(params, false);    
  70.         return params;    
  71.     }    
  72.     
  73.     protected void finalize() throws Throwable {    
  74.         super.finalize();    
  75.         if (this.mLeakedException != null) {    
  76.             Log.e(TAG, "Leak found"this.mLeakedException);    
  77.         }    
  78.     }    
  79. }    

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值