您的位置:首页 > 理论基础 > 计算机网络

第八章 网络的时代—网络开发(2)

2016-03-21 11:37 639 查看

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

8.3.1 HTTP协议简介

超文本传输协定(HTTP,HyperTextTransferProtocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。HTTP是一个客户端和服务器端请求和应答的标准(TCP)。客户端是终端用户,服务器端是网站。通过使用Web浏览器、网络爬虫或者其它的工具,客户端发起一个到服务器上指定端口(默认端口为80)的HTTP请求。我们称这个客户端为用户代理(useragent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器。在用户代理和源服务器中间可能存在多个中间层,比如代理,网关,或者隧道(tunnel)。尽管TCP/IP协议是互联网上最流行的应用,HTTP协议并没有规定必须使用它和基于它支持的层。事实上,HTTP可以在任何其他互联网协议上,或者在其他网络上实现。HTTP只假定其下层协议提供可靠的传输,任何能够提供这种保证的协议都可以被其使用。通常,由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器向客户端发回一个状态行,比如“HTTP/1.1200 OK”,和响应的消息,消息的消息体可能是请求的文件、错误消息、或者其它一些信息。HTTP使用TCP而不是UDP的原因在于打开一个网页必须传送很多数据,而TCP协议提供传输控制,按顺序组织数据,和错误纠正。通过HTTP或者HTTPS协议请求的资源,由统一资源标识符(UniformResource Identifiers,URI)来标识。HTTP/1.1协议中共定义了八种方法(有时也叫“动作”)来表明Request-URI指定的资源的不同操作方式:1)OPTIONS返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。2)HEAD向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。3)GET向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在WebApplication中。其中一个原因是GET可能会被网络蜘蛛等随意访问。参见安全方法4)POST向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。5)PUT向指定资源位置上传其最新内容。6)DELETE请求服务器删除Request-URI所标识的资源。7)TRACE回显服务器收到的请求,主要用于测试或诊断。8)CONNECTHTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(MethodNot Allowed);当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(NotImplemented)。HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的。当然,所有的方法支持的实现都应当符合下述的方法各自的语义定义。此外,除了上述方法,特定的HTTP服务器还能够扩展自定义的方法。AndroidSDK提供了多个封装的类,可以非常方便的实现基于HTML协议的编程。一般在,在Android中针对HTTP进行网络通信有几种方式:一种是通过URL类获取网络资源;一种是使用HttpURLConnection类(一般通过用URL类的openConnection()方法创建一个HttpURLConnection对象)来实现;一种是使用Apache的HTTP客户端组件HttpClient实现。下面会对这几种方式做详细的说明。

8.3.2 使用URL类读取HTTP资源

URL(UniformResourceLocator)对象代表统一资源定位器,它是指向互联网“资源”的指针。通常情况下,URL由协议名,主机,端口,资源组成。如下:http://www.your-host:80/index.phpURL类常用的方法有:
StringgetFile();//获取此URL的资源名StringgetHost();//获取此URL的主机名StringgetPath();//获取此URL的路劲IntgetPort();//获取此URL的端口号StringgetProtocol();//获取此URL的协议名称StringgetQuery();//获取此URL的查询字符串URLConnectionopenConnection();//返回一个URLConnection对象InputStreamopenStream();//打开连接,并返回一个用于读取该URL资源的InputStream
URL对象提供了openStream()方法,就可以读取该URL资源的InputStream,非常的方便。下面的代码示例,访问了Web地址“http://www.google.cn/”,并且将服务器返回的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网络资源。可以看到,我们将所有的代码都写到了Activity的onCreate()中。在真实的项目开发过程中,这种方式是有问题的。由于网络的阻塞,可能会出现ANR(ApplicationNot 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();//获取服务器的响应代码StringgetResponseMessage();//获取服务器的响应消息StringgetResponseMethod();//获取发送请求
使用HttpURLConnection类访问HTTP资源的基本步骤如下:1)创建URL以及HttpURLConnection对象。2)设置连接参数。3)连接到服务器。4)向服务器写数据。5)从服务器读取数据。下面提供一段代码说明具体如何实现。
try {//创建一个URL对象URLurl = new URL(your_url);//创建一个URL连接,如果有代理的话可以指定一个代理。URLConnectionconnection = url.openConnection(Proxy_yours);//对于HTTP连接可以直接转换成HttpURLConnection,这样就可以使用一些HTTP连接特定的方法,如setRequestMethod()等:HttpURLConnectionconnection = (HttpURLConnection)url.openConnection(Proxy_yours);
//在开始和服务器连接之前,可能需要设置一些网络参数connection.setConnectTimeout(10000);//连接到服务器connection.connect();//与服务器交互: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使用Apache的HttpClient

ApacheHttpClient 是一个开源项目,它对java.net中的类进行了封装,弥补了java.net.*灵活性不足的缺点,更适合在Android中开发网络应用,支持客户端的HTTP编程,更加方便高效。一般的,使用HttpClient进行网络开发的步骤如下:1)创建HttpClient对象。2)如果需要发送GET请求,创建HttpGet对象;如果需要发送Post请求,创建HttpPost对象。3)如果需要设置请求参数,可使用HttpGet和HttpPost共同的setParams(HttpParamsparams)方法来添加请求参数,HttpPost还可以调用setEntity(HttpEntityentity)方法来设置。4)调用HttpClient对象的execute(HttpUriRequestrequest)发送请求,执行该方法返回一个HttpResponse。5)调用HttpResponse的getAllHeaders(),getHeaders(Stringname)等方法可获取响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象封装了响应的内容。下面是一个详细的通用的网络连接类,包括了GET、POST的完整代码及注解。
//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(); //获取服务器响应信息,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; }}
从上面的编程过程我们可以看出,使用Apache的HttpClient更加简单,而且它比HttpURLConnection提供了更多的功能。所以一般情况下,我们可以在项目中用HttpClient封装一些Get、Post、下载、上传的接口,以供其它代码直接调用。
经验分享:在实现Android网络应用的开发过程中,需要特别留意两个问题:一个是网络流量的问题,另一个是网络连接可能不稳定的问题。对于Android设备的上网方式,一般的有WIFI、3G、2G几种方式。对于WIFI的用户,对于流量不会太在意。而对于3G、甚至2G上网的用户来说,流量是关系到用户钱包的大问题。所以,对于整个应用的设计,就要充分考虑流量的问题。或者在项目后期做单独的优化工作。比如,如果应用中需要轮询服务器获取信息,那么我们就可以根据用户的上网方式,自动调整轮询时间,为3G、2G的用户节省流量。这里只是举这样一个例子,具体的,还要根据业务需求进行仔细挖掘。用户使用Android设备,一般都是碎片时间,可能是在办公室,也可能是在乘坐公交车或者地铁,网络信号未必会一直稳定,网络连接可能会时断时续。我们在设计网络应用的时候,就要充分考虑这种情况。一个是要考虑如何对网络连接异常进行处理,一个是要考虑网络恢复后如何处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: