1、第十一章 JAVA网络编程 11.1 Java的网络通信功能 11.1.1 JAVA的网络支持 JAVA作为“网络上的世界语”,具有独特的网络优势与网络功能。为了进行网络编程,JAVA提供了一个包,将该工具包与java中的输入/输出流相结合,就可以做到从网络上读取文件、数据或向网络写文件或数据时,可以象在本地磁盘上读写文件或数据一样容易和方便。 Java提 供 了 大 量 的 类 以 满 足 网 络 化 、 多 线 程 、 面 向 对 象系统 的 需 要 。 1. 语 言 包 提 供 的 支 持 包 括 字 符 串 处 理 、 多 线 程 处 理 、例外 处 理 、 数 学 函
2、数 处 理 等 ,可 以 用 它 简 单 地 实 现 Java 程 序的 运 行 平 台 。 2. 实 用 程 序 包 提 供 的 支 持 包 括 哈 希 表 、 堆 栈 、 可 变 数组、 时 间 和 日 期 等。 3. 输 入 输 出 包 用 统 一 的 "流 "模 型 来 实 现 所 有 格式 的 I/O,包括 文 件 系 统 、 网 络、 输 入 /出 设 备 等 。 4. 低 级 网 络 包 用 于 实 现 URL、Socket、数据报等网络编 程方法 。 5. 抽 象 图 形 用 户 接 口 包 实 现 了 不 同 平 台 的 计 算 机 的 图形用 户 接 口 部
3、 件 ,包括 窗 口 、 菜 单 、 滚 动 条 、 对 话 框 等 ,使得 Java可 以 移 植 到 不 同平 台 的 机 器 。 6. 网 络 包 支 持 Internet的 TCP/IP协 议 ,提 供 了 与 Internet的 接 口。它 支 持 URL连 接,WWW的 即 时 访 问 ,并 且 简 化 了 用 户 /服 务 器 模型 的 程 序 设 计 。 在JAVA网络编程过程中,大量地使用到输入输出流,即JAVA.IO包中的两个基本流:InputStream和OutputStream。 InputStream继承了Object类,它有六个直属的子类,其中之一的Filt
4、erInputStream是一个抽象类,并有四个后代,如图11-1所示。 类似地,OutputStream继承了Object类,它有四个直属的子类,其中之一的FilterOutputStream是一个抽象类,并有三个子类,如图11-2所示。 FileInputStream PipeInputStream FilterInputStream ByteArrayInputStream SequenceInputStream StringBufferInputStream DataInputStream BufferedInputStream Line
5、NumberInputStream PushBackInputStream InputStream 图11-1 InputStream类的继承树 PipeOutputStream FilterOutputStream ByteArrayOutputStream FileOutputStream DataOutputStream BufferedOutputStream PrintStream OutputStream 图11-2 OutputStream类的继承树 11.1.2 InetAdd
6、ress类的使用 在进行网络通信时,必须指定通信地址,在JAVA中由InetAddress类来完成该功能。类InetAddress可以用于标识网络上的硬件资源,它提供了一系列方法以描述、获取及使用网络资源。 InetAddress类没有构造函数,因此不能用new来构造一个InetAddress实例。通常是用它提供的静态方法来获取: public static InetAddress getByName(String host) :host可以是一个机器名,也可以是一个形如“%d.%d.%d.%d”的IP地址或一个DSN域名。 public static InetAddress
7、 getLocalHost() public static InetAddress[] getAllByName(String host) 这三个方法通常会产生UnknownHostException例外,应在程序中捕获处理。 InetAddress类的主要方法包括: 1. GetHostName() 返回该地址的主机名。如果主机名为null,那么当前地址指向当地机器的任一可得网络地址。 返回值:类型为 string 2. GetAddress() 以网络地址顺序来返回IP地址 返回值:存在byte[]型的字节数组中,其中,最高序字节位于标值为0的元素中。 3. G
8、etHostAddress() 以“%d%d%d%d”的形式返回IP地址串。 返回值:类型为 string 4. HashCode() 返回该InetAddress对象的散列码。 返回值:类型为int 5. Equals(Object obj) 将当前对象与指定对象进行比较。 返回值:true 相同 false 不相同 6. ToString() 将该InetAddress对象以字符串的形式表示出来。 返回值:类型为string的实体对象。 7. GetByName(string host) 这是一个synchronized的类方法,该方法用于返回指
9、定主机的网络地址。如果主机名为null,则返回当地机器的默认地址。为了加速对地址的访问,使用了一个当地cashe.如果地址未知,则会发生unknowHostException例外。 参数:host 指定的主机。 返回值:类型为InetAddress 8. GetAllByName(string host) 返回指定主机名的所有InetAddress对象,这是一个synchrozed类方法,若无法决定主机名,则会发生unknowHostException例外。 参数:host 指定的主机。 返回值:存放于InetAddress[]数组中。 9. GetLocalHost() 用于
10、返回当地主机的InetAddress对象。如果无法决定主机名,则会发生unknowHostException例外。 利用InetAddress类提供的方法,可以开发一些应用程序。 例1:获取本机的IP地址 import .*; public class getLocalHostTest{ public static void main(String[] arg){ InetAddress myIP=null; try { myIP=InetAddress.getLocalHost(); } catch(UnknownHos
11、tException e){} System.out.println(myIP); } } 执行结果如下图所示: 例2:java根据域名自动到DNS上查找IP地址 import .*; public class getIP{ public static void main(String args[]){ InetAddress swjtu=null; try{ swjtu=InetAddress.getByName(""); } catch(Unk
12、nownHostException e) {} System.out.println(swjtu); } } 执行结果如图所示: 11.2 JAVA中的网络编程方法 JAVA中的网络编程可在三个层面上进行: ü URL层:这是最高级层面。可以利用URL直接进行Internet上的资源访问和数据传输。 ü Socket层:这是传统网络编程经常采用的方式。通过在应用程序间建立Socket套接字连接,然后在连接之上进行数据通信。Client/Server结构应用程序通常采用这种面向连接的模式。 ü Datagram数据流层
13、这是最低级层面。是无连接的通信方式。 为了实现上述的网络通信功能,JAVA提供了相应的类: Ø .URL类和.URLConnection类使得编程者能很方便地利用URL在Internet上进行网络通信。 Ø 包的Socket类(客户端)和ServerSocket类(服务器端)提供了用TCP/IP套接字来编写C/S应用程序的方法。 Ø 包的DatagramServer和DatagramPacket类用于实现UDP通信。 11.3 JAVA URL网络编程 .URL类和.URLConnection类使得编程者能很方便地利用URL在Internet上进行网络通信。 11.
14、3.1 URL概念 URL(Uniform Resource Locator)是统一资源定位器的简称,是用来标识Internet上的资源的,通过URL可以访问Internet上相应的文件和其他资源。URL指明了取得资源采用的协议和资源地址。 URL格式:协议名://资源名 协议名:指明获取资源所用的传输协议,如:http、ftp、gopher、 new、mailto、file等。 资源名:是资源的完整地址,包括主机IP地址(或主机域名)、端口号、完整文件名、HTML文件中参考位置等。 下面是几个URL的例子: http:// http:// http://:80/
15、Gamelan/network.html#BOTTOM 一个完整的URL如下: http://:80/home/white_paper.html#intro_1 协议 主机域名(IP地址) 端口号 目录 文件名 HTML参考点 其中, 传输协议:说明访问资源时使用的网络协议。 主机名称( host name ):资源所在的主机的名称(IP地址)。 文件名(file name) :资源在机器上的完整名字。这里的文件名并不是简单的名字,它要包括文件的完整的路径名,这样我们才能直接通过文件名访问到一个文 件。 端口号( port number):连接时所使
16、用的服务器端口号。省略时表示标准端口号,如http:80,telnet:23等 。 参考点( reference ):资源中的特定位置,用来标识一个文件中特定的偏移位置。通过参考点我们可以对一个文件中感兴趣的部分创建URL对象。 11.3.2 URL类 URL类定义了一个WWW的统一资源定位器和可以对其进行的一些操作。由URL类生成的对象指向WWW资源(如WEB页、文本文件、图形图象文件、音频视频文件等等)。建立URL对象后就可使用通用的格式取得URL的各个部分的信息和获取URL内容。 一、URL构造方法 URL共有6个构造方法: (1) URL(Strin
17、g spec) 简单地用一个字符串生成URL对象,例如: URL url0=new URL(" http://:80/YFjava/java7.html "); (2) URL(String protocol,String host,String file) 由用户分开指定URL各个部分、但采用缺省端口构成URL对象,例如: URL url1=new URL(" http" ," " ,"/YFjava/java7.html"); (3) URL(String protocol,String host,int port,String file); 由用户分
18、开指定URL各个部分构成URL对象,例如: URL url2=new URL("http","",80, "/YFjava/java7.html"); 注意:2、3构造方法不能构造含有“#参考点”的URL地址。 (4) URL(URL context,String spec) 构造相对URL对象,如: URL base1=new URL("http://:80/ YFjava/" ); URL url3=new URL( base1," java7.html"); (5)URL(URL context, String spec, URLStreamHandler hand
19、ler) (6)URL(String protocol, String host, int port, String file, URLStreamHandler handler) 二、 异常处理 URL类的构造方法都可能发生MalformedURLException非运行时异常,因此生成URL对象时,必须对这一异常进行处理。如: try { URL myURL=new URL("http:// } catch (MalformedURLException e) { System.out.println("MalformedURLException:"+e);
20、} 11.3.3 通过URL访问WWW 一、获取URL属性 通过URL类的方法可以获得URL的属性。 String getProtocol()方法:获取URL中的传输协议。 String getHost()方法:获取URL中的主机名称。 int getPort():获取URL中的端口号,如果一个URL地址没有端口号,返回值为-1。 String getFile():返回目录文件名(路径名)。 String getRef():获取URL中的参考点,如果一个URL地址没有参考点,返回值为null。 String toExternalForm():将URL地
21、址转换成字符串。 String toString() :将URL地址转换成字符串。 boolean sameFile(URL other):比较两个URL是否相同。 实例一:获取URL属性的URLTest0.java 命令行格式:java URLTest0 http:// 本例仅仅是获取URL属性,因此在没有建立连接的情况下就可运行。 import .*; public class URLTest0{ public static void main(String[] args){ if(args.length==1){ try { URL
22、url=new URL(args[0]); /*创建URL对象*/ System.out.println("URL: "+url.toExternalForm()+"\n"+ "File: "+url.getFile()+"\n"+ "Host: "+url.getHost()+"\n"+ "Port: "+url.getPort()+"\n"+ "Prot
23、ocol:"+url.getProtocol()+"\n"); }catch(MalformedURLException e){ System.out.println("Bad URL."); } } else System.out.println("Usage:URLTest0 URL"); } } 执行结果如下图所示 : 二、获取URL内容 可以通过URL类提供的三个主要方法来访问它指向的资源(获取URL内容): ü
24、用URL类的getContent()方法直接获取URL内容。 ü 用URL类的openConnection()获得与URL的URLConnection连接。 ü 用openStream()方法得到InputStream流。 实际上,类URL的方法openStream()是通过URLConnection来实现的,它等价于openConnection().getInputStream()。 (1)利用getContent()方法 建立一个与指定资源的连接并可直接获取URL指定的资源,它会试图决定流的MIME类型并将流转换为相应的Java Object。 例如,如果我们创建了一个指向
25、GIF格式图片的URL,getContent()方法将识别流的类型为"image/gif"或“image/jpeg”,并返回Image类的一个实例。该Image对象包含该GIF图片的一个拷贝。我们可以通过getContent()方法将资源取到一个Java对象中,然后进行相应处理。 实例二:通过getContent()方法下载一图象文件并在Frame中显示。 //文件名URLTest2.java import .*; import java.io.*; import java.awt.*; import java.awt.image.*; import java.awt.eve
26、nt.*; public class URLTest2 extends Frame { private Image img; public void paint(Graphics g) { g.drawImage(img,20,20,this); } public void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if(e.getID()==WindowEvent.WINDOW_CLOSING) System.exit(
27、0);
}
public static void main(String args[]) throws MalformedURLException,IOException {
if(args.length!=1) // 检查用户是否提供了程序所需的命令行参数
{
System.out.println("Usage:java URLTest2
28、 urlt= new URLTest2(); urlt.img=urlt.createImage((ImageProducer)url.getContent()); urlt.enableEvents(AWTEvent.WINDOW_EVENT_MASK); urlt.setSize(600,400); urlt.setVisible(true); } } 当把该java文件保存到服务器的doc目录下,输入的命令行参数是:http://127.0.0.1:8080/1.png 执行结果:
29、 (2)利用openConnection()方法 在创建URL对象之后,可以调用URL类中的openConnection()函数,在你的机器和对方主机之间建立通信连接。例如,可以采用下列的方法连上yahoo搜索引擎: try{ URL yahoo=new URL( Yahoo.openconnection(); }catch (malformedURLException e){ … //构造URL可能产生的异常 }catch (IOException e){ … //I/O操作可能产生的异常 } UR
30、LConnection对象提供了许多可用于网络读写的函数,它可与程序和网络的另一端进行信息交互,而不象URL对象只能用来读数据。 (3)利用openStream()方法 建立连接URL后,可以调用URL类中的openStream()函数来打开一个流,从流中可读取URL中的内容。OpenStream()函数返回一个java.io.InputStream流对象,可从中读取数据。 如:try{ URL url1=new URL(getCodeBase(),"readme.txt"); InputStream in=url1.openStream
31、); int ch; while((ch=in.read())!=-1) System.out.print((char)ch); }catch(Exception e) //意外处理 e.printStackTrace(); 实例三:在本机上打开一个输入流,从中读取数据,并显示在屏幕上。 运行格式: java URLTest3 http://127.0.0.1:8080/URLTest3.java //文件名是URLTes
32、t3.java import .*; import java.io.*; class URLTest3{ public static void main(String args[]){ try{ URL url=new URL (args[0]); DataInputStream dis; String inputLine; dis=new DataInputStream(url.openStream()); //生成数据输入流对象,以从中读取数据 while((inputLine=d
33、is.readLine())!=null){ System.out.println(inputLine); } dis.close(); } catch (MalformedURLException e) //构造URL可能产生的异常 { System.out.println("MalformedURLException:"+e); } catch (IOException e)//I/O操作可能产生的异常 {
34、 System.out.println("IOException:"+ e); } } } 11.4 Socket通信 11.4.1 socket socket类 Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以较为方便的编写网络上数据的传递。 在Java中,有专门的Socket类来处理用户的请求和响应。利用Socket类的方法,就可以实现两台计算机之间的通讯。JAVA socket提供如下的类: ServerSocket类(服务器socket类) 1. ServerSocket(int p
35、ort) 在指定端口上生成一个服务器套接字 2. ServerSocket(int port,int count) 在指定端口上生成一个服务器套接字,并且指定与端口间的连接进行监听的次数。 3. GetInetAddress() 返回该套接字所连接的地址值 4. GetLocalPort() 返回该套接字所连接的端口号 5. Access() 以阻塞方式接受一个连接 6. Close() 关闭套接口 7. ToString() 返回该serverSocket实体对象值的字符串表示形式(包括该对象的执行地址、端口等信息) 8. SetSocketFactary(soc
36、ketInplFactory fac) 这是一个synchronized的类方法,用于设置系统的服务器socketImplFactory接口(该接口为socketImpl实体对象(即SOCKET的实现类)定义一个制造工厂) socket类(客户端类) 1. Socket(string host,int prot) 2. Socket(string host,int port,Boolean stream) 3. Socket(InetAddress address,int port) 4. Socket(InetAddress address,int port,Boolean
37、stream) 5. GetInetAddress() 返回该套接字所连接的地址 6. Getport() 7. GetLocalPort() 8. GetInprtStream() 返回该套接字的输入流 9. GetOutputStream() 返回该套接字的输出流 10. Close() 11. ToString() 以字符串的形式返回该SOCKET地址和端口信息 12. SetSocketImplFactory(SocketImplFactory fac) SocketInputStream类 1. read() 从套接口读取一个字节的数据。如果发生IO
38、错误,则会导致IOException例外。 2. read(byte b[]) 从套接口读取一个字节数组的数据。如果发生IO错误,则会导致IOException例外。 3. read(byte b[],int off,int length) 从套接口中的偏移值为off出读取length长度的数据,存放在字节数组b中。如果发生IO错误,则会导致IOException例外。 4. skip(int numbytes) 跳过输入数据流的numbytes个字节。如果发生IO错误,则会导致IOException例外。 5. available() 返回无阻塞情况下可以读入的字节数。 返
39、回值:int 如果发生IO错误,则会导致IOException例外。 6. close() 关闭数据流。 SocketOutputStream类 1. write (int b) 向套接口写一个字节的数据。如果发生IO错误,则会导致IOException例外。 2. write (byte b[]) 将给定缓冲区b的(字节数组)的内容写到套接口。如果发生IO错误,则会导致IOException例外。 3. write (byte b[],int off,int length) 将给定字节数组b中的偏移值为off,长度为length的数据写入套接口中。如果发生IO错误,则
40、会导致IOException例外。 4. close() 关闭数据流。 11.4.2 socket通信的实现步骤 无论一个socket通信的功能多么齐全,程序多么复杂,其基本结构都是一样的,都包括以下五个基本的步骤。 以服务器端为例,服务器程序编写步骤如下 : (1 )打开SOCKET,创建serverSocket对象 ServerSockets s=newServerSocket(端口号) ; (2 )等待客户机的连接 Sockets= s.accept(); 通过ServerSocket 对象的accept()方法可以接收客户机程序的连接请求,其返回值是一个sock
41、et类型的对象。程序运行到这里将处于等待状态。 (3 )生成输入输出流 PrintStreamOut=newPrintStream(s.getOutStream()); BufferedReaderin=newBufferedReader(newInputStreamReader(s.getInputStream())); 通过serverSocket对象的getOutputStream()和getInputStream() 方法可以分别获得输出、输入流。 (4)处理输入输出流 通过输入流可以读取客户机程序发来的信息;通过输出流可以向客户机程序发信息。这里所要考虑的是何时读取信
42、息以及何时发送信息。 (5) 关闭socket 在采用线程编程时,(1)(2)可以放在主程序的循环中,反复接收各个用户的连接请求。接收到客户机的连接请求后,(3)(4)可以交给线程处理 一、打开socket java在包中提供了两个类:Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。 例如: 对于客户端程序,可以通过生成一个Socket对象来 打开一个socket: Socket client; client=new Socket ("Machine name", Portnumber); 在选择端口时,必须小心。每一个端口提供一种特定的服
43、务,只有给出正确的端口号,才能获得相应的服务。通常,0-1023的端口号为系统所保留(即众所周知的端口号),例如http服务的端口号为80,telnet→23,FTP→20,21等。所以我们在选择端 口号时,应选择一个大于1023的自由端口号,以防止发生冲突。 在打开socket时还应该进行异常处理,对发生的错误作出响应: try{ Socket socket=new Socket("KEG",4700); } catch(Exception e){ System.out.println("Error:"+e); } 对于服务方
44、我们通过生成一个ServerSocket对象打开socket,然后调用方法accept()准备接收客户发来的连接请求,同样,选择端口号时也要防止发生冲突。 ServerSocket server=null; try { server=new ServerSocket(4700); }catch(Exception e){ System.out.println("can not listen to :"+e); } Socket socket=null; try { socket=server.accept(); }catch(E
45、xception e){ System.out.println("Error:"+e); } 方法accept( )等待客户的请求,直到有一个客户启动并请求连接到相同的端口, 然后 accept( )返回一个对应于客户的socket句柄.这时,客户方和服务方都建立了用于通信的socket ,接下来就是由各个socket分别打开各自的输入/出流。 二、 打开输入/出流 类Socket提供了方法:getInputStream( )和getOutputStream( )来得到对应的输入/输出流以进行读/写操作,这两个方法分别返回InputStream和Ou
46、tputstream类对象。 为了便于读/写数据,我们可以在返回的输入/出流对象上建立过滤流,如DataInputStream、 DataOutputStream或PrintStream类对象。 如: PrintStream os=new PrintStream(new BufferedOutputStream( socket.getOutputStream())); DataInputStream is=new DataInputStream(socket.getInputStream()); 三、发送、接收数据 通过输
47、入流可以读取客户机程序发来的信息;通过输出流可以向客户机程序发信息。 四、 关闭socket 在关闭socket之前,应将与socket相关的所有的输入/出流全部关闭,这是因为一个良好的程序应该在执行完毕时清除所有的资源。 os.close(); is.close(); socket.close(); 在关闭socket时,一定要注意关闭的顺序,与socket相关的所有的输入/出流应该首先关闭,然后再关闭socket. 11.4.3 JAVA SOCKET编程实例 例11-1 (见附件) 11.5 JAVA数据报通信 11.5.1数据报通信和流式
48、通信 当用户进行网络编程时,有两种通信可供其选择:数据报通信(无连接方式)和流式通信(面向连接)。 数据报通信协议UDP(User Datagram Protocol)是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。 流式通信协议TCP(Tranfer control protocol)与UDP不同,它是面向连接的协议, 发送方和接收方的成对的两个Socket之间必须建立连接,以便在TCP协议的基础上进行通信。 当一个socket(通常都是serversocket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以 进行发送或接收操作。 究竟应使用哪种协议,TCP还是UDP,这取决于不同的应用情况。 使用UDP时,每个数据报中都给出了完整的地址信息,因此无需建立发送方和接收方的连接;对于TCP协议,由于它是一个面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中多了一个连接建立的时间。 使用UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64K之






