第13章 JAVA网络编程

13.1 URL类

URL类是java.net包中一个重要的类,使用URL创建对象得到应用程序称为客户端程序。

一个URL对象通常包含最基本的三部分信息:协议、地址和资源。

协议:URL对象所在JVM支持的协议,许多协议并不为我们所采用,常用的HTTP、FTP、File协议都是JVM支持得到协议。

地址:能连接的有效IP地址或域名。

资源:主机上任何一个文件。

13.1.1 URL的构造方法

①public  URL(String spec)  throws  MalformedURLException
②public  URL(String protocol,String host,String file)  throws  MalformedURLException
13.1.2 读取URL中的资源
URL对象调用InputStream openStream()方法返回一个输入流,该输入流指向URL对象所包含的资源。
注意:由于网络速度或其他因素,URL资源的读取可能会引起堵塞,因此,程序需在一个线程中读取URL资源,以免堵塞主线程。
13.2 InetAddress类
13.2.1 地址的表示
Internet上的主机有两种方式表示地址
①域名,如www.google.com
②IP地址,如192.168.0.1
java.net包中的InetAddress类对象含有一个Internet主机地址的域名和IP地址,如www.sina.com.cn/202.108.37.40
在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转换为IP地址,这样才能和主机建立连接。
13.2.2 获取地址
①获取Internet上主机的地址
使用InetAddress类的静态方法getByName(String s)将一个域名或IP地址传递给该方法的参数s,获得一个InetAddress对象,该对象包含主机地址的域名和IP地址。
public String getHostName() 获取InetAddress对象的域名
public String getHostAddress() 获取InetAddress对象的IP地址
②获取本地机的地址
getLocalHost()获得一个InetAddress对象,该对象有本地机器的域名和IP地址。
13.3 套接字
13.3.1 套接字概述
网络通信使用IP地址标识Internet上的计算机,使用端口号标识服务器上的进程(程序)。
如果服务器上的一个程序不占用一个端口号,用户程序就无法找到它,就无法与程序交互信息。
端口号:规定为一个16位的0~65535之间的整数,0~1023被预先定义的服务通信占用(如telnet占用端口23,http占用端口80等),除非需要访问这些特定服务,否则,就应该使用1024~65535这些端口中的某一个进行通信,以免发生端口冲突。
当两个程序需要通信时,它们可以通过使用Socket类建立套接字对象并连接到一起(端口号与IP地址的组合得出一个网络套接字)
13.3.2 客户端套接字
客户端程序使用Socket类建立负责连接到服务器的套接字对象。建立套接字对象可能发生IOException异常。
Socket的构造方法是Socket(String host,int port),
host:服务器的IP地址
port:端口号
try{Socket clientSocket=new Socket(http://192.168.0.78”,2010;
}
catch{IOException e}{}
clientSocket可以使用方法:

getInputStream()获得一个输入流,这个输入流的源和服务器端的一个输出流的目的地刚好相同。

getOutputStream()获得一个输出流,这个输出流的目的地与服务器端的一个输入流的源刚好相同。

13.3.3 ServerSocket对象与服务器端套接字

为了能使客户成功连接到服务器,服务器必须建立一个ServerSocket对象,该对象通过将客户端的套接字对象和服务器端的一个套接字对象连接起来,从而达到连接的目的。

ServerSocket的构造方法是ServerSocket(int port),port必须和客户呼叫的端口号相同,当建立ServerSocket对象时可能发生IOException异常(如端口已被占用)。

当服务器的ServerSocket对象建立后,就可以使用方法accept()将客户端的套接字和服务器端的套接字连接起来。

“接收”客户端套接字是指服务器端ServerSocket对象调用accept()方法会返回一个和客服端Socket对象相连接的Socket对象,该对象会驻留在服务器端:accept()方法会堵塞线程的进行,直到接收到用户的呼叫。

ServerSocket对调用setSoTimeout(int timeout)方法设置超时值(单位为毫秒),timeout是一个正值,当accept方法堵塞的时间超过timeout则触发SocketTimeoutException。

getInputStream()获得一个输入流,这个输入流的源和客户端的一个输出流的目的地刚好相同。

getOutputStream()获得一个输出流,这个输出流的目的地与客户端的一个输入流的源刚好相同。

注意:从套接字中连接读取数据与文件中读取数据有很大的不同,从文件读取数据时,所有数据都已经在文件中了。使用套接字读取数据,可能在另一端数据发送前,就已经开始读取了,这时,就会堵塞本线程,直到该方法读取到信息,线程才进行执行后续操作

连接建立后,服务器端的套接字对象调用getInetAddress()方法可以获取有关InetAddess对象,该对象有客户端的IP地址和域名,同样,客户端套接字对象也可以使用该方法获取

服务器端的IP地址和域名。

双方通信完毕后,套接字应使用close()方法关闭套接字连接

13.3.4 使用多线程技术

服务器收到一个客服端的套接字后,就应该启动一个专门为该客户服务的线程。

可以用Socket类的不带参数的构造方法Socket()创建一个套接字对象,该对象再调用public void connect(SocketAddress endpoint) throws IOException请求和参数SocketAddress指定的地址的服务器端的套接字建立连接,为了使用connect方法,可以使用SocketAddress的子类InetSocketAddress创建一个对象,构造方法:public InetSocketAddress(InetAddress addr,int port)

在套接字通信中,有两个基本原则:

(1)服务器应启动一个专门的线程,在线程中和客户的套接字建立连接。

(2)由于套接字的输入流在读取信息时可能发生堵塞,客户端和服务器端都需要在单独的的线程中读取信息

13.4 UDP数据报

套接字是基于TCP协议的网络通信,即客户端程序和服务器端程序是有连接的,双方的信息是通过程序中的输入、输出流来交互的。

UDP(用户数据报协议),基于UDP的通信与基于TCP的的通信不同,基于UDP的信息传递更快,但不提供可靠性保证。数据在输出时,用户无法找到数据能否正确到达目的地主机,也不能确保数据顺序到达。

基于UDP通信的基本模式是:

①将数据打包,成为数据包,如何将数据包发往目的地。

②接收发来的数据包,然后查看数据包中的内容。

13.4.1 发送数据包

用DatagramPacket类将数据打包,即用DatagramPacket创建一个对象,称为数据包。

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

用DatagramSocket类的不带参数的构造方法DatagramSocket()创建一个对象,该对象负责发送数据包。

DatagramSocket mail_out=new DatagramSocket();

mail_out.sent(pack);

13.4.2 接收数据包

用DatagramSocket的DatagramSocket(int port)方法,其中prot是发送文件的主机的端口号,使用receive(DatagramPacket pack)接收数据包,该方法将数据包传递到数据包参数中

使用DatagramPacket类的DatagramPacket(byte data[],int length)创建一个数据包,以便接收数据包。

注:  receive方法可能会堵塞,直到接收到数据包。

数据包数据的长度不能超过8192KB。

13.5 广播数据报

D类地址:D类地址表示用来代表位置的,不能用D类地址来查找主机。224.0.0.0~239.255.255.255是D类地址(保留地址、组播地址)。

广播接收广播的主机必须加入到同一个D类地址

13.6 JAVA远程调用

JAVA远程调用(Remote Method Invocation,RMI)是一种分布式技术。

13.6.1 远程对象及其代理

①远程对象

主流在服务器的对象是客户要请求的对象,称为远程对象。

②代理与存根(Stub)

RMI不希望客户应用程序直接与远程对打交道,而是让远程对象的代理与用户程序交互。

代理的特点:与远程对象实现相同的接口,如果代理确认远程对象能调用相同方法,就用实际的方法调用委派给远程对象。

RMI会帮助生成一个存根:一种特殊的字节码,并让存根产生的对象作为远程对象代理。代理驻留在客户端,远程需要将RMI生成的存根复制或下载的客户端。

③Remote接口

要求远程对象必须实现java.rmi包中的Remote接口,只有实现该接口的类实例才能被RMI认为是一个远程对象。Remote接口没有方法,该接口仅仅起一个标识作用。

13.6.2 RMI的设计细节

①扩展Remote接口

定义一个接口是Remote的子接口,即扩展Remote接口

②远程对象

RMI为了让一个对象成为远程对象还需要精心一些必要的初始化工作,在编写创建远处对象的类时,可以简单地让该类为java.rmi.server包中的UnicastRemoteObject类的子类

③存根与代理

RMI使用rmic命令生成存根RemoteConcreteSubject_Stub.class

④启动注册:rmiregistry

在远程服务器创建对象之前,RMI要求远程服务器必须首先启动注册rmiregistry,只有启动了rmiregistry,远程服务器才可以创建远处对象,并将该对象注册到rmiregistry所管理的注册表中。

⑤启动远程对象服务

远程服务器使用java.rmi包中的Naming类调用其类方法rebind(String name,Remote obj)确定一个远程对象到rmireegistry所管理的注册表中,该方法的name参数是URL格式,obj参数是远程对象,将来客服端代理会通过name找到远处对象obj。

⑥运行客户端程序

客户端使用java.rmi包中的Naming类调用类方法lookup(String name)返回一个远处对象的代理,即使用存根重生一个和远程对象有相同接口的对象,lookup(String name)方法中的name参数的取值必须是远程对象注册的name。