zoukankan      html  css  js  c++  java
  • 第八章 网络的时代—网络开发(2)

    8.3基于最成熟的Web协议—HTTP协议编程

    8.3.1 HTTP协议简单介绍

    超文本传输协定(HTTPHyperTextTransferProtocol)是互联网上应用最为广泛的一种网络协议。

    全部的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种公布和接收HTML页面的方法。

    HTTP是一个client和server端请求和应答的标准(TCP)。client是终端用户,server端是站点。通过使用Web浏览器、网络爬虫或者其它的工具,client发起一个到server上指定port(默认port为80)的HTTP请求。

    我们称这个client为用户代理(useragent)。

    应答的server上存储着一些资源,比方HTML文件和图像。我们称这个应答server为源server。在用户代理和源server中间可能存在多个中间层,比方代理,网关,或者隧道(tunnel)。虽然TCP/IP协议是互联网上最流行的应用,HTTP协议并没有规定必须使用它和基于它支持的层。

    其实,HTTP能够在不论什么其它互联网协议上。或者在其它网络上实现。HTTP仅仅假定其下层协议提供可靠的传输,不论什么能够提供这样的保证的协议都能够被其使用。

    通常,由HTTPclient发起一个请求,建立一个到server指定port(默认是80port)的TCP连接。HTTPserver则在那个port监听client发送过来的请求。

    一旦收到请求,server向client发回一个状态行,比方“HTTP/1.1200 OK”,和响应的消息,消息的消息体可能是请求的文件、错误消息、或者其它一些信息。

    HTTP使用TCP而不是UDP的原因在于打开一个网页必须传送很多数据。而TCP协议提供传输控制。按顺序组织数据,和错误纠正。

    通过HTTP或者HTTPS协议请求的资源,由统一资源标识符(UniformResource IdentifiersURI)来标识。

    HTTP/1.1协议中共定义了八种方法(有时也叫“动作”)来表明Request-URI指定的资源的不同操作方式:

    1OPTIONS

    返回server针对特定资源所支持的HTTP请求方法。也能够利用向Webserver发送'*'的请求来測试server的功能性。

    2HEAD

    向server索要与GET请求相一致的响应,仅仅只是响应体将不会被返回。这一方法能够在不必传输整个响应内容的情况下,就能够获取包括在响应消息头中的元信息。

    3GET

    向特定的资源发出请求。

    注意:GET方法不应当被用于产生“副作用”的操作中,比如在WebApplication中。当中一个原因是GET可能会被网络蜘蛛等任意訪问。

    參见安全方法

    4POST

    向指定资源提交数据进行处理请求(比如提交表单或者上传文件)。数据被包括在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的改动。

    5PUT

    向指定资源位置上传其最新内容。

    6DELETE

    请求server删除Request-URI所标识的资源。

    7TRACE

    回显server收到的请求,主要用于測试或诊断。

    8CONNECT

    HTTP/1.1协议中预留给能够将连接改为管道方式的代理server。

    当某个请求所针对的资源不支持相应的请求方法的时候,server应当返回状态码405MethodNot Allowed);当server不认识或者不支持相应的请求方法的时候,应当返回状态码501NotImplemented)。

    HTTPserver至少应该实现GETHEAD方法。其它方法都是可选的。

    当然。全部的方法支持的实现都应当符合下述的方法各自的语义定义。此外。除了上述方法。特定的HTTPserver还能够扩展自己定义的方法。

    AndroidSDK提供了多个封装的类,能够很方便的实现基于HTML协议的编程。一般在,在Android中针对HTTP进行网络通信有几种方式:一种是通过URL类获取网络资源。一种是使用HttpURLConnection类(一般通过用URL类的openConnection()方法创建一个HttpURLConnection对象)来实现。一种是使用ApacheHTTPclient组件HttpClient实现。以下会对这几种方式做详细的说明。

    8.3.2 使用URL类读取HTTP资源

    URLUniformResourceLocator)对象代表统一资源定位器。它是指向互联网“资源”的指针。通常情况下,URL由协议名,主机,port,资源组成。例如以下:

    http://www.your-host:80/index.php

    URL类经常使用的方法有:

    StringgetFile();//获取此URL的资源名

    StringgetHost();//获取此URL的主机名

    StringgetPath();//获取此URL的路劲

    IntgetPort();//获取此URL的port号

    StringgetProtocol();//获取此URL的协议名称

    StringgetQuery();//获取此URL的查询字符串

    URLConnectionopenConnection();//返回一个URLConnection对象

    InputStreamopenStream();//打开连接,并返回一个用于读取该URL资源的InputStream


    URL对象提供了openStream()方法,就能够读取该URL资源的InputStream,很的方便。以下的代码演示样例。訪问了Web地址“http://www.google.cn/”。并且将server返回的HTML文本输出出来。

    //import

    publicclass URLTest extends Activity {

    @Override

    publicvoid onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    TextViewtv = new TextView(this);

    StringmyString="";

    try{

    //定义获取文件内容的URL

    URLmURL = new URL("http://www.google.cn/");

    //打开URL链接

    //读取数据

    InputStreamis = mURL.openStream();

    BufferedInputStreambis = new BufferedInputStream(is);

    //使用用ByteArrayBuffer缓存

    ByteArrayBufferbaf = new ByteArrayBuffer(50);

    intcurrent = 0;

    while((current= bis.read()) != -1) {

    baf.append((byte)current);

    }

    //将缓存的内容转化为String,UTF-8编码

    myString= EncodingUtils.getString(baf.toByteArray(), "UTF-8");

    }catch(Exception e) {

    myString= e.getMessage();

    }

    //设置屏幕显示

    tv.setText(myString);

    this.setContentView(tv);

    }

    }


    须要特别注意的是,这里仅仅是简单举个样例简单说明怎样读取URL网络资源。能够看到。我们将全部的代码都写到了ActivityonCreate()中。在真实的项目开发过程中,这样的方式是有问题的。因为网络的堵塞,可能会出现ANRApplicationNot Response)错误,导致程序退出。正常的做法,应该是使用异步的方式请求网络数据。后面会有详细的样例说明详细怎样做。


    经验分享:

    为了避免频繁读取字节流。提高读取效率,用BufferedInputStream缓存读到的字节流。

    InputStreamis = mURL.openStream();

    BufferedInputStreambis =new BufferedInputStream(is);

    //准备好BufferdInputStream后,我们就能够用read方法读入网络数据

    ByteArrayBufferbaf = new ByteArrayBuffer(50);

    intcurrent = 0;

    while((current= bis.read())! = -1) {

    baf.append((byte)current);

    }

    因为读到的数据仅仅是字节流,无法直接显示到屏幕上,所以我们得在显示之前将字节流转换为可读取的字符串。

    假设读取的是.txt等文件是UTF-8格式的。就须要对数据进行专门的转换

    myString= EncodingUtils.getString(baf.toByteArray(),"UTF-8");


    8.3.3使用HttpURLConnection类訪问HTTP资源

    HttpURLConnection继承于URLConnection。它在URLConnection的基础上提供了例如以下的便捷的方法。

    voidsetResponseMethod(String method);//设置发送请求

    intgetResponseCode();//获取server的响应代码

    StringgetResponseMessage();//获取server的响应消息

    StringgetResponseMethod();//获取发送请求


    使用HttpURLConnection类訪问HTTP资源的基本过程例如以下:

    1)创建URL以及HttpURLConnection对象。

    2)设置连接參数。

    3)连接到server。

    4)向server写数据。

    5)从server读取数据。

    以下提供一段代码说明详细怎样实现。

    try {

    //创建一个URL对象

    URLurl = new URL(your_url);

    //创建一个URL连接。假设有代理的话能够指定一个代理。

    URLConnectionconnection = url.openConnection(Proxy_yours);

    //对于HTTP连接能够直接转换成HttpURLConnection。这样就能够使用一些HTTP连接特定的方法。如setRequestMethod():HttpURLConnectionconnection = (HttpURLConnection)url.openConnection(Proxy_yours);


    //在開始和server连接之前,可能须要设置一些网络參数

    connection.setConnectTimeout(10000);

    //连接到server

    connection.connect();

    //与server交互:

    OutputStreamoutStream = connection.getOutputStream();

    ObjectOutputStreamobjOutput = new ObjectOutputStream(outStream);

    objOutput.writeObject(newString(“this is a string…”));

    objOutput.flush();

    InputStreamin = connection.getInputStream();

    //处理数据 省略详细代码…

    }catch (Exception e) {

    //网络读写操作往往会产生一些异常,所以在详细编写网络应用时

    //最好捕捉每个详细以採取相应措施

    }


    8.3.4使用ApacheHttpClient

    ApacheHttpClient 是一个开源项目。它对java.net中的类进行了封装,弥补了java.net.*灵活性不足的缺点,更适合在Android中开发网络应用,支持client的HTTP编程,更加方便高效。

    一般的。使用HttpClient进行网络开发的过程例如以下:

    1)创建HttpClient对象。

    2)假设须要发送GET请求,创建HttpGet对象;假设须要发送Post请求。创建HttpPost对象。

    3)假设须要设置请求參数,可使用HttpGetHttpPost共同的setParams(HttpParamsparams)方法来加入请求參数。HttpPost还能够调用setEntity(HttpEntityentity)方法来设置。

    4)调用HttpClient对象的execute(HttpUriRequestrequest)发送请求。运行该方法返回一个HttpResponse

    5)调用HttpResponsegetAllHeaders()getHeaders(Stringname)等方法可获取响应头。调用HttpResponsegetEntity()方法可获取HttpEntity对象,该对象封装了响应的内容。

    以下是一个详细的通用的网络连接类,包括了GETPOST的完整代码及注解。

    //import

    publicclass HttpConnecter {

    /**

    *封装的Get方法

    */

    publicstatic String get(String uri) throws ClientProtocolException,IOException {

    //获取系统默认的HttpClient链接

    HttpClienthttpClient = new DefaultHttpClient();

    HttpGethttpGet = new HttpGet(uri);

    //发送GET请求

    HttpResponsehttpResponse = httpClient.execute(httpGet);

    intstatusCode = httpResponse.getStatusLine().getStatusCode();

    //获取server响应信息。200代表成功响应

    if(statusCode >= 200 && statusCode < 400) {

    StringBuilderstringBuilder = new StringBuilder();

    //httpResponse.getEntity().getContent()用来获取响应的内容

    BufferedReaderreader = new BufferedReader(new InputStreamReader(

    httpResponse.getEntity().getContent(),"UTF-8"));

    for(String s = reader.readLine(); s != null; s = reader.readLine()) {

    //读出内容

    stringBuilder.append(s);

    }

    reader.close();

    Log.d("HttpConnecter","HTTPGET:" + uri.toString());

    Log.d("HttpConnecter","Response:"+ stringBuilder.toString());

    returnstringBuilder.toString();

    }

    returnnull;

    }


    /**

    *封装的Post方法

    */

    publicstatic String post(String uri, List<NameValuePair>formparams)

    throwsClientProtocolException, IOException {

    HttpClienthttpClient = new DefaultHttpClient();

    UrlEncodedFormEntityentity

    =new UrlEncodedFormEntity(formparams,"UTF-8");

    HttpPosthttpPost = new HttpPost(uri);

    //设置请求參数

    httpPost.setEntity(entity);

    //发送Post请求

    HttpResponsehttpResponse = httpClient.execute(httpPost);

    intstatusCode = httpResponse.getStatusLine().getStatusCode();

    if(statusCode >= 200 && statusCode < 400) {

    StringBuilderstringBuilder = new StringBuilder();

    //httpResponse.getEntity().getContent()用来获取响应的内容

    BufferedReaderreader = new BufferedReader(new InputStreamReader(

    httpResponse.getEntity().getContent(),"UTF-8"));

    for(String s = reader.readLine(); s != null; s = reader.readLine()) {

    stringBuilder.append(s);

    }

    reader.close();

    Log.d("HttpConnecter","HTTPPOST:" + uri.toString());

    Log.d("HttpConnecter","Response:"+ stringBuilder.toString());

    returnstringBuilder.toString();

    }

    returnnull;

    }

    }


    从上面的编程过程我们能够看出。使用ApacheHttpClient更加简单,并且它比HttpURLConnection提供了很多其它的功能。所以普通情况下,我们能够在项目中用HttpClient封装一些GetPost、下载、上传的接口,以供其它代码直接调用。


    经验分享:

    在实现Android网络应用的开发过程中。须要特别留意两个问题:一个是网络流量的问题。还有一个是网络连接可能不稳定的问题。

    对于Android设备的上网方式。一般的有WIFI3G2G几种方式。

    对于WIFI的用户。对于流量不会太在意。而对于3G、甚至2G上网的用户来说。流量是关系到用户钱包的大问题。所以,对于整个应用的设计,就要充分考虑流量的问题。

    或者在项目后期做单独的优化工作。比方,假设应用中须要轮询server获取信息,那么我们就能够依据用户的上网方式,自己主动调整轮询时间,为3G2G的用户节省流量。这里仅仅是举这样一个样例。详细的,还要依据业务需求进行细致挖掘。

    用户使用Android设备,一般都是碎片时间。可能是在办公室,也可能是在乘坐公交车或者地铁,网络信号未必会一直稳定。网络连接可能会时断时续。

    我们在设计网络应用的时候。就要充分考虑这样的情况。一个是要考虑怎样对网络连接异常进行处理,一个是要考虑网络恢复后怎样处理。


  • 相关阅读:
    量子计算机还要忽悠多少年?[转载]
    量子计算机的七大惊人颠覆
    Windows10共享文件夹、打印机,可是网络上显示“未授予用户在此计算机上的请求登录类型”的解决方案
    深圳绿道-观澜段-乡村一号
    深圳绿道最全资料合集
    Office2013激活工具
    恢复桌面快捷方式小箭头最简单的方法
    css hack
    字体
    移动端 meta 标签笔记
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7356514.html
Copyright © 2011-2022 走看看