第五章 输入、界面和网络

  5.3 网络编程

  设计人机界面的目的是帮助用户与远端的服务器进行交互式对话,但在大多数情况下,实现这种对话需要Applet对服务器上的文件进行访问,或与服务器交换信息,所以要实现这种对话就离不开网络的支持。在Java中,也有网络编程。但是,出于安全性的考虑,Java中对Applet的访问权限有严格的控制。如果一台工作站运行另一台服务器上的Applet,那么这个Applet不经允许不能访问工作站上的文件,除了那台服务器,Applet也不能访问其它机器上的文件,也就是说,在Applet中只能访问服务器中的文件。而在Application中就没有这样的限制。实际上,Java本身的用途是一种网络计算语言,编写Applet只是Java用途中的一种,真正复杂的Java程序是进行网络通信的Application。
   Java中的网络编程,我们分成网络资源的使用、Socket类和数据报三个部分来介绍。

  5.3.1 网络资源的使用

  关于网络资源的编程主要有两方面,一个是在Applet中建立于其它URL的连接,另一个是对服务器上的文件进行操作。这两个方面需要使用几个重要的类AppletContext类、URL类和URLconnection类
   1. URL类
   在Internet上的所有网络资源都是用URL(Uniform Resource Locator)来表示的,URL类在Applet的网络编程中也是相对重要的,网络编程都以它为基础,其它两个类也要使用URL类。URL类的构造方法有四种。
   URL(String, String, int, String)构造一个URL类。第一个String类型的参数是协议的类型,可以是http,ftp,file等。第二个String类型参数是主机名,int类型参数是指定端口号,最后一个参数是给出文件名或路径名。
   URL(String, String, String)构造一个URL类。参数含义与上相同,使用缺省端口号。
   URL(URL, String)构造一个URL类。使用给出的URL和相对路径,String类型参数是相对路径。
   URL(String)使用URL字符串构造一个URL类。与以往介绍的其它类不同的是,在构造URL类时,必须有相应的异常处理(有关异常处理在后面的第七章中将详细介绍)。因此应写成:
   String ECNU = *http://www.ecnu.edu.cn/*;
   try { url = new URL(ECNU); }
   catch (MalformedURLException e) {
   …… //出错处理
   }
  在构造完一个URL类后,可以使用URL类中的openStream方法与服务器上的文件建立一个流的连接,但是这个流是输入流(InputStream),只能读而不能写。(关于流的概念,在第六章有专门介绍)。
   2. URLConnection类
  使用URL类中openConnection方法可以构造一个URLConnection类。这个类中包含了更丰富的方法,可以对服务器上的文件进行更多的处理。URLConnection类的构造方法是URLConnection(URL),可以构造一个对指定URL的连接对象。用URLConnection的构造方法来构造URLConnection类时,并未建立与指定URL的连接,所以还必须使用URLConnection类中的connect方法建立连接。而用URL类中的openConnection方法来构造时,已建立了连接,就不需要使用connect方法。
  3. AppletContext类
  AppletContext类是一个接口类,Applet通过AppletContext接口与环境进行通讯。
  可以利用这个类从Applet环境获取信息,而这个环境一般是指浏览器。AppletContext类没有构造方法,但可以通过Applet类中的getAppletContext方法获取AppletContext接口使用showStatus方法可以在浏览器的状态条中显示提示信息。使用showDocument方法可以通知浏览器在指定窗口中显示另一个URL的内容。比如要浏览器显示中国教育科研网总节点的主页,可以写成:
   String cernet = *http://www.cernet.edu.cn/*;
   try {url = new URL(cernet); }
   catch (MalformedURLException e) {
   …… //出错处理
   }
   getAppletContext().showDocument(url);

  5.3.2 Socket编程

  Socket原先是Unix系统中的概念,以后在网络编程中广泛使用。在Java的Application中可以将Socket类和ServerSocket类分别用于Client端和Server端,在任意两台机器间建立连接。下面来介绍这两个类的使用方法。
   1. Socket类
   Socket类用在用户端,用户通过构造一个Socket类来建立与服务器的连接。Socket连接可以是流连接,也可以是数据报连接,这取决于构造Socket类时使用的构造方法。一般使用的流连接,流连接的优点是能所有数据都能准确、有序地送到接收方,缺点是速度较慢。Socket类的构造方法有四种。
   Socket(String, int)构造一个连接指定主机、指定端口的流Socket。Socket(String, int, boolean)构造一个连接指定主机、指定端口的Socket类,boolean类型的参数用来设置是流Socket还是数据报Socket。Socket(InetAddress,int)构造一个连接指定Internet地址、指定端口的流Socket。Socket(InetAddress, int,boolean)构造一个连接指定Internet地址、指定端口的Socket类,boolean类型的参数用来设置是流Socket还是数据报Socket。在构造完Socket类后,就可以通过Socket类来建立输入、输出流,通过流来传送数据。
   2. ServerSocket类
   ServerSocket类用在服务器端,接收用户端传送的数据。ServerSocket类的构造方法有两种。
   ServerSocket(int)在指定端口上构造一个ServerSocket类。
   ServerSocket(int, int)在指定端口上构造一个ServerSocket类,并进入监听状态,
   第二个int类型的参数是监听时间长度。
   3. 建立连接
  在介绍了Socket类和ServerSocket类后,再来介绍一下连接的过程。
  首先,在服务器端构造一个ServerSocket类,在指定端口上进行监听,这将使线程处于等待状态。然后在用户端构造Socket类,与服务器上的指定端口进行连接。服务器监听到连接请求后,就可在两者之间建立连接。建立连接的具体细节对用户是透明的,用户只需要构造这两个类就可以了。在建立连接之后,就可以建立相应的输入、输出流,两端通过流来进行通讯。下面是建立连接的一个例子,接收服务器的IP地址是202.120.88.71,端口号是80。
  服务器上的程序:
  ServerSocket server;
  Socket ssocket;
  try {
  server = new ServerSocket(80);
  svsk = server.accept();
  OutputStream out = serverSocket.getOutputStream();
  out.write(*Test*);
  }
  catch (IOException e) {
  }
  用户端的程序:
  Socket csocket;
  try {
  cSocket = new Socket ("202.120.88.71",80);
   InputStream in = clientSocket.getInputStream();
   System.out.println("client reads:" + in.read());
   }
   catch (UnknownHostException e) {
   }
   catch (IOException e) {
   }
   需要说明一下的是,通过Socket建立的流可以是双向的,即在一个Socket的两端可以分别同时建立输入流和输出流。

  5.3.3 数据报

  数据报是一种无连接的通信方式,它的速度比较快,但是由于不建立连接,不能保证所有数据都能送到目的地。所以一般用于传送非关键性的数据。发送和接收数据报需要使用Java类库中的DatagramPacket类和DatagramSocket类,下面将详细介绍。
   1.DatagramPacket类
   它是进行数据报通信的基本单位,它包含了需要传送的数据、数据报的长度、IP地址和端口等。DatagramPacket类的构造方法有两种。
   DatagramPacket(byte [],int)构造一个用于接收数据报的DatagramPacket类,byte []类型的参数是接收数据报的缓冲,int类型的参数是接收的字节数。
   DatagramPacket(byte [],int, InetAddress,int)构造一个用于发送数据的DatagramPackte类,byte[]类型参数是发送数据的缓冲区,int类型参数是发送的字节数,InetAddress类型参数是接收机器的Internet地址,最后一个参数是接收的端口号。
   2. DatagramSocket类
   DatagramSocket类是用来发送数据报的Socket,它的构造方法有两种。DatagramSocket()构造一个用于发送的DatagramSocket类。DatagramSocket(int) 构造一个用于接收的DatagramSocket类。构造完DatagramSocket类后,就可以发送和接收数据报。
   3. 发送和接收过程
   发送数据报,需要在接收端先建立一个接收的DatagramSocket,在指定端口上监听,构造一个DatagramPacket类指定接收的缓冲区(DatagramSocket的监听将阻塞线程)。在发送端需要首先构造DatagramPacket类,指定要发送的数据、数据长度、接收主机地址及端口号,然后使用DatagramSocket类来发送数据报。接收端接收到后,将数据保存到缓冲区,发送方的主机地址和端口号一并保存。随后将接收到的数据报返回给发送方,并附上接收缓冲区地址,缓冲长度、发送方地址和端口号等信息,等待新的数据。下面是一个发送和接收数据报的例子,接收端的IP地址是202.120.88.71,端口号是80,发送的数据在缓冲区message中,长度为200。
   接收端的程序:
   byte [] inbuffer = new byte[1024]; //接收缓冲
   DatagramPacket inpacket = new DatagramPacket(inbuffer, inbuffer.length);
   DatagramSocket insocket = new DatagramSocket(80);
   insocket.receive(inpacket); //监听数据
   String s = new String(inbuffer, 0, 0, inpacket.getlength); //将接收的数据存入字符串s
   发送端的程序:
   DatagramPacket outpacket = new DatagramPacket(message,200,"202.120.88.71",80);
   DatagramSocket outsocket = new DatagramSocket();
   outsocket.send(outpacket);