第 17 章 网络编程
现代的应用程序都离不开网络,网络编程是非常重要的技术。Java SE提供java.net包,其中包含了网络编程所需要的最基础一些类和接口。这些类和接口面向两个不同的层次:基于Socket的低层次网络编程和基于URL的高层次网络编程,所谓高低层次就是通信协议的高低层次,Socket采用TCP、UDP等协议,这些协议属于低层次的通信协议;URL采用HTTP和HTTPS这些属于高层次的通信协议。低层次网络编程,因为它面向底层,比较复杂,但是“低层次网络编程”并不等于它功能不强大。恰恰相反,正因为层次低,Socket编程与基于URL的高层次网络编程比较,能够提供更强大的功能和更灵活的控制,但是要更复杂一些。
本章会介绍基于URL的高层次网络编程,以及数据交换格式。
17.1 网络基础
网络编程需要程序员掌握一下基础的网络知识,这一节先介绍一些网络基础知识。
17.1.1 网络结构
首先了解一下网络结构,网络结构是网络的构建方式,目前流行的有客户端服务器结构网络和对等结构网络。
客户端服务器结构网络
客户端服务器(Client Server,缩写C/S)结构网络,是一种主从结构网络。如图17-1所示,服务器一般处于等待状态,如果有客户端请求,服务器响应请求建立连接提供服务。服务器是被动的,有点像在餐厅吃饭时候的服务员。而客户端是主动的,像在餐厅吃饭的顾客。
图17-1 客户端服务器结构网络
事实上,生活中很多网络服务都采用这种结构。例如:Web服务、文件传输服务和邮件服务等。虽然它们存在的目的不一样,但基本结构是一样的。这种网络结构与设备类型无关,服务器不一定是电脑,也可能是手机等移动设备。
对等结构网络
对等结构网络也叫点对点网络(Peer to Peer,缩写P2P),每个节点之间是对等的。它们如图17-2所示,每个节点既是服务器又是客户端,这种结构有点像吃自助餐。
图17-2 对等结构网络
对等结构网络分布范围比较小。通常在一间办公室或一个家庭内,因此它非常适合于移动设备间的网络通讯,网络链路层是由蓝牙和WiFi实现。
17.1.2 TCP/IP协议
网络通信会用到协议,其中TCP/IP协议是非常重要的。TCP/IP协议是由IP和TCP两个协议构成的,IP(Internet Protocol)协议是一种低级的路由协议,它将数据拆分成许多小的数据包中,并通过网络将它们发送到某一特定地址,但无法保证都所有包都抵达目的地,也不能保证包的顺序。
由于IP协议传输数据的不安全性,网络通信时还需要TCP协议,传输控制协议(Transmission Control Protocol,TCP)是一种高层次的协议,面向连接的可靠数据传输协议,如果有些数据包没有收到会重发,并对数据包内容准确性检查并保证数据包顺序,所以该协议保证数据包能够安全地按照发送时顺序送达目的地。
17.1.3 IP地址
为实现网络中不同计算机之间的通信,每台计算机都必须有一个与众不同的标识,这就是IP地址,TCP/IP使用IP地址来标识源地址和目的地址。最初所有的IP地址都是32位数字构成,由4个8位的二进制数组成,每8位之间用圆点隔开,如:192.168.1.1,这种类型的地址通过IPv4指定。而现在有一种新的地址模式称为IPv6,IPv6使用128位数字表示一个地址,分为8个16位块。尽管IPv6比IPv4有很多优势,但是由于习惯的问题,很多设备还是采用IPv4。不过Java语言同时指出IPv4和IPv6。
在IPv4地址模式中IP地址分为A、B、C、D和E等5类。
A类地址用于大型网络,地址范围:1.0.0.1~126.155.255.254。
B类地址用于中型网络,地址范围:128.0.0.1~191.255.255.254。
C类地址用于小规模网络,192.0.0.1~223.255.255.254。
D类地址用于多目的地信息的传输和作为备用。
E类地址保留仅作实验和开发用。
另外,有时还会用到一个特殊的IP地址127.0.0.1,127.0.0.1称为回送地址,指本机。主要用于网络软件测试以及本地机进程间通信,使用回送地址发送数据,不进行任何网络传输,只在本机进程间通信。
17.1.4 端口
一个IP地址标识这一台计算机,每一台计算机又有很多网络通信程序在运行,提供网络服务或进行通信,这就需要不同的端口进行通信。如果把IP地址比作电话号码,那么端口就是分机号码,进行网络通信时不仅要指定IP地址,还要指定端口号。
TCP/IP系统中的端口号是一个16位的数字,它的范围是0~65535。小于1024的端口号保留给预定义的服务,如HTTP是80,FTP是21,Telnet是23,Email是25等,除非要和那些服务进行通信,否则不应该使用小于1024的端口。
17.2 数据交换格式
数据交换格式就像两个人在聊天一样,采用彼此都能听得懂的语言,你来我往,其中的语言就相当于通信中的数据交换格式。有时候,为了防止聊天被人偷听,可以采用暗语。同理,计算机程序之间也可以通过数据加密技术防止“偷听”。
数据交换格式主要分为纯文本格式、XML格式和JSON格式,其中纯文本格式是一种简单的、无格式的数据交换方式。
例如,为了告诉别人一些事情,我会写下如图17-3所示的留言条。
图17-3 留言条
留言条有一定的格式,共有4部分:称谓、内容、落款和时间,如图17-4所示。
图17-4 留言条格式
如果用纯文本格式描述留言条,可以按照如下的形式:
"云龙同学","你好!n今天上午,我到你家来想向你借一本《小学生常用成语词典》。可是不巧,你不在。我准备晚上6时再来借书。请你在家里等我,谢谢!","关东升","2012年12月08日"
留言条中的4部分数据按照顺序存放,各个部分之间用逗号分隔。数据量小的时候,可以采用这种格式。但是随着数据量的增加,问题也会暴露出来,可能会搞乱它们的顺序,如果各个数据部分能有描述信息就好了。而XML格式和JSON格式可以带有描述信息,它们叫做“自描述的”结构化文档。
将上面的留言条写成XML格式,具体如下:
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>云龙同学</to>
<conent>你好!n今天上午,我到你家来想向你借一本《小学生常用成语词典》。
可是不巧,你不在。我准备晚上6时再来借书。请你在家里等我,谢谢!</conent>
<from>关东升</from>
<date>2012年12月08日</date>
</note>
上述代码中位于尖括号中的内容(<to>…</to>等)就是描述数据的标识,在XML中称为“标签”。
将上面的留言条写成JSON格式,具体如下:
{to:"云龙同学",conent:"你好!n今天上午,我到你家来想向你借一本《小学生常用成语词典》。可是不巧,你不在。我准备晚上6时再来借书。请你在家里等我,谢谢!",from:"关东升",date:"2012年12月08日"}
数据放置在大括号{}之中,每个数据项目之前都有一个描述名字(如to等),描述名字和数据项目之间用冒号(:)分开。
可以发现,一般来讲,JSON所用的字节数要比XML少,这也是很多人喜欢采用JSON格式的主要原因,因此JSON也被称为“轻量级”的数据交换格式。接下来,重点介绍JSON数据交换格式。
17.2.1 JSON文档结构
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。所谓轻量级,是与XML文档结构相比而言的,描述项目的字符少,所以描述相同数据所需的字符个数要少,那么传输速度就会提高,而流量却会减少。
如果留言条采用JSON描述,可以设计成下面的样子:
{"to":"云龙同学",
"conent": "你好!n今天上午,我到你家来想向你借一本《小学生常用成语词典》。可是
不巧,你不在。我准备晚上6时再来借书。请你在家里等我,谢谢!",
"from": "关东升",
"date": "2012年12月08日"}
由于Web和移动平台开发对流量的要求是要尽可能少,对速度的要求是要尽可能快,而轻量级的数据交换格式JSON就成为理想的数据交换格式。
构成JSON文档的两种结构为对象和数组。对象是“名称-值”对集合,它类似于Java中Map类型,而数组是一连串元素的集合。
对象是一个无序的“名称/值”对集合,一个对象以{(左括号)开始,}(右括号)结束。每个“名称”后跟一个:(冒号),“名称-值”对之间使用,(逗号)分隔。JSON对象的语法表如图17-5所示。
图17-5 JSON对象的语法表
下面是一个JSON对象的例子:
{
"name":"a.htm",
"size":345,
"saved":true
}
数组是值的有序集合,以[(左中括号)开始,](右中括号)结束,值之间使用,(逗号)分隔。JSON数组的语法表如图17-6所示。
图17-6 JSON数组的语法表
下面是一个JSON数组的例子:
["text","html","css"]
在数组中,值可以是双引号括起来的字符串、数值、true、false、null、对象或者数组,而且这些结构可以嵌套。数组中值的JSON语法结构如图17-7所示。
图17-7 JSON值的语法结构图
17.2.2 使用第三方JSON库
由于目前Java官方没有提供JSON编码和解码所需要的类库,所以需要使用第三方JSON库,笔者推荐JSON-java库,JSON-java库提供源代码,最重要的是不依赖于其他第三方库,需要再起找其他的库了。读者可以在https://github.com/stleary/JSON-java下载源代码。API在线文档http://stleary.github.io/JSON-java/index.html。
下载JSON-java获得源代码文件,解压后文件如图17-8所示。
图17-8 JSON-java源代码文件
将JSON-java库源代码文件添加到工程中,需要两个步骤:
创建org.json包
JSON-java库中的源代码文件都隶属于org.json包,从图17-8可见源文件夹下没有与包对应的目录结构,为此需要在Eclipse的项目中创建org.json包。选择Eclipse项目的src源代码文件夹,右击菜单中选择“新建”→“包”,弹出新建包对话框,如图17-9所示在名称的中输入org.json,然后单击完成,就成功创建org.json包。
图17-9 在Eclipse中创建包
复制源代码文件
org.json包创建好后,需要将JSON-java库文件夹中的源代码文件复制到Eclipse工程的org.json包中。由于操作系统的资源管理器与Eclipse工具之间互相复制粘贴,Eclipse中复制和粘贴操作的快捷键和右键菜单与操作系统下完全一样。如图17-10所示,将源代码文件复制到Eclipse中。
图17-10 复制源代码文件到Eclipse工程
17.2.3 JSON数据编码和解码
JSON和XML真正在进行数据交换时候,它们存在的形式就是一个很长的字符串,这个字符串在网络中传输或者存储于磁盘等介质中。在传输和存储之前需要把JSON对象转换成为字符串才能传输和存储,这个过程称之为“编码”过程。接收方需要将接收到的字符串转换成为JSON对象,这个过程称之为“解码”过程。编码和解码过程就像发电报时发送方把语言变成能够传输的符号,而接收时要将符号转换成为能够看懂的语言。
下面具体介绍一下JSON数据编码和解码过程。
编码
如果想获得如下这样JSON字符串:
{"name": "tony", "age": 30, "a": [1, 3]}
应该如何实现编码过程,参考代码如下:
try { JSONObject jsonObject = new JSONObject(); ① jsonObject.put("name", "tony"); ② jsonObject.put("age", 30); ③ JSONArray jsonArray = new JSONArray(); ④ jsonArray.put(1).put(3); ⑤ jsonObject.put("a", jsonArray); ⑥ //编码完成 System.out.println(jsonObject.toString()); ⑦ } catch (JSONException e) { e.printStackTrace(); }
上述代码第①行是创建JSONObject(JSON对象),代码第②行和第③行是把JSON数据项添加到JSON对象jsonObject中,代码第④行创建JSONArray(JSON数组),代码第⑤行是向JSON数组中添加1和3两个元素。代码第⑥是将JSON数组jsonArray作为JSON对象jsonObject的数据项添加到JSON对象。
代码第⑦行jsonObject.toString()是将JSON对象转换为字符串,真正完成了JSON编码过程。
解码
解码过程是编码反向操作,如果有如下JSON字符串:
{"name":"tony", "age":30, "a":[1, 3]}
那么如何把这个JSON字符串解码成JSON对象或数组,参考代码如下:
String jsonString = "{"name":"tony", "age":30, "a":[1, 3]}"; ① try { JSONObject jsonObject = new JSONObject(jsonString); ② String name = jsonObject.getString("name"); ③ System.out.println("name : " + name); int age = jsonObject.getInt("age"); System.out.println("age : " + age); JSONArray jsonArray = jsonObject.getJSONArray("a"); ④ int n1 = jsonArray.getInt(0); ⑤ System.out.println("数组a第一个元素 : " + n1); int n2 = jsonArray.getInt(1); System.out.println("数组a第二个元素 : " + n2); } catch (JSONException e) { e.printStackTrace(); }
上述代码第①行是声明一个JSON字符串,网络通信过程中JSON字符串是从服务器返回的。代码第②行通过JSON字符串创建JSON对象,这个过程事实上就是JSON字符串解析过程,如果能够成功地创建JSON对象,说明解析成功,如果发生异常则说明解析失败。
代码第③行从JSON对象中按照名称取出JSON中对应的数据。代码第④行是取出一个JSON数组对象,代码第⑤行取出JSON数组第一个元素。
注意 如果按照规范的JSON文档要求,每个JSON数据项目的“名称”必须使用双引号括起来,不能使用单引号或没有引号。在下面的代码文档中,“名称”省略了双引号,该文档在其他平台解析时会出现异常,而在Java平台则可以通过,这得益于Java解析类库的强大,但这并不是规范的做法。如果与其他平台进行数据交换时,采用这种不规范的JSON文档进行数据交换,那么很有可能会导致严重的问题发生。
{ResultCode:0,Record:[ {ID:'1',CDate:'2012-12-23',Content:'发布iOSBook0',UserID:'tony'}, {ID:'2',CDate:'2012-12-24',Content:'发布iOSBook1',UserID:'tony'}]}。
17.3 访问互联网资源
Java的java.net包中还提供了高层次网络编程类——URL,通过URL类访问互联网资源。使用URL进行网络编程,不需要对协议本身有太多的了解,相对而言是比较简单的。
17.3.1 URL概念
互联网资源是通过URL指定的,URL是Uniform Resource Locator简称,翻译过来是“一致资源定位器”,但人们都习惯URL简称。
URL组成格式如下:
协议名://资源名
“协议名”指明获取资源所使用的传输协议,如http、ftp、gopher和file等,“资源名”则应该是资源的完整地址,包括主机名、端口号、文件名或文件内部的一个引用。例如:
http://www.sina.com/
http://home.sohu.com/home/welcome.html
http://www.51work6.com:8800/Gamelan/network.html#BOTTOM
17.3.2 HTTP/HTTPS协议
访问互联网大多都基于HTTP/HTTPS协议。下面介绍一下HTTP/HTTPS协议。
HTTP协议
HTTP是Hypertext Transfer Protocol的缩写,即超文本传输协议。HTTP是一个属于应用层的面向对象的协议,其简捷、快速的方式适用于分布式超文本信息的传输。它于1990年提出,经过多年的使用与发展,得到不断完善和扩展。HTTP协议支持C/S网络结构,是无连接协议,即每一次请求时建立连接,服务器处理完客户端的请求后,应答给客户端然后断开连接,不会一直占用网络资源。
HTTP/1.1协议共定义了8种请求方法:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE和CONNECT。在HTTP访问中,一般使用GET和HEAD方法,其他方法都是可选的。
GET方法:是向指定的资源发出请求,发送的信息“显式”地跟在URL后面。GET方法应该只用在读取数据,例如静态图片等。GET方法有点像使用明信片给别人写信,“信内容”写在外面,接触到的人都可以看到,因此是不安全的。
POST方法:是向指定资源提交数据,请求服务器进行处理,例如提交表单或者上传文件等。数据被包含在请求体中。POST方法像是把“信内容”装入信封中,接触到的人都看不到,因此是安全的。
HTTPS协议
HTTPS是Hypertext Transfer Protocol Secure,即超文本传输安全协议,是超文本传输协议和SSL的组合,用以提供加密通信及对网络服务器身份的鉴定。
简单地说,HTTPS是HTTP的升级版,HTTPS与HTTP的区别是:HTTPS使用https://代替http://,HTTPS使用端口443,而HTTP使用端口80来与TCP/IP进行通信。SSL使用40位关键字作为RC4流加密算法,这对于商业信息的加密是合适的。HTTPS和SSL支持使用X.509数字认证,如果需要的话,用户可以确认发送者是谁。
17.3.3 使用URL类
Java 的java.net.URL类用于请求互联网上的资源,采用HTTP/HTTPS协议,请求方法是GET方法,一般是请求静态的、少量的服务器端数据。
URL类常用构造方法:
URL(String spec):根据字符串表示形式创建URL对象。
URL(String protocol, String host, String file):根据指定的协议名、主机名和文件名称创建URL对象。
URL(String protocol, String host, int port, String file):根据指定的协议名、主机名、端口号和文件名称创建URL对象。
URL类常用方法:
InputStream openStream():打开到此URL的连接,并返回一个输入流。
URLConnection openConnection():打开到此URL的新连接,返回一个URLConnection对象。
下面通过一个示例介绍一下如何使用java.net.URL类,示例代码如下:
//HelloWorld.java文件
package com.a51work6;
...
public class HelloWorld {
public static void main(String[] args) {
// Web网址
String url = "http://www.sina.com.cn/";
URL reqURL;
try {
reqURL = new URL(url); ①
} catch (MalformedURLException e1) {
return;
}
try ( // 打开网络通信输入流
InputStream is = reqURL.openStream(); ②
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr)) {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append('n');
line = br.readLine();
}
// 日志输出
System.out.println(sb);
} catch (IOException e) {
e.printStackTrace();
}
}
}
上述代码第①行创建URL对象,参数是一个HTTP网址。代码第②行通过URL对象的openStream()方法打开输入流。
17.3.4 使用HttpURLConnection发送GET请求
由于URL类只能发送HTTP/HTTPS的GET方法请求,如果要想发送其他的情况或者对网络请求有更深入的控制时,可以使用HttpURLConnection类型。
示例代码如下:
//HelloWorld.java文件
package com.a51work6;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HelloWorld {
// Web服务网址
static String urlString = "http://www.51work6.com/service/mynotes/WebService.php?"
+ "email=<换成你在51work6.com注册时填写的邮箱>&type=JSON&action=query"; ①
public static void main(String[] args) {
BufferedReader br = null;
HttpURLConnection conn = null;
try {
URL reqURL = new URL(urlString);
conn = (HttpURLConnection) reqURL.openConnection(); ②
conn.setRequestMethod("GET"); ③
// 打开网络通信输入流
InputStream is = conn.getInputStream(); ④
// 通过is创建InputStreamReader对象
InputStreamReader isr = new InputStreamReader(is, "utf-8");
// 通过isr创建BufferedReader对象
br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
}
// 日志输出
System.out.println(sb);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect(); ⑤
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
上述代码第①行是一个Web服务网址字符串。
提示 发送GET请求时发送给服务器的参数是放在URL的“?”之后,参数采用键值对形式,例如:第①行的URL中type=JSON是一个参数,type是参数名,JSON是参数名,服务器端会根据参数名获得参数值。多个参数之间用“&”分隔,例如type=JSON&action=query就是两个参数。
代码第②行是用reqURL.openConnection()方法打开一个连接,返回URLConnection对象,由于本次连接是HTTP连接,所以返回的是HttpURLConnection对象。URLConnection是抽象子类,HttpURLConnection是URLConnection的子类。
代码第③行conn.setRequestMethod("GET")是设置请求方法为GET方法。代码第④行是通过conn.getInputStream()打开输入流,上一节实例使用的URL的openStream()方法获得输入流。代码第⑤行conn.disconnect()是断开连接,这可以释放资源。
从服务器端返回的数据是JSON字符串,格式化后内容如下:
{
"ResultCode": 0,
"Record": [
{
"ID": 5238,
"CDate": "2017-05-18",
"Content": "欢迎来到智捷课堂。"
},
{
"ID": 5239,
"CDate": "2018-10-18",
"Content": "Welcome to zhijieketang."
}
]
}
提示 上述示例中URL所指向的Web服务是由作者所在的智捷课堂提供的,读者要想使用这个Web服务需要在www.51work6.com进行注册,注册时需要提供自己有效的邮箱,这个邮箱用来激活用户。在网络请求时需要提交email参数,这个参数是注册时填写的邮箱。
17.3.5 使用HttpURLConnection发送POST请求
HttpURLConnection也可以发送HTTP/HTTPS的POST请求,下面介绍如何使用HttpURLConnection发送POST请求。
示例代码如下:
//HelloWorld.java文件
package com.a51work6;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HelloWorld {
// Web服务网址
static String urlString = "http://www.51work6.com/service/mynotes/WebService.php"; ①
public static void main(String[] args) {
BufferedReader br = null;
HttpURLConnection conn = null;
try {
URL reqURL = new URL(urlString);
conn = (HttpURLConnection) reqURL.openConnection(); ②
conn.setRequestMethod("POST"); ③
conn.setDoOutput(true); ④
String param = String.format("email=%s&type=%s&action=%s",
"<换成你在51work6.com注册时填写的邮箱>", "JSON", "query"); ⑤
// 设置参数
DataOutputStream dStream = new DataOutputStream(conn.getOutputStream()); ⑥
dStream.writeBytes(param); ⑦
dStream.close(); ⑧
// 打开网络通信输入流
InputStream is = conn.getInputStream();
// 通过is创建InputStreamReader对象
InputStreamReader isr = new InputStreamReader(is, "utf-8");
// 通过isr创建BufferedReader对象
br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
}
// 日志输出
System.out.println(sb);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
上述代码第①行URL后面不带参数,这是因为要发送的是POST请求,POST请求参数是放在请求体中。代码第②行是通过reqURL.openConnection()是建立HTTP连接,代码第③行是设置HTTP请求方法为POST,代码第④行conn.setDoOutput(true)是设置请求过程可以传递参数给服务器。
代码第⑤是设置请求参数格式化字符串"email=%s&type=%s&action=%s",其中%s是占位符。
代码第⑥行~第⑧行是将请求参数发送给服务器,代码第⑥行中conn.getOutputStream()是打开输出流,new DataOutputStream(conn.getOutputStream())是创建基于数据输出流。代码第⑦行dStream.writeBytes(param)是向输出流中写入数据,第⑧行dStream.close()是关闭流,并将数据写入到服务器端。
17.3.6 实例:Downloader
为了进一步熟悉URL类,这一节介绍一个下载程序Downloader。Downloader.java代码如下:
//Downloader.java文件
package com.a51work6;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class Downloader {
// Web服务网址
private static String urlString = "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/"
+ "static/superman/img/logo/bd_logo1_31bdc765.png";
public static void main(String[] args) {
download();
}
// 下载方法
private static void download() {
HttpURLConnection conn = null;
try {
// 创建URL对象
URL reqURL = new URL(urlString);
// 打开连接
conn = (HttpURLConnection) reqURL.openConnection(); ①
try (// 从连接对象获得输入流
InputStream is = conn.getInputStream(); ②
BufferedInputStream bin = new BufferedInputStream(is); ③
// 创建文件输出流
OutputStream os = new FileOutputStream("./download.png"); ④
BufferedOutputStream bout = new BufferedOutputStream(os);) { ⑤
byte[] buffer = new byte[1024];
int bytesRead = bin.read(buffer);
while (bytesRead != -1) {
bout.write(buffer, 0, bytesRead);
bytesRead = bin.read(buffer);
}
} catch (IOException e) {
}
System.out.println("下载完成。");
} catch (IOException e) {
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
}
上述代码第①行打开连接获得HttpURLConnection对象。代码第②行是从连接对象获得输入流。代码第③行创建缓冲流输入流,使用缓冲流可以提高读写效率。
代码第④行是创建文件输出流,代码第⑤行是创建缓冲流输出流。
运行Downloader程序,如果成功会在当前目录获得一张图片。
本章小结
本章主要介绍了Java网络编程,首先介绍了一些网络方面的基本知识。重点介绍了JSON数据交换格式,由于Java官方没有提供JSON解码和编码库,需要是使用第三方库。最后介绍了使用URL类访问互联网资源。