Java网络编程

                                    **Java网络编程**
    
                    **网络技术基础**
	OSI模型
我们已经知道,计算机网络是处于不同地理位置的计算机系统通过通信设备和线路连接起来,以网络软件实现资源共享的系统。在计算机网络出现的早期,不同的软件、硬件厂商按照各自的标准生产网络软、硬件产品,这就导致了不同厂商、不同结构的网络产品之间互连时会遇到的不兼容性问题。为解决此类问题,国际标准化组织ISO提出了一个OSI(Open System Interconnection,开放系统互联)参考模型,为异构系统互联提供了概念性的框架。
OSI参考模型采用分层结构,将网络分为七层,如图所示。

Java网络编程_第1张图片

在这个七层模型中,每一层使用下层提供的服务,同时为上层提供服务。各层的作用分别如下:
	 物理层。传输信息离不开物理介质,如双绞线和同轴电缆。物理层的任务就是为它的上一层提供物理连接,以及规定通行节点之间的机械和电器等特征。在这一层,数据作为原始的比特流(bit)传输。典型的设备包括集线器(Hub)。
	 数据链路层。数据链路层负责两个相邻的节点间的线路上,无差错的传送以帧(Frame)为单位的数据。每一帧包括一定量的数据和一些必要的控制信息。在传送数据时,如果接收方检测到所传的数据中有差错,就要通知发送方重新发送这一帧。本层的典型的设备是Switch(交换机)。
	 网络层。在计算机网络中两台计算机之间可能会经过很多的数据链路,也可能通过很多的通信子网。网络层的任务就是选择合适的网间路由和交换节点,确保数据及时传送到目标主机。在这一层,数据的单位称为包(Packet)。网络层将数据链路层提供的帧组成数据包,包中封装了网络层的包头,包头中含有逻辑地址信息(源主机和目标主机的网络地址)。本层的典型的设备就是路由器(Router)。
	 传输层。该层的任务是通过通信子网的特性最好地利用网络资源,为两个源主机和目标主机的会话层提供建立、维护和取消传输的连接的功能,以可靠的方式或不可靠的方式。在这一层,以可靠方式传输的数据单位称为段(Segment),以不可靠方式传输的数据单位称为数据报(Datagram)。
	会话层。在会话层及以上层次中,数据传送的单位不再另外命名,统称为报文(Message)。会话层管理进程之间的会话过程,即负责建立、管理和终止进程之间的会话。会话层还通过数据中插入校验点来实现数据的同步。
	表示层。表示层对上层的数据进行转换,以保证一个主机的应用层的数据可以被另一个主机的应用层理解。表示层的数据转换包括对数据的加密、解密、压缩、解压和格式转换等。
	应用层。应用层确定进程之间通信的实际用途,以满足用户的实际请求。例如浏览Web站点、收发E-mail、上传或下载文件等。
在数据通信中,发送方每一层都将上层的数据加上一个报头,并交给下一层。这一过程重复进行,直到底层的物理层,然后通过物理链路实际传送到接收放。在接收方,则将报头层层剥离,最后将数据交给接收进程。
不同主机之间的相同层次称为对等层。例如主机A中的表示层和主机B中的表示层互为对等层、主机A中的会话层和主机B中的会话层互为对等层等。对等层之间互相通信需要遵守一定的规则,如通信的内容、通信的方式,我们将其称为协议(Protocol)。我们将某个主机上运行的某种协议的集合称为协议栈。主机正是利用这个协议栈来接收和发送数据的。
OSI参考模型通过将协议栈划分为不同的层次,可以简化问题的分析、处理过程以及网络系统设计的复杂性。

           	TCP/IP分层模型
ISO制定的OSI参考模型的过于庞大、复杂招致了许多批评。与此对照,由技术人员自己开发的TCP/IP协议栈获得了更为广泛的应用。实际上,TCP/IP协议也是目前因特网范围内运行的唯一一种协议。图表示了TCP/IP分层模型与OSI模型的对比图。 

Java网络编程_第2张图片

TCP/IP协议被组织成四个概念层,其中有三层对应于ISO参考模型中的相应层。TCP/IP协议族并不包含物理层和数据链路层,因此它不能独立完成整个计算机网络系统的功能,必须与许多其他的协议协同工作。 
TCP/IP分层模型的四个协议层分别完成以下的功能: 
	网络接口层。网络接口层包括用于协作IP数据在已有网络介质上传输的协议。实际上TCP/IP标准并不定义与ISO数据链路层和物理层相对应的功能。相反,它定义像ARP(Address Resolution Protocol,地址解析协议)这样的协议,提供TCP/IP协议的数据结构和实际物理硬件之间的接口。 
	网络互联层。网络互联层对应于OSI七层参考模型的网络层。本层包含IP协议、RIP协议(Routing Information Protocol,路由信息协议),负责数据的包装、寻址和路由。同时还包含网间控制报文协议(Internet Control Message Protocol,ICMP)用来提供网络诊断信息。 
	传输层。传输层对应于OSI七层参考模型的传输层,它提供两种端到端的通信服务。其中TCP协议(Transmission Control Protocol)提供可靠的数据流运输服务,UDP协议(User Datagram Protocol)提供不可靠的用户数据报服务。 
	应用层。应用层对应于OSI七层参考模型的应用层和表示层。因特网的应用层协议包括Finger、Whois、FTP(文件传输协议)、 Gopher、HTTP (超文本传输协议)、Telent(远程终端协议)、SMTP(简单邮件传送协议)、IRC(因特网中继会话)、NNTP(网络新闻传输协议)等。
网络编程的实质就是编写程序直接或间接地通过网络协议与其它计算机上的某个程序进行通讯。那么,程序员在网络编程中需要关注的问题包括两个:一是如何找到网络上的主机上的要进行通讯的程序,二是找到了主机上的程序后如何传输数据。
我们首先来看如何找到网络上某个主机上要进行通讯的程序。
寄信的时候,要在表格中填上邮政服务能够理解的收信人的地址。在给别人打电话之前,必须给电话系统提供联系人的电话号码。同样,一个程序要与另一个程序通信,就要给网络提供足够的信息,使其能够找到另一个程序。在TCP/IP协议中,网络互联层主要负责网络主机的寻址定位。由IP地址就可以唯一确定网络上的一台计算机。因此,我们在程序中只要提供了要通讯的主机的IP地址,就可以找到该主机。除了在程序中提供要寻找的主机的IP地址外,我们还可以提供要寻找的主机的域名,然后互联网上的DNS(域名系统)服务器会自动将该域名转化为IP地址。
一台主机上可能会运行很多程序,那么我们如何找到该主机上我们要进行通讯的程序呢?TCP/IP协议为了区分主机上的程序,提供了端口(Port)的概念,给每个基于TCP/IP协议运行的网络程序分配一个唯一的端口号。这样,我们只要提供了IP地址和端口号,就可以找到网络上指定主机上要进行通讯的指定程序。
那么什么是端口号呢?打个比方,邮政服务通过街道地址把信分发到一个邮箱,再由清空邮箱的人把这封信递送到这栋楼的正确房间中。或者考虑一个公司的内部电话系统:要与这个公司中的某个人通话,首先要拨打该公司的总机电话号码连接到其内部电话系统,然后再拨打你要找的那个人的分机号码。于是,IP地址就相当于于街道地址或公司的总机电话号码,端口号就相当于房间号或分机号码。端口号是一组16位的无符号二进制数,每个端口号的范围是1到65535(0被保留)。在互联网上,一些常用的端口号被约定赋给了某些应用程序。例如,端口号21被FTP(File Transfer Protocol,文件传输协议)使用。当你运行FTP客户端应用程序时,它将默认通过这个端口号连接服务器。
现在,我们已经解决了网络主机上程序的寻址定位问题。下一个问题就是如何传输数据到网络上某个主机的程序中。
前面我们已经知道,数据传输由TCP/IP分层模型中的传输层负责,该层包含TCP和UDP两种协议。其中,TCP代表传输控制协议(Transfer Control Protocol),它允许两个应用程序之间的进行可靠的通讯。UDP代表用户报文协议(User Datagram Protocol),它是一种非连接协议,允许两个应用程序之间进行不可靠的通讯。
下面,我们来比较两种协议。
TCP协议通常可以与打电话相比。如果我们想要给某人打电话,那么他要有一个电话、一个电话号码,并需要等待打进的电话。在我们要呼叫的人应答电话后,我们就有了一个可靠的、双向的通讯流,允许两人相互交流,甚至是同时相互交流。如果其中一人挂掉电话,那么通讯就结束了。
对于TCP网络连接来说,客户端计算机与打电话的人类似,而服务器计算机与等待接听电话的人类似。当客户端试图连接到服务器时,服务器需要正在运行,需要在网络上有一个地址,需要在一个端口(Port)上等待要来的连接。当TCP连接建立时,客户端和服务器就有了一个可靠的、可以让数据双向传输的双向通讯流。两台计算机可以保持通讯,一直到连接关闭或失败。
UDP提供了在应用程序之间发送称为数据报的协议。如果把TCP协议比作打电话,那么UDP协议可以比作寄信。报文包如同一封信,客户端发送报文到服务器时,不需要实际连接到服务器。这样,与客户端和服务器直接相连的TCP协议相比,UDP就显得不太可靠。比如,如果在同一天寄两封信,这两封信可能在同一天发出去,也可能这两封信根本就没有发出去。数据报包与此类似。UDP不能确保包会按照它们被发送的顺序接受,也不能保证包肯定会被发出去。
如果这种不可靠性对于我们编写的程序来说是不可接受的,那就应该使用TCP协议。但是,如果我们开发的网络程序对通讯可靠性没有要求,UDP可能就是一个更好的选择。因为TCP传输中,为确认传送到正确终点没有错误的附加在网络留言后的信息。

        客户端和服务器
在前面的邮政和电话系统例子中,每次通信都是由发信方或打电话者发起,而另一方则通过发回反馈信或接听电话来对通信的发起者作出响应。互联网通信也与这个过程类似。客户端(client)和服务器(server)这两个术语代表了两种角色:客户端是通信的发起者,而服务器程序则被动等待客户端发起通 信,并对其作出响应。客户端与服务器组成了应用程序。客户端和服务器这两个术语对典型的情况作出了描述,服务器具有一定的特殊能力,如提供数据库服务,并使任何客户端能够与之通信。
一个程序是作为客户端还是服务器,决定了它在与其对等端(Peer)建立通信时使用的套接字API的形式(客户端的对等端是服务器,反之亦然)。更进一步来说,客户端与服务器端的区别非常重要,因为客户端首先需要知道服务器的地址和端口号,反之则不需要。如果有必要,服务器可以使用套接字API,从收到的第一个客户端通信消息中获取其地址信息。这与打电话非常相似:被呼叫者不需要知道拨电话者的电话号码。就像打电话一样,只要通信连接建立成功,服务器和客户端之间就没有区别了。

         套接字
为了能够方便开发网络应用程序,Unix系统推出了一种应用程序访问通信协议的操作系统调用——Socket套接字,使得程序员很方便的访问TCP/IP协议,从而开发各种网络应用程序。后来Windows也引入Socket,Java语言也引入了套接字的编程模型。
Socket(套接字)是一种抽象层,应用程序通过它来发送和接收数据。一个Socket允许应用程序添加到网络中,并与处于同一个网络中的其他应用程序进行通信。一台计算机上的应用程序向Socket写入的信息能够被另一台计算机上的另一个应用程序读取,反之亦然。
不同类型的Socket与不同类型的底层协议族以及同一协议族中的不同协议栈相关联。现在TCP/IP协议族中的主要socket类型为流套接字(Stream Socket)和数据报套接字(Datagram Socket)。流套接字将TCP作为其端对端协议,提供了一个可信赖的字节流服务。一个TCP/IP流套接字代表了TCP连接的一端。数据报套接字使用UDP协议,提供了一个“尽力而为”的数据报服务,应用程序可以通过它发送最长65500字节的信息。一个TCP/IP 套接字由一个IP地址、一个端对端协议(TCP或UDP协议)以及一个端口号唯一确定。

       	Java对网络编程的支持
JDK预定义的网络编程相关类均存放在java.net包中。图是Java网络编程中常用类和接口的层次关系图。

Java网络编程_第3张图片

使用其中的哪些类取决于程序所需处理的通讯协议。例如,基于TCP的应用程序可使用 Socket、ServerSocket 等类;基于UDP的应用程序则使用DatagramPacket、DatagramSocket、MulticastSocket 等类;基于 HTTP 和 FTP 等协议直接访问 URL 资源的应用程序可使用 URL、URLConnection 等类。
InetAddress类代表IP地址。该类没有构造器,但是提供多个方法: 
	getByName(String host):根据主机获取对应的InetAddress对象。该方法是静态方法。
	getByAddress(byte[] address):根据原始IP地址获取对应的InetAddress对象。该方法是静态方法。
	String getCanonicalHostName():获取此IP地址的全限定域名。
	String getHostAddress():返回此IP地址对应的IP地址字符串。
	String getHostName():返回此IP地址对应的主机名。
	String getLocalHost():获取本地IP地址对应的InetAddress对象。
URLEncoder和URLDecoder类是HTML格式编码和解码的实用工具类。URLEncoder中提供了一个静态方法encode(String s, string enc),使用指定的编码机制将字符串转换为 application/x-www-form-urlencoded MIME格式。而URLDecoder类提供了一个静态方法 decode(String s, String enc), 使用指定的编码机制对 application/x-www-form-urlencoded 字符串解码。

         **TCP套接字编程**
如果把TCP打比方问打电话,那么套接字(Socket)就是电话。套接字提供了在两台计算机之间使用TCP进行通讯的机制。客户端程序在其通讯的末尾创建一个套接字,并试图将该套接字连接到服务器。当连接建立时,服务器在其通讯末尾建立一个套接字对象。客户端和服务器就可以从套接字中读写来进行通讯。
java.net包包含了提供所有低层通讯的类。例如,java.net.Socket类代表一个套接字,java.net.ServerSocket类提供了服务器程序监听客户端,并在二者之间建立连接的一种机制。
当使用套接字在两台计算机之间建立TCP连接时,发生了如图18.4所示的如下步骤:
1. 服务器初始化一个ServerSocket对象,指示通讯将要发生在哪个端口号上。
2. 服务器调用ServerSocket类的accept()方法。该方法会一直等待,直到一个客户端连接到服务器上的指定端口。
3.在服务器等待的同时,客户端实例化一个Socket对象,指定要连接的服务器名和端口号。
3. Socket类的构造器试图将客户端连接到指定的服务器和端口号。如果通讯建立了,客户端现在就有一个能够与服务器进行通讯的Socket对象。
4. 在服务器端,accept()方法返回一个将要连接到客户端套接字的服务器新套接字的引用。

Java网络编程_第4张图片

注意:当客户端建立一个到服务器的套接字连接时,客户端需要指定一个端口号。该端口号指示服务器正在监听的端口。但是,在客户端和服务器使用套接字连接后,它们的连接实际上发生在不同的端口。这就允许在分离线程上的服务器继续在其端口上监听其它客户端。这一切都是在后台发生的,不影响我们的代码。
在连接建立后,通讯可以通过使用I/O流发生。每个套接字都有一个OutputStream和一个InputStream。客户端的OutputStream连接到服务器的InputStream,同时客户端的InputStream连接到服务器的OutputStream。TCP是一个双向的通讯协议,所以数据可以同时通过两个流发生。
套接字流是低级I/O流InputStream和OutputStream。因此,它们可以与缓冲流、过滤流以及其它高级流链接在一起,从而可以执行任何类型的高级I/O。这也是为什么我们要在网络编程之前学习java.io包的原因。我们将会发现建立连接是很简单的,网络编程的大部分工作是将数据传过来传过去。当然,这是网络编程应该做的,从而让我们将注意力放在要解决的问题上,而不用关心低层通讯以及协议详细。这也是为什么Java在网络编程中如此流行的原因之一。
下面,我们来看一个使用套接字的示例。我们将从一个运行在服务器上的程序开始,让它为客户端请求监听某个端口。然后我们再展示如何编写一个连接到服务器应用的客户端代码。

             ServerSocket类
java.net.ServerSocket类用于服务器程序获得一个端口,并监听客户端请求。该类有四个构造器:
	public ServerSocket(int port) throws IOException. 创建绑定到特定端口的服务器套接字。如果该端口已经被其它应用程序绑定,那么就会发生一个异常。如果端口号设置为0,将会在任何空闲的端口上创建套接字。
	public ServerSocket(int port, int backlog) throws IOException.:利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。backlog参数指定可以有多少客户端存在等待队列中。如果队列满了,当客户端试图连接到该端口时,就会接收到一个异常。如果该值为0,就使用本地平台默认的队列大小。
	public ServerSocket(int port, int backlog, InetAddress address) throws IOException:与上一个构造器相似,但是用InetAddress参数指定要绑定到的本地IP地址。有多个IP地址的服务器可以用InetAddress来指定用哪个IP地址来接收客户端请求。
	public ServerSocket() throws IOException:创建非绑定服务器套接字。在使用该构造器时,用bind()方法来只绑定服务器套接字。
如果发生了错误,那么上述每个构造器都会抛出一个IOException异常。如果ServerSocket构造器没有抛出异常,这就意味着程序已经成功绑定到指定端口,并且为客户端请求做好了准备。ServerSocket类的常用方法如下:
	public int getLocalPort():返回服务器套接字监听的端口。如果我们给ServerSocket的构造器传入端口号为0,让服务器选择一个端口,那么就要用到这个方法。
	public Socket accept() throws IOException:等待要连接的客户端。该方法会阻塞,直到一个客户端连接到指定端口上的服务器,或者假如我们用setSoTimeout()方法设置了超时值,而套接字超过该值了。否则,该方法就无限阻塞下去。
	public void setSoTimeout(int timeout):设置超时值,指定在accept()方法调用期间,服务器套接字等待客户端的时间。
	public void bind(SocketAddress host, int backlog):将套接字绑定到SocketAddress对象中指定的服务器和端口。如果我们使用无参数的构造器实例化一个ServerSocket对象,就要使用该方法。
以上几个方法中,accept()方法是我们要关注的重点,因为这是服务器如何监听到来的请求的方法。当ServerSocket调用accept()方法时,如果没有设定超时值,该方法就一直要等到客户端连接后才返回。在客户端连接上来以后,ServerSocket就在一个非指定的端口上(这个端口与它监听的端口不同)创建一个新的Socket,并返回一个对这个新Socket的引用。现在TCP连接就在客户端和服务器之间存在了,通讯就开始了。
注意:如果编写一个允许多个客户端的服务器程序,那么我们肯定想让服务器套接字持续调用accept()来等待客户端。标准的技巧是当客户端连接时,启动一个新线程,用于与新客户端通讯,而让当前线程马上再次调用accept()方法。例如,如果有50个客户端连接到一个服务器,那么服务器端就会有51个线程:50个用于与客户端通讯,1个用于通过accept()方法等待新的客户端。

                  Socket类
java.net.Socket类代表用于客户端和服务器相互通讯的套接字。客户端通过实例化一个Socket获得Socket对象,而服务器从ServerSocket对象的accept()方法的返回值中获得一个Socket对象。Socket类有五个构造器用于将客户端连接到服务器:
	public Socket(String host, int port) throws UnknownHostException, IOException:试图连接到服务器上指定端口。如果该构造器没有抛出一个异常,那么连接就成功了,而客户端就连接到服务器了。当连接到服务器时,这是最简单的构造器。
	public Socket(InetAddress host, int port) throws IOException:与上一个构造器相同,但是主机是通过InetAddress对象描述的。
	public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException:连接到指定的主机和端口,并在本地主机上的指定地址和端口上创建一个套接字。当客户端有多个IP地址,或者想让套接字绑定到特定的本地端口上时,通常使用该构造器。
	public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException:与前一个构造器相同,但是主机是用InetAddress描述的。
	public Socket():创建一个非连接的套接字。以后可以使用connect()方法将该套接字连接到服务器。
当Socket类的构造器返回时,它不仅仅是实例化一个Socket对象。在构造器中,它实际上试图连接到指定的服务器和端口。如果构造器成功返回,客户端就有了一个到服务器的TCP连接。
下面列出了Socket类的一些方法。注意,因为客户端和服务器都有一个Socket对象,所以客户端和服务器都可以调用这些方法。
	public void connect(SocketAddress host, int timeout) throws IOException:将套接字连接到指定主机。本方法只有在使用无参数构造器实例化Socket时才需要。
	public InetAddress getInetAddress():返回该Socket连接到的其它计算机的地址。
	public int getPort():返回套接字绑定到的远程机器上的端口。
	public int getLocalPort().:返回套接字绑定到的本地机器上的端口。
	public SocketAddress getRemoteSocketAddress():返回远程套接字的地址。
	public InputStream getInputStream() throws IOException:返回套接字的输入流。该输入流连接到远程套接字的输出流。
	public OutputStream getOutputStream() throws IOException:返回套接字的输出流。该输出流连接到远程套接字的输入流。
	public void close() throws IOException:关闭套接字,让该Socket对象不再有能力再次连接到任何服务器。
Socket类包含了很多方法,完整的方法列表请参考JDK文档。我们会注意到Socket类中的很多方法包含允许访问和修改一个连接的不同TCP属性,例如超时值或保持活动状态设置。在所有的Socket类的方法中,最重要的两个方法是getInputStream()和getOutputStream()。下面我们将进行详细讨论。

                   **UDP套接字编程**
用户报文协议(UDP)是用于将二进制数据从一台计算机发送到另一台计算的非连接协议。这里,数据被称为数据报包,它包含了数据将要发送到的目标服务器和端口号。消息的发送者使用数据报套接字发送包,接受者使用数据报套接字接收消息。
当消息被发送时,接受者并不需要是可用的。同样,当消息接收时,发送者也不需要是可用的。

            DatagramSocket类
数据报包的发送者和接收者都使用java.net.DatagramSocket类分别发送和接收包。DatagramSocket类有四个构造器:
	public DatagramSocket(int port) throws SocketException:创建数据报套接字,并将其绑定到本地主机指定的端口上。
	public DatagramSocket(int port, InetAddress address) throws SocketException:使用指定的端口和本地地址创建数据报套接字。如果计算机有多个地址,就应该使用本构造器。
	public DatagramSocket(SocketAddress address) throws SocketException:在指定的SocketAddress上创建数据报套接字。SocketAddress封装了服务器名和端口号。
	public DatagramSocket() throws SocketException:创建一个非绑定的数据报套接字。创建之后,可以使用DatagramSocket类的bind()方法将该套接字绑定到一个端口。
如下是DatagramSocket类的一些方法:
	public void send(DatagramPacket packet) throws IOException.:发送指定的数据报包。DatagramPacket对象包含了包的目的地信息。
	public void receive(DatagramPacket packet) throws IOException:接受一个数据报包,将其存在指定的参数上。该方法会一直阻塞,不返回,除非接收到数据报包,或者套接字超时。如果套接字超时,就抛出一个SocketTimeoutException异常。
	public void setSoTimeout(int timeout) throws SocketTimeoutException.:设置套接字的超时值。这个超时值决定了receive()方法阻塞的微秒数。

               	DatagramPacket类
DatagramSocket类的send()和receive()方法都带有一个DatagramPacket参数。DatagramPacket类代表一个数据报包,与DatagramSocket类似,包的发送者和接受者都要使用它。DatagramPacket有六个构造器。其中,两个是用于接受者,四个用于发送者。
如下的两个DatagramPacket构造器用于接收数据报包:
	public DatagramPacket(byte [] buffer, int length):创建一个数据报包,用于接收指定大小的包。buffer包含了要接收的包。
	public DatagramPacket(byte [] buffer, int offset, int length):与上一个构造器相同,除了要接收的包的数据存在用offset参数指定的字节数字位置上。
传递给这些构造器的字节数组用于存储要接收的包的数据,通常是空数组。如果它们不会空,那么要接收的数据报包将覆盖数组中的数据。
如下的四个构造器用于发送一个数据报包:
	public DatagramPacket(byte [] buffer, int length, InetAddress address, int port):创建一个数据报包,用于发送指定大小的包。buffer用于存储包数据,address和port描述接收者。
	public DatagramPacket(byte [] buffer, int length, SocketAddress address):与上一个构造器相似,但是接收者的名字和端口号存储在SocketAddress参数中。
	public DatagramPacket(byte [] buffer, int offset, int length, InetAddress address, int port):创建一个数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
	public DatagramPacket(byte [] buffer, int offset, int length, SocketAddress address):与前一个构造器类似,但是接收者的名字和端口号包含在SocketAddress参数中。
注意六个构造器都带有一个字节数组做参数。当接收包时,该数组开始是空,然后被接收的数据报包填充。当发送包时,字节数组存储要发送的包的数据。
DatagramPacket类包含了数据报包不同属性的get和set方法:
	public byte [] getData():返回数据缓冲区。
	public void setData(byte [] buffer):设置包数据。
	public int getLength():返回将要发送或接收到的数据的长度。
	public void setLength(int length):设置将要发送或者接收到的数据的长度。
	public SocketAddress getSocketAddress():返回消息要发送到的或者发出此消息的远程主机的地址。
	public void setSocketAddress(SocketAddress address):设置消息要发送到的或者发出消息的远程主机的地址。

          **URL类与URLConnection类**
本章迄今为止,我们已经讨论了在Java中如何使用套接字和数据报包来创建网络应用。在本节中,我们将学习如何编写与URL资源进行通讯的Java程序。URL(Uniform Resource Locator)代表统一资源定位,代表万维网上的一个资源,例如一个网页或者一个FTP目录。
一个URL实际上是一类URI(Uniform Resource Identifier,统一资源标识符)。URI标识一个资源,但是不包括如何访问该资源的信息。URL标识一个资源以及访问该资源的协议。URI在java中使用java.net.URI类代表。
一个URL可以分为如下几个部分:
协议://主机:端口/路径?查询字符串#锚点引用

这里,路径也称为文件名,主机也称为授权。协议包含HTTP、HTTPS、FTP和File。
java.net.URL类代表一个URL。URL类有如下几个构造器用于创建URL:
	public URL(String protocol, String host, int port, String file) throws MalformedURLException:根据指定 protocol、host、port 号和 file 创建 URL 对象。
	public URL(String protocol, String host, String file) throws  MalformedURLException:与前一构造器相同,但是用的是指定协议的默认端口。
	public URL(String url) throws MalformedURLException:根据给定的字符串创建URL对象。
URL类中有很多方法用于访问URL的不同部分,常用的方法如下:
	public String getPath():获取此URL的路径部分。
	public String getQuery():获取此 URL 的查询部分。
	public String getAuthority():获取此 URL 的授权部分。
	public int getPort():获取此 URL 的端口号。
	public int getDefaultPort():获取与此 URL 关联协议的默认端口号。
	public String getProtocol():获取此 URL 的协议名称。
	public String getHost():获取此 URL 的主机名。
	public String getFile():获取此 URL 的文件名。
	public String getRef():获取此 URL 的锚点。
	public URLConnection openConnection() throws IOException:打开一个到该URL的连接,允许客户端与该资源进行通讯。

使用URL类的openConnection()方法,可以连接到一个URL,并与资源进行通讯。openConnection()方法返回一个java.net.URLConnection,URLConnection是一个抽象类,其子类代表不同类型的URLConnection。例如,如果我们连接到一个HTTP协议的URL,那么openConnection()方法就返回一个HttpURLConnection对象。如果连接到一个代表JAR额外年间的URL,那么openConnection()方法就返回一个JarURLConnection对象。
URLConnection类有多个用于设置或者判断连接信息的方法,包括:
	public void setDoInput(boolean input):将此 URLConnection 的 doInput 字段的值设置为指定的值。URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输入,则将 DoInput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 true,因为客户端通常是从URLConnection中读取。
	public void setDoOutput(boolean output):将此 URLConnection 的 doOutput 字段的值设置为指定的值。如果打算使用 URL 连接进行输出,则将 DoOutput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 false,因为很多URL类型不支持写入数据。
	public InputStream getInputStream() throws IOException:返回从资源读取的URLConnection的输入流。
	public OutputStream getOutputStream() throws IOException:返回写入到此连接的输出流。返回写入到资源的URLConnection的输出流。
	public URLgetURL():返回此URLConnection对象要连接到的URL。
URLConnection类还包含了一些访问连接头信息的方法,让我们可以判断URL内容的类型和长度、最后更改的日子、内容编码等等。更多的方法,请参考JDK文档中URLConnection的详细说明。

你可能感兴趣的:(Java网络编程)