第14章 Java中的网络编程 教学提示:学习并使用Java提供的网络接口来编写网络应用程序。内容包括Java与
Internet的关系、2P类、URL类、Socket类和UDP通信类。其中,Socket套接字用来建立
网络上两台不同计算机上运行的程序间的一个通信通道,是最基本的网络编程方法。
教学目标:理解TCP/IP和UDP协议,学会使用ServerSocket和Socket建立server和client应用,使用DatagramPacket和DatagramSocket建立UDP;了解在网络上建立连接及对网上资源的访问方法;掌握Java的Socket通信机制、网络资源访问、Java的数据报通信方式。
14.1 Java与网络
Java语言已成为网络应用软件开发的主要工具。使用Java语言进行网络连接编程比C++语言要容易得多。Java提供了许多内置的网络功能,使开发基于Internet和Web的应用程序更容易。
Java最初是作为一种网络编程语言出现的,它能够使用网络上的各种资源和数据,与
服务器建立各种传输通道,将自己的数据传送到网络上。
实现网络功能要靠URL类、URLConection类、Socket类和DatagramSocket类。
网络上的数据传送是将网络连接转换成输入输出流。
DataInputStream和DataOutputStream (PrintStream)类是网间数据流的载体。
URL类适用于Web应用,如访问HTTP服务器属于应用层服务。
URL Connection类提供比URL类更强的服务器交互控制。
IP地址(127.0.0.1)可用于在本地机器上调试网络程序。
Socket类适用于面向连接的,可靠性要求高的应用。
Datagram类适用于效率要求高的应用。
Socket是由IP和端口构成的一种网上通信链路的一端。
Socket通信要分别运行服务器和客户程序。
服务器程序是多线程的,可处理多个客户的请求。
14.1.1 网络
网络编程涉及客户与服务器两个方面及它们之间的联系。客户端请求服务器执行某个操作,服务器执行这个操作并对客户端作出响应。在网页浏览器与http服务器之间,按照请求应答响应模式工作。当用户在浏览器中选定一个网站时,这个请求就发送到相应的网络服务器上。服务器发送相应的HTML网页来响应客户端。
第14章 Java中的网络编程 ·257·
1. 网络协议
1) Internet分层模型
TCP/IP是传输控制协议/网际协议的简称,它包含100多个不同功能的协议,是互联网上的通信规则。其中最主要的是TCP和IP协议。TCP/IP是一个四层的体系结构,它包含应用层、运输层、网际层和网络接口层。其体系结构如图14.1所示。
图14.1 TCP/IP体系结构
按照网络通信的不同层次,Java提供的网络功能有4大类:URLs、Sockets、Datagram 和InetAddress。应用层负责将网络传输的信息转换成我们能够识别的信息,包括很多面向应用的协议,如SMTP(简单邮件传输协议)、HTTP(超文本传输协议)等。在这一层,Java 使用URL、URLConnection类。通过URL类,Java程序可以直接发出或读取网络上的数据。
运输层提供端到端的通信,包括面向连接的TCP(传输控制协议)和无连接的UDP(用户数据包协议)。TCP协议提供了可靠的数据传输服务,具有流量控制、拥塞控制、按顺序递交等功能。UDP增加的服务是不可靠的,但其系统资源开销小,在流媒体系统中使用较多。TCP协议的相关类有Socket、ServerSocket;UDP协议的相关类有:DatagramPacket、DatagramSocket、MulticastSocket。Sockets使用的是TCP协议,这是传统网络程序最常用的方式,可以想像为两个不同的程序通过网络的通信信道进行通信。Datagram则使用UDP 协议,是另一种网络传输方式,它把数据的目的地记录在数据包中,然后直接放在网络上。
网际层中最主要的协议就是无连接的IP协议,它负责同一网络或不同网络中计算机之间的通信。在这层中,Java使用InetAddress类来表示IP地址。一般应用程序是靠TCP实现通信功能,因为它们需要通过端口进行数据的无差错传输。
2) TCP与UDP协议
TCP协议是在端点与端点之间建立持续的连接而进行通信。建立连接后,发送端将发送的数据以字节流的方式发送出去;接收端则对数据按序列顺序将数据整理好,数据在需要时可以重新发送。这与两个人打电话的情形是相似的。
TCP协议具有可靠性和有序性,并且以字节流的方式发送数据,是一种面向连接的协议。
与TCP协议不同,UDP协议是一种无连接的传输协议。利用UDP协议进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的端
·257·
Java 语言程序设计实用教程
·258·
·258·
点(Socket ,主机地址和端口号),然后再将数据报发送出去。这种传输方式是无序的,也不能确保绝对的安全可靠,但它很简单而且具有比较高的效率,这与通过邮局发送邮件的情形非常相似。
2. 建立网络连接
如何在网络上建立客户机与服务器之间的通信链路?
一台机器通常只通过一条链路连接到网络上,即它只有一个IP 地址,但一台机器中往往有很多应用程序需要进行网络通信,如何区分呢?这就要依靠网络端口号(port)。端口号是一个标记机器的逻辑通信信道的正整数,端口号不是物理实体。
TCP/IP 中的端口号是一个16位的数字,它的范围是0~65535。其中0~1023为系统所保留,专门给那些通用的服务(例如TELNET 、SMTP 和FTP 等),如HTTP 服务的端口号为80,TELNET 服务的端口号为21,FTP 服务的端口号为23。因此,编写通信程序时,应选择一个大于1023的数作为端口号,以免发生冲突。IP 地址与端口号组合可以完全分辨Internet 上某台计算机运行的某一程序。
客户和服务器必须事先约定所使用的端口。如果系统两部分所使用的端口不一致,则不能进行通信。客户端与服务器端建立连接的过程如图14.2所示:
图14.2 客户端与服务器端的连接过程
服务器端软件在远程计算机上连续不断地运行,监听各端口上的通信请求。当服务器上接收到一个对某一端口的连接请求时,就唤醒正在运行的服务器软件,建立连接。该连接将一直保持下去,直到通信的某一方将它中断。
3. Socket 机制
Socket(套接字)是网络上运行的两个程序之间双向通信链路的终端点,它是TCP 和UDP 的基础一个Socket 被绑定到一个端口号上,这样运输层就能识别数据要发送哪个应用 程序。
建立网络连接之后,使用与Socket 相关联的流和使用其他流非常相似。基于Socket 的通信通过Socket 读取和写入数据,使应用程序对网上数据的读写操作,像对本地文件的读写一样简单。Socket 允许程序把网络连接当成一个流,可以向这个流写字节,也可以从这个流读取字节。
当进程通过网络进行通信时,Java 技术使用它的流模型。一个Socket 包括两个流:Input Stream(输入流)和Output Stream(输出流)。如果一个进程要通过网络向另一个进程发送数据,只需简单地写入与Socket 相关联的输出流。一个进程通过从与Socket 相关联的输入流来读取另一个进程所写的数据。
Java 提供了Stream Sockets(流套接字)和Datagram Sockets(数据报套接字)。用Stream
第14章 Java 中的网络编程
·259·
·259·
Sockets 可以在两个进程之间建立一个连接,连接建立后,数据在相互连接的进程间流动。所以说Stream Sockets 提供面向连接的服务,它使用的是TCP 协议。
使用Datagram Sockets 传输的是一个个独立的数据包。它使用的UDP 协议是一种无连接服务,不能保证数据包按一定的顺序到达。在传输过程中,包可能丢失、重复发送和不按先后顺序到达。如果使用UDP 协议,用户需要想办法解决这些问题。所以UDP 协议适用于不需要错误检查和可靠性的网络应用。对于大多数编程者,Stream Sockets 和TCP 协议用得最多。
无连接服务一般性能更好,但可靠性较面向连接服务要差。TCP 协议及其相关协议使异构计算机系统之间能相互通信。
在Java 编程语言中,TCP/IP Socket 连接是用https://www.doczj.com/doc/2e9685153.html,
包中的类实现的。图14.3说明了Socket 连接机制。
图14.3 Socket 连接机制
Socket 工程过程为:
(1) 建立连接:服务器端程序分配一个端口号,开始监听来自客户端的请求。当客户请求一个连接时,服务器使用accept()方法打开Socket 连接,将该Socket 连接到此端口上。
(2) 数据通信:服务器和客户端使用Socket 的InputStream(输入流)和OutputStream(输出流)进行通信。
(3) 关闭连接:通信结束,服务器和客户端程序断开连接,释放所占用的系统资源。 14.1.2 Java 中的网络功能
Java 的网络功能由几个主要的包实现,基本的网络功能由https://www.doczj.com/doc/2e9685153.html, 包中的类和接口定义实现。Java 通过该包提供的基于套接字Socket 的通信,使应用程序能把网络当作一个数据流来使用。
Java 所提供的网络功能可大致分为4大类。
InetAddress :在网络层,用于标识网络上的硬件资源。
URL 和URLConnection 可表示和建立确定网络上资源的位置,Java 程序可以直接读入网络上的数据,或把自己的数据传送到网络的另一端。
Socket 是两个不同的程序之间在网络上传输数据的通道,这是网络程序中最基本的方法。一般在TCP/IP 协议下的客户服务器软件采用Socket 作为交互方式。
Java 语言程序设计实用教程
·260·
·260·
Datagram :是功能最低级的一种。其他网络数据传送方式,都假想在程序执行时,建立一条安全稳定的通道。但是以Datagram 方式传送数据时,只把数据目的地记录在数据包中,然后就直接放在网络上进行传输,系统不保证数据一定能够安全送到,也不能确定什么时候可以送到。
14.2 IP 与URL
连接到网络中的每台计算机(或其他设备)都有唯一的地址,这就是IP 地址。https://www.doczj.com/doc/2e9685153.html,.InetAddress 类是Java 的IP 地址封装类,它不需要用户了解如何实现对IP 地址操作的细节。
在互联网上,以URL(统一资源定位器)表示Internet 上各种数据资源的地址。为了处理方便,Java 将URL 封装成URL 类,我们可以用一个URL 对象记录下完整的URL 信息。 14.2.1 InetAddress 类
如果需要在主机名与Internet 地址之间进行转换,那么可以使用InetAddress 类。 InetAddress 类的定义如下:
public final class InetAddress extends object implements Serializable
该类中有两个字段:hostName(String)和address(int),即主机名和IP 地址。这两个字段是不公开的,不能直接访问它们。
1. 创建InetAddress 对象的方法
InetAddress 类没有构造方法,要创建该类的实例对象,可以通过该类的静态方法获得该对象。找不到本地机器的地址时,这些方法通常会抛出UnknownHostException 异常,应在程序中捕获处理。
1) public static InetAddress getLocalHost()
getLocalHost 方法用于获得本地机的lnetAddress 对象,查找不到本地机的地址时,抛出一个UnknownHostException 异常。
try {
InetAddress address=InetAddress.getLocalHost(); …;//其他处理代码 }
catch(UnknownException e){ …;//异常处理代码 }
2) public static InetAddress getByName(String host)
getByName(String host) 方法用于获得由host 指定的InetAddress 对象。参数host 可以是一个机器名,也可以是一个IP 地址或一个DSN 域名。
例如:
InetAddress address=InetAddress.getByName ("https://www.doczj.com/doc/2e9685153.html,");
第14章 Java中的网络编程 ·261·将返回一个InetAddress对象,封装了4个字节的序列。可以使用getAddress()方法访问这些字节,如
Byte[] addresses=InetAddress.getAddress ();
3) public static InetAddress[] getAllByName(String host)
有些主机名可以带有许多对应于多个Internet地址的信息,以利于实现负载平衡。如https://www.doczj.com/doc/2e9685153.html,对应3个不同的地址,在主机被访问时随机选择一个地址。GetAllByName用来获取所有的主机地址对象,并存放在一个地址对象数组中。
4) public static InetAddress getByAddress(Byte[] addr)
根据给定的IP字节地址创建一个InetAddress 对象。如果addr是IPv4地址IPv6,则返回一个Inet4Address对象,如果是IPv6地址,则返回一个Inet6Address对象。IPv4地址字节数组必须是4个字节,IPv6地址字节数组必须是16个字节。
获得一个InetAddress对象后,就可以使用InetAddress类的public byte[] getAddress()方法获得本对象的IP地址(存放在字节数组中);使用public String getHostAddress()方法获得本机对象的IP地址;使用public String getHostName()方法获得主机名;使用public String toString()方法得到主机名和IP地址的字符串,如主机名/点分地址。
2. 实例
1) 查询IP地址
// ==================== Program Discription ==================
// 程序名称:getIP.java
// 程序目的:熟悉getByname方法
// ======================================================
import https://www.doczj.com/doc/2e9685153.html,.*;
public class getIP
{
public static void main(String args[])
{
InetAddress sun = null;
try{
sun = InetAddress.getByName("https://www.doczj.com/doc/2e9685153.html,");
}catch(UnknownHostException e) {}
System.out.println(sun);
}
}
运行结果:
https://www.doczj.com/doc/2e9685153.html,/72.5.124.61
2) 查询IP地址的版本
// ==================== Program Discription ==================
// 程序名称: IPVersion.java
// 程序目的:查询IP的版本,熟悉InetAddress用法
// ======================================================
·261·
Java语言程序设计实用教程·262·
·262·import https://www.doczj.com/doc/2e9685153.html,.*;
public class IPVersion
{
public static void main(String args[])
{
try
{ InetAddress inetadd =
InetAddress.getByName("https://www.doczj.com/doc/2e9685153.html,");
// InetAddress inetadd=InetAddress.getLocalHost(); byte[ ] address=inetadd.getAddress( );
if (address.length==4)
{
System.out.println(" the ip version is ipv4");
byte firstbyte=address[0];
System.out.println("firstbyte of IP address:"+
Integer.toBinaryString(firstbyte) );
if((firstbyte&0x80)==0 )
System.out.println("the ip class is A");
else if ((firstbyte&0x40)==0 )
System.out.println("the ip class is B");
else if((firstbyte&0x20)==0 )
System.out.println("the ip class is C");
else if((firstbyte&0x10)==0 )
System.out.println("the ip class is D");
else
{ System.out.println("the ip class is E");
}
}
else if(address.length==16)
System.out.println("the ip version is ipv6");
}
catch (Exception e)
{ };
}
}
运行结果:
the ip version is ipv4
firstbyte of IP address:1001000
the ip class is A
14.2.2 URL和URLConnection
URL和URLConnection 类封装了大量从远程站点检索信息的复杂细节,支持对HTTP 和FTP资源的访问,例如:
URL url=new URL("www.openlab.ca")
URL url=new URL("ftp://username:password@openlab.ca")
第14章 Java中的网络编程 ·263·
1. URL类的构造方法
在https://www.doczj.com/doc/2e9685153.html,包中提供了类URL来表示URL。类URL提供了很多构造方法来生成一个URL对象:
public URL(String spec)
public URL(URL context, String spec)
该构造方法用以存在的URL对象context创建URL对象。示例如下:
try{
URL base=new
URL("https://www.doczj.com/doc/2e9685153.html,:80/HomePage/jiao_yu_zi_yuan/index.shtml");
URL lc=new URL(base,"#change");
}
catch(MalformedURLException e){
…
}
public URL(String protocol, String host, String file)
该构造方法用指定的协议、主机名、路径及文件名创建URL对象。
public URL(String protocol, String host, int port, String file)
该构造方法用指定的协议、主机名、端口号、文件路径及文件名创建一个URL对象。
其中,文件file变量必须以下划线开始。比如要表示URL:
https://www.doczj.com/doc/2e9685153.html,/downloads/index.html
URL url=new URL("http"," https://www.doczj.com/doc/2e9685153.html, ",80," downloads/index.html ");
以下是一些具体的构造示例:
URL url1 = new
URL("https://www.doczj.com/doc/2e9685153.html,/HomePage/zhong_guo_jiao_yu/index.shtml");
URL base = new URL("https://www.doczj.com/doc/2e9685153.html,/HomePage");
URL url2 = new URL(base, "/jiao_yu_zi_yuan/index.shtml");
URL url3 = new URL("http",
"https://www.doczj.com/doc/2e9685153.html,","/HomePage/jiao_yu_zi_yuan/index.shtml");
URL url4 = new URL("http", "https://www.doczj.com/doc/2e9685153.html,", 8080, "/java/network.html");
如果创建URL时发生错误,系统会产生异常MalformedURLException,这是非运行时异常,必须在程序中捕获处理。如示例:
URL url1,url2,url3;
try{
url1 = new URL("file:/D:/image/example.gif");
url2 = new URL("https://www.doczj.com/doc/2e9685153.html,/");
url3 = new URL(url2, "a_06.gif");
}catch(MalformedURLException e)
{
DisplayErrorMessage();
}
·263·
Java 语言程序设计实用教程
·264·
·264·
2. URL 类常用方法
一个URL 对象生成后,其属性是不能被改变的(与String 对象相似),但可以通过它给定的方法来获取这些属性。
public final Object getContent():这个方法取得传输协议。 public String getFile():这个方法取得资源的文件名。 public String getHost():这个方法取得机器的名称。 public int getPort():这个方法取得端口号。
public String getProtocol():这个方法取得传输协议。
public String toString():这个方法把URL 转化为字符串。
在Java 中可以通过URL 读取WWW 信息。通过URL 类提供的openStream()方法,就可以读取URL 对象所指定的资源。
public final InputStream openStream()
方法openStream()与指定的URL 建立连接并返回一个InputStream 对象,将URL 位置的资源转成一个输入数据流。通过这个InputStream 对象,就可以读取资源中的数据。使用URL 类获得服务器端的数据。
示例:
// ==================== Program Discription ========================== // 程序名称: Myurl.java
// 程序目的:熟悉URL 对象的创建、使用。
// ============================================================== import https://www.doczj.com/doc/2e9685153.html,. *; import java.io.*; class Myurl {
public static void main(String args[]) {
try {
URL url=new URL("https://www.doczj.com/doc/2e9685153.html,:80/downloads/index.htm"); System.out.println("the Protocol: "+url.getProtocol()); System.out.println("the hostname: " +url.getHost() ); System.out.println("the port: "+ url.getPort()); System.out.println("the file:"+url.getFile()); System.out.println(url.toString()); }
catch(MalformedURLException e) { System.out.println(e); } } }
运行结果:
the Protocol: http
the hostname: https://www.doczj.com/doc/2e9685153.html, the port: 80
the file:/downloads/index.htm
https://www.doczj.com/doc/2e9685153.html,:80/downloads/index.htm
第14章 Java中的网络编程 ·265·
3. 创建URLConnection 对象
通过URL类的openStream()方法,只能从网络上读取资源中的数据。通过URLConnection 类,可以在应用程序和URL资源之间进行交互,既可以从URL中读取数据,也可以向URL 中发送数据。URLConnection类表示了应用程序和URL资源之间的通信连接。
URLConnection是一个抽象类,创建URLConnection对象之前必须首先创建一个URL 对象,然后通过URL类提供的openConnection()方法,就可以获得一个URLConnection对象。通过URLConnection类,向URL中发送数据。
public URLConnection openConnection()
例如:
URL url=new URL("https://www.doczj.com/doc/2e9685153.html,");
URLConnection connection=url.openConnection();
4. URLConnection类的常用方法
URLConnection类不仅可以使用getInputStream()方法获得URL节点的信息,还可以使用getOutputStream()方法向URL节点传输数据,这样在本机与URL节点间形成一个遵循HTTP协议的数据流通道。
URLConnection类最常用的方法是:
public InputStream getInputStream()
public OutputStream getOutputStream()
通过getInputStream()方法,应用程序就可以读取资源中的数据。事实上,类URL的方法openStream()就是通过URLConnection类来实现的,它等价于:
openConnection().getInputStream();
示例:
// ==================== Program Discription =====================
// 程序名称: URLDemoTest .java
// 程序目的:URLConnection类的用法。
//=============================================================
import java.io.*;
import https://www.doczj.com/doc/2e9685153.html,.*;
import java.util.Date;
class URLDemoTest
{
public static void main(String args[]) throws Exception
{
System.out.println("starting...");
int c;
URL url=new URL("https://www.doczj.com/doc/2e9685153.html,");
URLConnection urlcon=url.openConnection();
System.out.println("the date is :"+new
Date(urlcon.getDate()));
System.out.println("content_type :"+urlcon.getContentType());
·265·
Java语言程序设计实用教程·266·
·266· InputStream in=urlcon.getInputStream(); while (((c=in.read())!=-1))
{
System.out.print((char)c);
}
in.close();
}
}
运行结果:
the date is :Mon Jul 17 13:01:08 CST 2006
content_type :text/html;charset=UTF-8
"https://www.doczj.com/doc/2e9685153.html,/TR/html4/loose.dtd">
……
14.3 TCP服务器/客户端Socket
在Java语言中,用Socket类处理用户的请求和响应。Socket为程序员屏蔽了网络的底层细节,如信息类型、信息包的大小和网络地址等。利用Socket类的方法,可以实现两台计算机之间的通信。
基于TCP协议的Socket类有两种,一种Socket在服务器端创建,称为服务器Socket(ServerSocket);一种Socket在客户端被创建,称为客户端Socket (Client Socket)。
1. ServerSocket的工作过程
(1) 用ServerSocket()方法在指定端口创建一个新的ServerSocket对象。
(2) ServerSocket对象调用accept()方法在指定的端口监听到来的连接。accept()一直处于阻塞状态,直到有客户端试图建立连接。这时accept()方法返回连接客户端与服务器的Socket对象。
(3) 调用getlnputStream()方法或者getOutputStream()方法或者两者全调用建立与客户端交互的输入流和输出流。具体情况要看服务器的类型而定。
(4) 服务器与客户端根据一定的协议交互,直到关闭连接。
(5) 服务器、客户机或者两者都关闭连接。
(6) 服务器回到第(2)步,继续监听下一次的连接。
2. 编程步骤
(1) 创建ServerSocket的一个实例和Socket实例。
ServerSocket ssocket=new ServerSocket(1222);//在服务器端指定端口创建一个
//ServerSocket
Socket line=ssocket.accept();//ServerSocket类的accept()方法使服务器处于阻
//塞状态,当接收到客户端Socket请求时与服务器端
// 建立监听关系
第14章 Java 中的网络编程
·267·
·267·
Socket service=new Socket("server",1222); //在客户端指定的端口号创建一个
//Socket 实例
(2) 通信。
OutputStream send=service.getOutputStream(); InputStream receive=service.getInputStream(); send.write( data); //写数据 receive.read(); //读数据
(3) 关闭Socket 。 服务器端关闭Socket 的方法:server.close ; 客户端:socket.close();
14.3.1 服务器端ServerSocket
在ServerSocket 类中包含了创建ServerSocket 对象的构造方法、在指定端口监听的方法、建立连接后发送和接收数据的方法。
构造方法如下:
public ServerSocket(int port)throws IOException BindException
public ServerSocket(int port,int queuelength)throws IOException BindException public ServerSocket(int port, int queuelength,InetAddress bindaddress)throws
IOException,BindException
这些构造方法允许指定端口,用来保存到来的连接请求队列的长度,绑定本地网络的地址。例如,如果想在端口1290创建一个服务器端口,同时使队列中所能存储的到来的请求数为100,代码如下:
try{
ServerSocket socket=new ServerSocket(1290,100); )
catch(IOException e){ //异常处理 }
ServerSocket 类的常用方法有以下几个。
public Socket accept()throws IOException public void close() throw s IOException public InetAddress getlnetAddress() public int getLocalPort()
服务器建立并准备接收连接时,调用ServerSocket 的accept()方法。执行该方法后,程序等待下一个客户端的连接。当客户端请求连接时,accept()方法返回一个Socket 对象,然后用这个Socket 对象的getInputStream()和getOutputStream()方法返回的流与客户端交互,示例:
ServerSocket server=new ServerSocket(); While(true) {
Socket connection=server .accept();
Java 语言程序设计实用教程
·268·
·268·
0utputStream out=new OutputStream(connection .getOutputStream()); out .write("the java networking"); connection .close();}
14.3.2 客户端Socket
客户端Socket 的构造函数如下:
public Socket(String host,int port)throws unknownHostException IOException
这个方法建立一个到主机host 、端口号为port 的套接字,连接到远程主机。示范代码如下:
try{
Socket soc=new Socket("https://www.doczj.com/doc/2e9685153.html,",80); //发送数据 }
catch(unknownHostException uex){ }
catch(IOException e){ }
public Socket(InetAddress host,int port)throws IOException
建立一个套接字,与前一个不同的是,它用InetAddress 对象指定套接字。如果出错则抛出IOException 异常。
public InetAddres s getlnetAddress()
调用Socket 对象的getlnetAddress()方法返回连接到远程主机的地址,如果连接失败,则返回以前连接的主机。示例如下:
try{
Socket socket=new Socket("https://www.doczj.com/doc/2e9685153.html,",80); InetAddress host=socket .getInetAddress(); //操作 }
catch(IOException e){ //异常处理 }
catch(unknownHostException e){ //异常处理 }
public int getPort()
返回Socket 连接的远程主机端口号。示范代码如下:
try{
Socket socket=new Socket("WWW .sun .com",80); int Port=socket .getPort(); //操作 }
catch(IOException e){ //异常处理 }
第14章 Java 中的网络编程
·269·
·269·
catch(unknownHostException e){ //异常处理 }
public int getLocalPort()
一个Socket 连接两个终端,方法getLocalPort()返回本地连接终端的端口号。示范代码如下:
try{
Socket socket=new Socket("https://www.doczj.com/doc/2e9685153.html,",80); int port=socket .getLocalPort(); //操作 }
catch(IOException e){ //异常处理 }
catch(unknownHostException e){ //异常处理 }
public InetAddress getLocalAddress()
此方法告诉用户套接字Socket 绑定到哪个网络接口。用户通常在多目录的主机或带有多目录的网络接口上使用这个方法。
public InputStream getInputStream()throws IOException
这个方法返回一个输入流,利用这个流就可以从套接字读取数据。通常连接这个流到一个BufferedInputStream 或者BufferedReader 。
public OutputStream getOutputStream() throws IOException
返回一个原始的Outputstream ,可以从应用程序写数据到套接字的另一端。通常将它连接到DataOutputStream 或者OutputStreamWriter 等更方便的类,还可以连接到缓冲类。
示例如下:
OutputStreamWriter out ; try{
Socket socket=new Socket("220,204.10.6",80); OutputStream outs=socket .getOutputStream();
BufferedOutputStream buffer=new BufferedOutputStream(outs); out=new OutputStreamWriter(buffer ,"ASCII"); out .write("the java networking"); }
catch(Exception e){…//异常处理 }
finally { try{
out .close(); }
catch(Exceptin e){…//异常处理 } }
Java 语言程序设计实用教程
·270·
·270·
public synchronized void close() throws IOException
虽然套接字会在程序结束时被自动关闭,但是应该用close()方法断开连接,特别是要运行无限长时间的程序时。关闭套接字的示范代码如下:
Socket socket=null; try{
socket=new Socket("https://www.doczj.com/doc/2e9685153.html,",80); //套接字操作 }
catch(IOException e){ //异常处理 }
catch(unknownHostException e){ //异常处理 }
finally{ if(socket!=null) Socket.close(); }
14.3.3 实例
下面进行客户机/服务器模式下的Socket 编程,包括一个服务器和一个客户机的程序例子。服务器端程序负责监听客户机请求,为每个客户机请求建立Socket 连接,从而为客户机提供服务。本程序提供的服务为:读取来自客户机的命令,根据客户机的命令,决定服务器要发给客户机的信息,并发送给客户机。
1. TCP/IP 服务器ServerSocket
// ==================== Program Discription ===================== // 程序名称: SimpleServer.Java
// 程序目的:TCP/IP 服务器应用程序依靠Java 技术语言提供的网络类。ServerSocket 类 // 完成建立一个服务器所需的大部分工作。在本机端口5432提供时间服务。
//============================================================= import java.io.*; import java.util.*; import https://www.doczj.com/doc/2e9685153.html,.*;
public class SimpleServer {
public static void main(String args[]) { ServerSocket s = null; Socket s1;
OutputStream s1out; DataOutputStream dos;
//String line="only one line."; try {
s = new ServerSocket(5432);// Register your service on port 5432 }
catch (IOException e) {}// Run the listen/accept loop forever while (true) { try {
第14章 Java中的网络编程 ·271· s1 = s.accept();// Wait here and listen for a connection
s1out = s1.getOutputStream();// Get a communication stream for
soocket
dos = new DataOutputStream(s1out);
dos.writeUTF(new Date().toString());// Send string!(UTF provides machine-independent format)
//dos.writeUTF(line);
s1out.close();// Close the connection, but not the server socket s1.close();
}
catch (IOException e) {}
}
}
}
2. 客户端Socket
// ==================== Program Discription =====================
// 程序名称: SimpleClient.Java
// 程序目的:客户端Socket完成了建立一个连接所需的大部分工作。客户连接到服务器上,显//示服务器发送的所有时间数据。通过Socket获得服务器提供的时间字符串,并显示。
//=============================================================
import https://www.doczj.com/doc/2e9685153.html,.*;
import java.io.*;
public class SimpleClient {
public static void main(String args[]) throws IOException {
Socket s1;
InputStream s1In;
DataInputStream dis;
String st;
s1 = new Socket("127.0.0.1",5432);
//Open your connection to sunbert, at port 5432
s1In = s1.getInputStream();
//Get an inputStream from the socket and read the input
dis = new DataInputStream(s1In);
st = new String (dis.readUTF());
System.out.println(st);// When done, just close the connection and exit
s1In.close();
s1.close();
}
}
运行结果:
Mon Jul 17 14:07:13 CST 2006
14.4 UDP服务器/客户端Datagram
TCP/IP是一种面向连接的协议,而用UDP是一种无连接的协议,它只是把数据的目的地记录在数据包中,然后直接放在网络上,不保证传送质量。
·271·
Java 语言程序设计实用教程
·272·
·272·
在https://www.doczj.com/doc/2e9685153.html, 包中有3个类支持基于UDP 协议的网络通信:表示数据包的DatagramSocket 类和用于端到端通信的DatagramPacket 类,以及用于广播通信的MulticastSocket 类。 14.4.1 DatagramPacket
数据包(Datagram)是网络层数据单元传输信息的一种逻辑分组格式,它是一种在网络中传播的、独立的、自身包含地址信息的消息。它的通信双方是不需要建立连接的,创建数据包的方式如下所述。
1. 将数据打包
https://www.doczj.com/doc/2e9685153.html, 包中的DatagramPacket 类用来创建数据包。数据包有两种,一种用来传递数据包,该数据包有要传递到的目的地址;另一种数据包用来接收传递过来的数据包中的数据。分别用DatagramPacket 的两个构造函数来创建。
DatagramPacket(byte[] recvBuf, int readLength):用来建立一个字节数组以接收UDP 包。byte 数组在传递给构造函数时是空的,而int 值用来设置要读取的字节数(不能比数组的大小还大)。 例如:
byte[]buffer :new byte[8912];
DatagramPacket datap=new DatagramPacket(buffer ,buffer .1ength());
DatagramPacket(byte[] sendBuf, int sendLength, InetAddress iaddr, int iport):用来建立将要传输的UDP 包。sendLength 不应该比sendBuf 字节数组的大小还大。
如果数据包长度超出了length ,则触发IllegalArgument Exception 。不过这时RuntimeException 不需要用户代码捕获。
示例如下:
String s=new String("java network"); Byte[] data=s.getbytes(); int port=1024; try{
InetAddress ineta=InetAddress.getByName("202.204.220.10")
DatagramPacket datap=new DataPacket(data,data.length(),ineta,port); }
catch(IOException e){ //异常处理 }
2. 使用数据包
通过接收数据包对象,获取数据包中的信息的方法有以下几个。
public InetAddress getAddress()
如果是发送数据包,则获得数据包要发送的目标地址,但是如果是接收数据包则返回发送此数据包的源地址。
public byte[]getData()
返回一个字节数组,其中是数据包的数据。如果想把字节数组转换成其他类型就要进行转化。
第14章 Java 中的网络编程
·273·
·273·
public int getLength()
获得数据包中数据的字节数。
pubic int getPort()
返回数据包中的目标地址的主机端口号。 通过发送数据包对象,设置发送数据包中的信息的方法有:setAddress(InetAddress iaddr)、setPort(int iport)、setData(byte[] buf)、setData(byte[] buf, int offset, int length)、setLength(int length)。详细资料可查Java 文档。
14.4.2 DatagramSocket
发送和接收数据包还需要发送和接收数据包的Socket ,即DatagramSocket 对象。 DatagramSocket 用来读写UDP 包。它在本地机器端口监听是否有数据到达或者将数据包发送出去。DatagramSocket 类有3个构造函数,允许指定要绑定的端口号和Internet 地址。
public DatagramSocket()
用本地机上任何一个可用的端口创建一个套接字,这个端口号是由系统随机产生的。使用方法如下:
try{
DatagramSocket datas=new DatagramSocket(); //发送数据包 }
catch(SocketException e){ //异常处理 }
这种构造方法没有指定端口号,可以用在客户端。如果构造不成功则触发 SocketException 异常。
public DatagramSocket(int port)
用一个指定的端口号port 创建一个套接字。当不能创建套接字时就抛出SocketException 异常,其原因是指定的端口已被占用或者是试图连接低于1024的端口,但是又没有权限。
DatagramSocket(int port, InetAddress iaddr)
绑定指定地址的指定端口。 14.4.3 实例
下面是基于UDP 的服务器/客户端通信程序。 1. 创建UDP 服务器
// ==================== Program Discription ======================= // 程序名称:UDPServer.Java
// 程序目的: UDP 服务器在8000端口监听客户的请求。当它从客户接收到一个 // DatagramPacket 时,它发送服务器上的当前时间。
//================================================================= import java.io.*; import https://www.doczj.com/doc/2e9685153.html,.*; import java.util.*;
Java语言程序设计实用教程·274·
·274·public class UDPServer{
//This method retrieves the current time on the server
public byte[] getTime(){
Date d= new Date();
return d.toString().getBytes();
}
// Main server loop.
public void go() throws IOException {
DatagramSocket datagramSocket;
DatagramPacket inDataPacket; // Datagram packet from the client DatagramPacket outDataPacket; // Datagram packet to the client InetAddress clientAddress; // Client return address
int clientPort; // Client return port
byte[] msg= new byte[10]; // Incoming data buffer. Ignored.
byte[] time; // Stores retrieved time
// Allocate a socket to man port 8000 for requests.
datagramSocket = new DatagramSocket(8000);
System.out.println("!!!!!!at UDPServer,datagramSocket is: " + datagramSocket.getPort() + "local is: " + datagramSocket.getLocalPort()); System.out.println("UDP server active on port 8000");
// Loop forever
while(true) {
// Set up receiver packet. Data will be ignored.
inDataPacket = new DatagramPacket(msg, msg.length);
// Get the message.
datagramSocket.receive(inDataPacket);
// Retrieve return address information, including InetAddress // and port from the datagram packet just recieved.
clientAddress = inDataPacket.getAddress();
clientPort = inDataPacket.getPort();
// Get the current time.
time = getTime();
//set up a datagram to be sent to the client using the
//current time, the client address and port
outDataPacket = new DatagramPacket
(time, time.length, clientAddress, clientPort);
//finally send the packet
datagramSocket.send(outDataPacket);
}
}
public static void main(String args[]) {
UDPServer udpServer = new UDPServer();
try {
udpServer.go();
} catch (IOException e) {
第14章 Java中的网络编程 ·275·
System.out.println ("IOException occured with socket.");
System.out.println (e);
System.exit(1);
}
}
}
运行结果:
!!!!!!at UDPServer,datagramSocket is: -1local is: 8000
UDP server active on port 8000
2. UDP客户端
// ==================== Program Discription ======================= // 程序名称:UDPClient.java
// 程序目的: UDP客户向前面创建的客户发送一个空包并接收一个包含服务器实际时间的包。
// ================================================================ import java.io.*;
import https://www.doczj.com/doc/2e9685153.html,.*;
public class UDPClient {
public void go() throws IOException, UnknownHostException {
DatagramSocket datagramSocket;
DatagramPacket outDataPacket; // Datagram packet to the server DatagramPacket inDataPacket; // Datagram packet from the server InetAddress serverAddress; // Server host address
byte[] msg = new byte[100]; // Buffer space.
String receivedMsg; // Received message in String form.
// Allocate a socket by which messages are sent and received.
datagramSocket = new DatagramSocket();
System.out.println("!!!!!!at UDPClient,datagramSocket is: " + datagramSocket.getPort() + "local port is: "
+datagramSocket.getLocalPort());
// Server is running on this same machine for this example.
// This method can throw an UnknownHostException.
serverAddress = InetAddress.getLocalHost();
// Set up a datagram request to be sent to the server.
// Send to port 8000.
outDataPacket = new DatagramPacket(msg, 1, serverAddress, 8000);
// Make the request to the server.
datagramSocket.send(outDataPacket);
// Set up a datagram packet to receive server's response.
inDataPacket = new DatagramPacket(msg, msg.length);
// Receive the time data from the server
datagramSocket.receive(inDataPacket);
// Print the data received from the server
·275·