第十二章  Java网络编程入门

  1. Java提供的三大类网络功能:

    (1)URL和URLConnection:三大类中最高级的一种,通过URL网络资源表达方式,可以很容易确定网络上数据的位置。利用URL的表示和建立,Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络的另一端。

    (2)Socket:又称“套接字”,用于描述IP地址和端口(在Internet中,网络中的每台主机都有一个唯一的IP地址,而每台主机又通过提供多个不同端口来提供多种服务)。在客户/服务器网络中,当客户机中运行的应用程序(如网页浏览器)需要访问网络中的服务器时,客户机会被临时分配一个Socket,然后它会通过服务器的Socket向服务器发出请求。简而言之,网络中的数据的发送和接收都是通过Socket来完成的,Socket就像一个文件句柄(读写文件时的一个唯一的顺序号),用户可以通过读写Socket来完成客户机、服务器之间的通信。

    (3)Datagram:三大类中最低级的一种。其他网络数据传送方式都假想在程序执行时建立一条安全稳定的通道,但是以Datagram方式传送数据时,只是把数据的目的地记录在数据包中,然后就直接放在网络上进行传输。系统不保证数据一定能安全送达,也不能确定什么时候可以送到。也就是说,Datagram不能保证传送质量。

  2. 了解Java网络编程基础知识

    (1)网络编程中有两个主要问题:如何准确定位网络上的一台或多台主机,以及找到主机后如何可靠地、高效地进行数据传输。在TCP/IP网络协议中,IP地址可以唯一确定Internet上的一台主机,而TCP则提供可靠的数据传输机制。

    (2)TCP/IP协议(传输控制协议/因特网互联协议  网络通信协议)

    TCP/IP协议是Transmission Control Protocol/Internet Protocol的缩写,是Internet最基本的协议,是供已连接因特网的计算机进行通信的通信协议。其定义了电子设备(如计算机)如何连入因特网,以及数据如何在它们之间传输的标准。TCP/IP协议由网络层IP协议和传输层的TCP协议组成。

    计算机网络通信划分为七层,自下而上分别是:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。每一层都提供了不同的网络协议。

    TCP/IP使用客户端/服务器模式进行通信,且通信是点对点的,即通信是在网路中的两台主机之间进行的。TCP协议为传输控制协议,负责聚集信息或把文件拆分成更小的包。这些包通过网络传送到接收端的TCP层,接收端的TCP层把包还原为原始文件。IP协议是网际协议,负责处理每个包的地址部分,使这些包能正确到达目的地。网络上的网关计算机根据信息的地址来进行路由选择。即使来自同一文件的分包路由也有可能不同,但最后会在目的地汇合。

  3. TCP协议与UDP协议(传输层)

    (1)TCP协议:面向连接,可靠传输。客户端与服务器之间必须先建立连接才能进行通信。得到一个顺序的、无差错的数据流。可以传输大容量数据,并确保服务器正确地接收到客户端发送的全部数据。

    (2)UDP协议:User Datagram Protocol(用户数据报协议),一种面向无连接,不可靠的传输协议。它将信息包装成数据报进行传输,数据报中包含完整的源地址或目的地址。不能保证数据报是否到达目的地、到达目的地的时间以及内容的正确性。传输数据大小限定在64KB之内。

  4. IP地址

    Java中用InetAddress类来描述IP地址。这个类没有公共的构造方法,但是它提供了三个用来获得InetAddress对象的静态方法。分别是:

    InetAddress getLocalHost():返回一个本地主机的InetAddress对象。

    InetAddress getByName(String host):返回主机名host指定的InetAddress对象。

    InetAddress[] getAllByName():对于某个多IP地址主机,可用该方法得到一个IP地址数组。

    此外,InetAddress类还提供了以下方法:

    String getHostAddress();返回IP地址字符串。

    String getHostName();返回主机名。

    例如:

    public class InetAddressTest{

       public static void main(String[] args){

           InetAddress ia;

           ia = InetAddress.getLocalHost();   //获得本地主机的InetAddress对象

           String localHostAddress = ia.getHostAddress(); //获得本地主机IP地址

           String localHostName = ia.getHostName()  //获得本地主机名

           ia = InetAddress.getByName("www.baidu.com");  //获得百度网主机的InetAddress对象

           String bdHostAddress = ia.getHostAddress();  //获取百度网主机地址

           String bdHostName = ia.getHostName();        //获取百度网主机名

       }

    }

  5. 端口

    端口是为了在一台主机上提供更多的网络资源而采取的一种手段。只有通过IP地址和端口号才能唯一地确定网络通信中的进程。端口用端口号来标记,端口号用0~65535的整数表示。

  6. 使用URL访问网络资源

    (1)URL:Uniform Resource Locator(统一资源定位符),对Internet资源的一个引用。大多数情况下,资源表示为一个文件,如一个HTML文档,一个图像文件,或一个声音片段等。因此,可以将URL理解为一个Internet资源地址。通用格式如下:

    <PROTOCOL>://<HOSTNAME:PORT>/<PATH>/<FILE>

    PROTOCOL表示Internet协议,常用的有HTTP、FTP、SMTP等。HOSTNAME表示资源所在的Internet主机名。主机名和IP地址是一一对应的,通过域名解析可以由主机名得到IP地址。PORT表示端口号,每一个Internet协议都有自己对应的端口号。PATH和FILE分别表示资源的路径名和文件名。

    (2)创建URL对象

    Java中可以使用URL类访问Internet资源。URL类的构造方法如下:

    a. URL(String spec):使用表示URL地址的字符串spec创建URL对象。例如:

    URL url = new URL("http://www.sina.com.cn");

    b. URL(URL baseURL,String relativeURL):使用绝对地址baseURL与相对地址relativeURL创建URL对象。例如:

    URL baseURL = new URL("http://www.base.edu.cn");

    URL url = new URL(baseURL,"/library/library.htm");

    c. URL(String protocol,String hostName,int port,String fileName):使用网络协议protocol、主机名hostName、端口号port、文件名fileName创建URL对象。例如:

    URL url = new URL("http","www.baidu.com","80","/library/library.htm"); 

    d. URL(String protocol,String hostName,String fileName):使用网络协议protocol、主机名hostName、文件名fileName创建URL对象。例如:

    URL url = new URL("http","www.baidu.com","/library/library.htm");

    备注:URL类的构造方法会抛出非运行时异常MalformedURLException,程序需要对此进行处理。 

  7. 直接通过URL对象读取内容

    成功建立一个URL对象后,可以调用openStream()方法从网络资源中读取内容。例如:

    public class OpenStreamTest{

       public static void main(String[] args){

           try{

                URL sina = new URL("http://www.sina.com.cn/");

                DataInputStream dis = new DataInputStream(sina.openStream());

                String readLine;

                while((readLine = dis.readLine())!=null){

                    System.out.print(readLine);

                }

                dis.close();

            }catch(MalformedURLException me){

                System.out.print("MalformedURLException:"+me);      

            }catch(IOException ioe){

                System.out.print("IOException:"+ioe);

            }

        }

    }

8. 建立一个URL连接并从中读取内容

   通过URL类的openStream()方法,只能从网络资源中读取数据。如果想同时对数据进行读写操作,则必须利用URLConnection类建立一个URL连接。当与一个URL对象建立连接时,首先要通过URL对象的openConnection()方法生成URLConnection对象。URLConnection对象表示Java程序和URL对象在网络上的通信连接。如果连接过程失败,将会产生IOException。例如:

    public class URLConnectionTest{

        public static void main(String[] args){

            try{

                URL baidu = new URL("http://www.baidu.com/");

                URLConnection conn = baidu.openConnection();

                //创建数据输入流

                DataInputStream dis = new DataInputStream(baidu.getInputStream);

                String inputLine;

                while((inputLine = dis.readLine())!=null){

                    System.out.print(inputLine);

                }

                dis.close();

            }catch(MalformedURLException me){

                System.out.print("MalformedURLException:"+me);

            }catch(IOException ioe){

                System.out.print("IOException:"+ioe);

            }

        }

    }

9. 使用Socket进行网络通信

    在应用层通过传输层进行数据通信时,TCP或UDP协议会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个端口传输数据。为了区别不同的应用程序进程或连接,计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。每个Socket由IP地址、传输层协议(TCP或UDP)、端口号三个参数唯一确定。

    使用Socket进行服务器/客户端的通信时,可以分为三个步骤:服务器监听,客户端请求,连接确认。

    (1)服务器监听:服务器端Socket实时监听某个端口是否有连接请求。

    (2)客户端请求:指由客户端的Socket提出连接请求,要连接的目标是服务器端的Socket。客户端的Socket首先需要描述它要连接的服务器端的Socket,指出服务器端Socket的IP地址和端口号,然后便可以向服务器端发送连接请求。

    (3)连接确认:当服务器端的Socket监听到或者接收到客户端Socket的连接请求时,就响应客户端的请求,并建立一个新的线程,然后把服务器端的Socket的描述发送给客户端。一旦客户端确认了此描述,一个连接便建立起来了。而服务器端Socket继续处于监听状态,并接收其他客户端Socket的连接请求。

    (4)常见的Socket主要分为两种:

    a. 流式Socket:一种基于TCP协议的通信,即在通信开始之前先由通信双方确认身份并建立一条连接通道,然后通过这条通道传输数据。

    b. 数据报Socket:一种基于UDP协议的通信,无需通信双方建立连接,而是直接将信息打包传向指定的目的地。

    (5)流式Socket编程

      java.net包中定义了Socket类和ServerSocket类,它们是实现流式Socket通信的主要工具。创建Socket对象就创建了一个客户端与服务器端的连接,而创建一个ServerSocket对象就创建了一个监听服务。

     a. Socket类:通过构造一个Socket对象可以建立客户端与服务器的连接。Socket类的构造方法如下:

     Socket(String host,int port)

     Socket(InetAddress address,int port)

     Socket(InetAddress address,int port,InetAddress localAddr,int localPort)

    其中,host、port和address分别表示连接主机的主机名、端口号和IP地址;locaAddr表示本地主机的IP地址;localPort表示本地主机端口号。例如:

    Socket mySocket = new Socket("www.sina.com",3000);

    备注:每一个端口号提供一种特定的服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统保留,例如,http服务的端口号为80,telenet服务的端口号为23,ftp服务端口号为21。

    创建一个新的Socket对象后,就可以使用其getInputStream()方法获得一个InputStream流,然后可以通过InputStream对象从某个主机接收信息;而使用getOutputStream()可以获得一个OutputStream对象,利用它可以发送信息到某个主机。例如:

    public class GetDayTime{

        public static void main(String[] args){

            try{

                Socket socket = new Socket("stdtime.gov.hk",13);

                BufferedReader br = new BufferedReader(new                                                                      InputStreamReader(socket.getInputStream()));

                String daytime = br.readLine();

                System.out.println("DayTime received:"+daytime);

                socket.close();

            }catch(IOException e){

                System.out.println("ERROR:"+e);

            }

        }

    }

    b. ServerSocket类

    ServerSocket类表示通信双方中的服务器,它可以监听客户端发送的连接请求并进行处理。将服务器所监听的端口号传递给ServerSocket的构造方法,就可以创建一个ServerSocket对象。ServerSocket类的构造方法如下:

    ServerSocket(int port);

    ServerSocket(int port,int count);

    其中,port表示端口号,count表示服务器所能支持的最大连接数。例如:

    ServerSocket serverSocket = new ServerSocket(5000);

    这里指定服务器监听的端口号是5000。当创建一个ServerSocket对象后,就可以调用accept()方法接受来自客户端的请求。其格式为:

    Socket socket = serverSocket.accept();

    ServerSocket对象的accept()方法会使服务器端的程序一直处于阻塞状态,直到捕获一个来自客户端的请求,并返回一个Socket类的对象来处理与客户端的通信。

    当需要结束监听时,可以使用如下语句关闭这个ServerSocket对象:

    serverSocket.close();

    (6)数据报Socket编程

    java.net包中提供了DatagramPacket和DatagramSocket类用来支持无连接的数据报Socket通信。DatagramSocket类用于在程序之间建立传送数据报的通信连接,DatagramPacket类则用于存储数据报等信息。

    a. DatagramSocket类:构造方法如下:

    DatagramSocket();

    DatagramSocket(int port);

    DatagramSocket(int port,InetAddress localAddr);

    其中,port表示端口号;localAddr表示本地地址。

    DatagramSockt类构造方法会抛出非运行时异常SocketException,程序需要对此进行处理。

    b. DatagramPacket类:

    用数据报方式编写Client/Server程序时,无论在客户端还是服务器,都首先需要建立一个DatagramSocket对象,用来接收或发送数据报,而DatagramPacket对象是数据报传输的载体。构造方法如下:

    DatagramPacket(byte[] buf,int length);

    DatagramPacket(byte[] buf,int length,InetAddress address,int port);

    其中,字节数组buf中存放欲发送或接收的数据报,length表示数据报的长度,address和port表示数据报发送的目的地和主机端口号。

    在客户端或服务器端接收数据之前,应该采用DatagramPacket类的第一种构造方法创建一个DatagramPacket对象,然后调用DatagramSocket类的receive()方法等待数据报的到来。例如:

    DatagramSocket socket = new DatagramSocket();

    DatagramPacket packet = new DatagramPacket(buf,256);

    socket.receive();

    在发送数据之前,需要使用DatagramPacekt类的第二种构造方法创建一个新的DatagramPacket对象,即要指明数据报发送的目的地址和端口号。发送数据报通过DatagramSocket类的send()方法实现。例如:    

    DatagramSocket socket = new DatagramSocket();

    DatagramPacket packet = new DatagramPacket(buf,length,address,port);

    socket.send(packet);