580 likes | 722 Views
第 11 章 网络编程. 教学目的要求 1. 了解网络编程的基本知识; 2. 掌握使用 URL 获取网络资源的方法; 3. 熟练掌握基于 TCP 的网络通信技术。. TCP/IP 协议概述. 在编写网络通信的 Java 程序时,是在 系统的 应用层编写程序的,所以根本不必关心 网络层中的 TCP/IP 的层次,只要用 java.net 包中的类即可,这些类提供了系统无关的网络通信服务。但是即使如此,也需了解一下 TCP/IP 协议。
E N D
第11章 网络编程 教学目的要求 1.了解网络编程的基本知识; 2.掌握使用URL获取网络资源的方法; 3.熟练掌握基于TCP的网络通信技术。
TCP/IP协议概述 • 在编写网络通信的Java程序时,是在系统的应用层编写程序的,所以根本不必关心网络层中的TCP/IP的层次,只要用java.net包中的类即可,这些类提供了系统无关的网络通信服务。但是即使如此,也需了解一下TCP/IP协议。 • 确切地说,TCP/IP协议是一组包括TCP协议和IP协议、UDP(User Datagram Protocol)协议、ICMP(Internet Control Message Protocol)协议和其他一些协议的协议族。TCP/IP通信协议采用了4层的层级结构,每一层都使用它的下一层所提供的网络来完成自己的需求。这4层分别为:
应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。 • 传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。 • 互连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。 • 网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据。
下面简单介绍TCP/IP中的几个主要协议的功能。 1.IP协议 • 网际协议IP是TCP/IP的心脏,也是网络层中最重要的协议。 • IP层接收由更低层(网络接口层例如以太网设备驱动程序)发来的数据包,并把该数据包发送到更高层——TCP或UDP层;相反,IP层也把从TCP或UDP层接收来的数据包传送到更低层。IP数据包是不可靠的,因为IP并没有做任何事情来确认数据包是按顺序发送的或者没有被破坏。IP数据包中含有发送它的主机的地址(源地址)和接收它的主机的地址(目的地址)。
2.TCP协议 • TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。 • 如果IP数据包中有已经封好的TCP数据包,那么IP将把它们向“上”传送到TCP层。TCP将包排序并进行错误检查,同时实现虚电路间的连接。TCP数据包中包括序号和确认,所以未按照顺序收到的包可以被排序,而损坏的包可以被重传。
TCP是可靠的、面向连接的、连续的、流的协议。当应用程序需要一个可靠的、点对点的连接进行通信时,就用TCP协议。像HTTP、FTP、Telnet等应用程序都需要一个可靠的通信媒介。数据在网络上发送和接收的次序对于能否成功的应用HTTP读取URL资源是很重要的,数据的接收次序与发送的次序必须一样,否则就会收到一些错误的信息。TCP是可靠的、面向连接的、连续的、流的协议。当应用程序需要一个可靠的、点对点的连接进行通信时,就用TCP协议。像HTTP、FTP、Telnet等应用程序都需要一个可靠的通信媒介。数据在网络上发送和接收的次序对于能否成功的应用HTTP读取URL资源是很重要的,数据的接收次序与发送的次序必须一样,否则就会收到一些错误的信息。 • 对于许多应用程序来说,这种可靠性的保证是能否成功传输信息的关键。然而,这种可靠的传输方式并非对所有的应用程序都适合。事实上,TCP需要花费大量的时间和带宽来保证可靠传输,有一些场合可靠传输并不是必须的。
3.UDP协议 • UDP是User Datagram Protocol(用户数据报协议)的简称,是一种无连接的、不可靠的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。但是这个协议的速度却比较快,所以在现在网络基础设施越来越好的情况下,使用UDP协议的应用程序也越来越多了。 • 在Java的java.net类库中,URL、URLConnection、Socket、SocketServer类都是利用TCP在网络上通信的;而DatagramPacket和DatagramServer类是使用UDP的。本章将主要讲述利用TCP协议进行通信的各个类。
Socket套接字 • 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个双向链路的一端称为一个Socket,即Socket是面向客户/服务器模型设计的,通常用Socket来实现客户方和服务方的连接。客户程序可以向Socket写请求,服务器将处理此请求,然后通过Socket将结果返回给用户。 • Socket通信机制提供了两种通信方式:有连接和无连接方式,分别面向不同的应用需求。使用有连接方式时,通信链路提供了可靠的,全双工的字节流服务。在该方式下,通信双方必须创建一个连接过程并建立一条通信链路,以后的网络通信操作完全在这一对进程之间进行,通信完毕则关闭此连接过程。使用无连接方式时其系统开销比无连接方式小,但通信链路提供了不可靠的数据报服务,不能保证信源所传输的数据一定能够到达信宿。在该方式下,通信双方不必创建一个连接过程和建立一条通信链路,网络通信操作在不同的主机和进程之间转发进行。
端口 • 计算机与网络一般只有一个单独的物理连接,所有的数据最终都要通过连接到达一个指定的计算机。然而,数据是传向运行在计算机上的不同应用程序的,计算机需要知道数据传向哪个应用程序,这就要用到端口。 • 数据在网络上传送是与地址信息的传送一起进行的,地址信息表明了数据要传向的计算机和端口。计算机是一个32位的IP地址标识,IP用来把数据传送到网络上的目的计算机上。端口是用一个16位的数来表示,TCP和UDP都是通过这个端口来指明数据要发往的应用程序。
在面向连接的通信中,一个应用程序与另一个应用程序的连接的建立是通过把一个端口号绑定在套接字上实现的。这样就把应用程序注册在操作系统中,从指定的端口接收所有的数据。运行在同一台计算机上的两个应用程序不可能绑定在同一个端口上,如果应用程序试图绑定在一个已经在使用中的端口上将导致失败。在面向消息的通信中,数据报包含了目的地的端口号。TCP和UDP协议是利用端口把流入的数据映射到运行的计算机上的特定进程。在面向连接的通信中,一个应用程序与另一个应用程序的连接的建立是通过把一个端口号绑定在套接字上实现的。这样就把应用程序注册在操作系统中,从指定的端口接收所有的数据。运行在同一台计算机上的两个应用程序不可能绑定在同一个端口上,如果应用程序试图绑定在一个已经在使用中的端口上将导致失败。在面向消息的通信中,数据报包含了目的地的端口号。TCP和UDP协议是利用端口把流入的数据映射到运行的计算机上的特定进程。 • 端口号是从0到65535(因为端口是用16位二进制数表示),其中0~1023的端口号被系统保留,它们被许多知名的服务占据,如HTTP、FTP和其他的系统服务,应用程序不能绑定这些端口。
URL • URL(Uniform Resource Locator)是统一资源定位符的简称,它表明了Internet上某一资源的地址。通过URL,可以访问Internet和WWW。浏览器通过解析给定的URL可以在网络上查找相应的文件或网络资源。IP地址定位了在互连网上的一台计算机,端口定义了为这台计算机上提供的服务。一个URL的语法格式如下: <protocol>://<hostname:port>/dir/filename • protocol定义了传输协议,如http、ftp、gopher、file等等;hostname为主机域名或IP地址;port为服务端口号;dir和filename分别为服务器上的目录和文件名。URL具有强大的功能,它提供了统一的方法来存储与单个类中的一个特定资源进行通信所需的全部信息;完成所有socket的创建、握手协议以及通过HTTP检索资源所需的解释程序。
Java语言提供了类java.net.URL来实现利用URL进行网络编程。该类具有4个构造器及其相应的方法。利用构造器创建了URL对象后,Java提供了2种方式来读取网络数据。Java语言提供了类java.net.URL来实现利用URL进行网络编程。该类具有4个构造器及其相应的方法。利用构造器创建了URL对象后,Java提供了2种方式来读取网络数据。 • 一种是通过URL对象及相关方法直接得到相关的网络信息,首先创建一个URL对象,然后使用URL的方法openStream()与指定的URL建立连接并得到一个输入流InputStream类的对象,在此基础上可进一步生成其它的输入流对象实例,进而对信息进行操作;这种方法简单直接,但缺乏灵活性,并且只能够读信息,因为许多Web提供了双向对话的功能,因而就限制了这种方法的应用。 • 另一种是通过方法openConnection()生成相应的URLConnection对象,从而得到InputStream输入流或InputStream对象,进而对其进行读写操作。这种方法应用相当广泛,如向服务器的CGI程序发送一些数据,首先必须与URL建立连接,然后对其进行读写操作,利用URLConnection类提供的公用方法除了可以简单地访问网络资源外,还可以访问某些协议提供的有关资源,如资源的MIME类型(Multimedia Internet Mail Extensions,多用途Internet邮件扩展)、资源的更改时间等。
11.2 使用URL获取网络资源 URL类 • 在Java的java.net包中,包含一些用于网络编程的类,其中URL类可以很方便地利用URL地址在Internet上进行网络通信并获取网络资源。 • URL对象是只写一次的对象,一旦创建了一个URL对象,就不能再改变它的任何属性(协议、主机名、文件名和端口号)。为了表示URL,java.net中实现了类URL。可以通过下面的构造方法来初始化一个URL对象:
构造方法一:public URL (String spec); • 该构造方法通过一个表示URL地址的字符串可以构造一个URL对象。例如: URL urlBase=new URL("http://www.163.com/") ; 构造方法二:public URL(URL context, String spec); • 该构造方法通过绝对URL和相对URL构造一个URL对象。例如: URL com163 = new URL ("http://www.163.com/"); URL index163 = new URL(com163, "index.html"); 构造方法三:public URL(String protocol, String host, String file); 例如: URL com163 = new URL("http", "www.163.com", "/pages/net. html");
构造方法四:public URL(String protocol, String host, int port, String file); • 例如: URL gamelan = new URL("http", "www.gamelan.com", 80, "Pages/net.html"); 注意:类URL的构造方法都声明抛弃非运行时异常(MalformedURLException),因此生成URL对象时,必须要对这一异常进行处理,通常是用try-catch语句进行捕获。格式如下: try{ URL myURL= new URL(…)}catch (MalformedURLException e){ …… }
URL类提供了多个方法进行URL对象的查询,可以使用这些方法来获取URL的属性信息,其中常用的方法有:URL类提供了多个方法进行URL对象的查询,可以使用这些方法来获取URL的属性信息,其中常用的方法有: getProtocol():返回URL的协议标识部分; getHost():返回URL的主机名部分; getPort():将端口号作为整数返回,如没有设置则返回-1; getPath():返回该URL的路径; getFile():返回URL的文件名部分; getRef():返回URL的引用部分。 • 当然,并不是所有的URL地址都包括这些组成部分。URL类提供这些方法是因为HTTP的URL包括这些部分,而HTTP的URL差不多是最通用的URL。从某种程序上讲,URL类是以HTTP为中心的。无论使用什么构造函数来创建URL对象,都可以使用这些方法来获取该URL对象的信息。例11.1描述了URL类的基本用法。
〖例11.1〗 URL类的基本用法举例。 //Li11_01.java import java.net.URL; //引入URL类 import java.net.MalformedURLException; //引入java.net包中的异常类 class Li11_01{ public static void main(String args[]) { URL sampleURL = null; try { sampleURL = new URL("http://www.163.com:80/index.html#down"); //创建URL对象 }catch(MalformedURLException e) { //异常处理 e.printStackTrace(); }
//显示sampleURL对象的各属性值 System.out.println("协议: "+sampleURL.getProtocol()); System.out.println("主机名:"+sampleURL.getHost()); System.out.println("端口号:"+sampleURL.getPort()); System.out.println("文件名:"+sampleURL.getFile()); System.out.println("锚点: "+sampleURL.getRef()); } } • 这是一个Java应用程序。运行结果输出URL地址的各属性值,因为URL没有参考点,输出为null。运行结果为: 协议: http 主机名:www.163.com 端口号:80 文件名:/index.html 锚点: down
获取图像 • Java Applet可以直接从网络上结点获取图像并显示出来。 为了了解其编程方法和从本地显示图像的编程有何不同,先不考虑网络功能,来看一个以前学习过的简单的图像显示的例子: • 〖例11.2〗 获取并显示图像举例。 import java.applet.*; import java.awt.*; public class Li11_02 extends Applet{ Image image; public void init() { image=getImage(getDocumentBase(),"swan.gif"); }
public void paint(Graphics g) { g.drawImage(image, 0, 0,this); } } • 这是一个获取并显示图像的简单例子,在该例中,先用getImage()方法从HTML文档所在位置调用图像swan.gif,并由此生成一个Image类型的对象image,然后用drawImage(image,0,0,this)在屏幕上将图像显示出来。 • 如果想从网络上其他结点获取图像,关键是创建对应于网络上其他结点的Image类型的对象,一旦获得Image类型的对象,便可以对其进行任何可能的图像操作。
Java提供了getImage(new URL(字符串))方法可以创建对应于其他结点的图像,其使用格式可有两种: String url = "结点URL"; Image image; try { image = getImage(new URL(url)); }catch(Exception e){ System.out.println("不能打开这个URL地址 "); } 或 URL imgur = null; Image image; try { imgur = new URL("结点URL "); }catch (MalformedURLException e) { System.out.println("不能打开这个URL地址 "); } image=getImage(imgur);
前一种格式用“new URL(url)”生成URL对象,并直接作为getImage的参数,后一种格式先用“new URL(url)”生成一个 URL对象,再传给getImage。两种格式本质上是一样的。两种格式中,生成URL对象的部分都包含在 try{//获取URL对象 }catch (MalformedURLException e) { //出错提示} 之中。 • 例如要调用http://xzsd.8866.org/java/automain.jpg结点的图像,第一种格式的完整实现见例11.3源程序。第二种格式的完整实现见例11.4的源代码。说明:例11.3至11.8编译后需要把.class文件和相应的.htm文件放入站点中的相应虚拟目录才可显示出效果。对于没有服务器或不能上网的读者,也可以在自己的计算机上通过安装微软的IIS或PWS(Windows98用户)建立一台WEB服务器进行测试,服务器建立后,把上面的程序放入WEB服务器相应目录中,然后把例题中的域名换成http://127.0.0.1即可。
〖例11.3〗 显示网络结点上的图像。 //Li11_03.java import java.applet.*; import java.net.*; import java.awt.*; public class Li11_03 extends Applet{ Image image; public void init() { String url="http://xzsd.8866.org/java/automain.jpg";//请按实际情况更改站点的域名 try { image = getImage(new URL(url)); } catch(Exception e){} } public void paint(Graphics g) { g.drawImage(image, 0, 0,this); }}
〖例11.4〗 显示网络结点上的图像。 //Li11_04.java import java.applet.*; import java.net.*; import java.awt.*; public class Li11_04 extends Applet{ Image image; URL imgur=null; public void init() { try { imgur=new URL("http://xzsd.8866.org/java/automain.jpg");//请按实际情况更改站点的域名 } catch (MalformedURLException e) { System.out.println("不能打开该URL地址。"); } image=getImage(imgur); } public void paint(Graphics g) { g.drawImage(image, 0, 0,this); }}
将上述两个程序分别以Li11_03.java和Li11_04. java存盘并把URL地址更改为自己服务器的图像文件的地址,执行javac Li11_03.java和javac Li11_04.java,将得到编译后生成的Li11_03.class和Li11_04.class,最后创建调用这两个Java Applet的HTML文档。 • 将.class和.htm程序放入Web服务器(xzsd.8866.org)并用IE打开,便可以看到Java Applet 所显示的从网络上获得的图像了。不过,在Applet程序中一般很少采用绝对URL,因为出于安全性的考虑,浏览器只允许Applet访问与Applet同一主机的资源。如果采用绝对URL,则当Applet放到其他的Web服务器上运行时,必须修改程序。
获取声音 • Java从网络上获取声音文件并播放声音的编程方法有两种,第一种是利用Java提供的play(URL1)及play(URL1,String1) 直接播放网络上的声音文件,另一种是通过getAudioClip(URL)或getAudioClip(URL, String)先从网络上获取声音文件,并生成AudioClip 类型的对象,然后对该对象进行操作。第一种方法实现可以用如下代码: String Audur = "结点URL"; try { play(new URL(Audur)); //或play(new URL(Audur),声音文件名); } catch(Exception e){ }
第二种方法实现可以用如下代码: String Audur = "结点URL"; AudioClip loopClip; try { loopClip = getAudioClip(new URL(Audur)); //或loopClip = getAudioClip(new URL(Audur) ,声音文件名); } catch(Exception e){ System.out.println("不能打开这个URL地址。"); }
上面的两种格式都是将生成URL对象部分——“new URL(url)”直接作为play或getAudioClip的参数;和前面处理图像的例子一样,也可以先用“new URL(url)”获取一个URL对象,再传给 play 或getAudioClip。如对第一种play(URL)的格式,也可采用如下的编程格式: URL Audur =null; try { Audur=new URL("结点URL "); } catch(Exception e){ System.out.println("不能打开该URL地址。"); } play(Audur);
〖例11.5〗 获取并播放网络结点上的声音文件。 //Li11_05.java import java.applet.*; import java.awt.*; import java.net.*; public class Li11_05 extends Applet { AudioClip loopClip; public void paint(Graphics g) { String Audur = "http://xzsd.8866.org/test.au"; //请根据实际情况更改站点的域名 //或String Audur = "http://xzsd.8866.rg/"; try { play(new URL(Audur)); //或play(new URL(Audur),"test.au"); } catch(Exception e){} }}
〖例11.6〗 获取并播放网络结点上的声音文件。 import java.applet.*; import java.awt.*; import java.net.*; public class Li11_06 extends Applet{ AudioClip loopClip; public void init() { String Audur = "http://xzsd.8866.org/java/test.wav";//请根据实际情况更改站点域名 //或String Audur = "http://xzsd.8866.org/java/"; try { loopClip = getAudioClip(new URL(Audur)); //或loopClip = getAudioClip(new URL(Audur),"test.wav"); } catch(Exception e){} }//init() public void paint(Graphics g){ loopClip.loop(); }}
获取文档 • 利用Java提供的getAppletContext().showDoc ument()方法可以显示Web服务器结点的HTML文档,在调用这个方法之前首先需要指定文档的网络资源的URL。其两种格式如下: void showDocument(URL url); void showDocument(URL url, String target); • 第一种格式在当前窗口中打开指定的网页文件,第二种格式在指定的窗口中打开指定的网页文件。同前面的显示网络上其他结点的资源一样,获取文档也有两种编程方式,例11.7和例11.8分别演示了showDocument()方法的使用。
〖例11.7〗 获取并显示网络结点上的文档资源。 import java.applet.*; import java.awt.*; import java.net.*; public class Li11_07 extends Applet{ URL docur= null; public void paint(Graphics g) { try { docur=new URL("http://xzsd.8866.org/java/index.htm"); //请根据实际情况更改站点域名 } catch (MalformedURLException e) { System.out.println("不能打开该URL地址。"); } if (docur != null) { getAppletContext().showDocument(docur); } } }
〖例11.8〗 获取并显示网络结点上的文档资源。 import java.applet.*; import java.awt.*; import java.net.*; public class Li11_08 extends Applet{ URL docur= null; public void paint(Graphics g) { try { getAppletContext().showDocument(new URL("http://xzsd.8866.org/java/index.htm")); }catch (MalformedURLException e) { System.out.println("不能打开该URL地址。"); } }//paint() }
上面的两个程序只是利用showDocment()方法显示网上的HTML文档,但并不能对其内容进行处理。实际上,Java还可以读取网络上文件的内容,并对其内容进行处理。上面的两个程序只是利用showDocment()方法显示网上的HTML文档,但并不能对其内容进行处理。实际上,Java还可以读取网络上文件的内容,并对其内容进行处理。 • Java读取并处理网络上文件内容的步骤如下: (1)创建一个URL类型的对象。例如: URL fileurl = new URL(url); (2)利用URL类的openStream( )方法获得对应的InputStream类的对象。例如: InputStream filecon = fileurl.openStream( );
(3)将InputStream对象转化为DataInput Stream类的对象。例如: DataInputStream filedata = new DataInputStream(filecon); (4)读取内容并处理。例如,对上面的filedata,可用filedata.readLine一行一行读取内容或用filedata.readchar一个字符一个字符地读取,读取后可使用java语句对其进行处理并输出。 • 对于读取并处理网上HTML文件内容的程序,请读者根据以上说明步骤,自己编写一个小程序。
11.3 使用TCP协议的Socket编程 Socket套接字 • 对于超出了URL类所提供的连网应用程序,Java提供了套接口类(Socket)和服务套接口类(ServerSocket)作为标准的TCP套接口编程技术,通过它们可以实现主机与主机之间的对话。 • 使用Socket进行Client/Server程序设计的一般连接过程是这样的:Server端Listen(监听)某个端口是否有连接请求,Client端向Server端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息,一个连接就建立起来了。Server端和Client端都可以通过套接口类提供的一些方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤: 1.创建Socket; 2.打开连接到Socket的输入/输出流; 3.按照一定的协议对Socket进行读/写操作; 4.关闭Socket。 • Java在java.net包中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端创建、管理通信。这是两个封装得非常好的类,使用很方便。其构造方法如下:
Socket(); • Socket(InetAddress address, int port); • Socket(InetAddress address, int port, boolean stream); • Socket(String host, int prot); • Socket(String host, int prot, boolean stream); • Socket(SocketImpl impl); • Socket(String host, int port, InetAddress localAddr, int localPort); • Socket(InetAddress address, int port, InetAddress localAddr, int localPort); • ServerSocket(); • ServerSocket(int port); • ServerSocket(int port, int backlog); • ServerSocket(int port, int backlog, InetAddress bindAddr);
其中address、host和port分别是双向连接中一方的IP地址、主机名和端口号,stream指明socket是流socket还是数据报socket,localPort表示本地主机的端口号,localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可以用来创建Socket。例如:其中address、host和port分别是双向连接中一方的IP地址、主机名和端口号,stream指明socket是流socket还是数据报socket,localPort表示本地主机的端口号,localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可以用来创建Socket。例如: Socket client = new Socket("127.0.01.", 8088); ServerSocket server = new ServerSocket(8088);
注意:在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以在选择端口号时,最好选择一个大于1023的数以防止发生冲突。 • 在创建socket时如果发生错误,将产生IOException,在程序中必须对之做出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。 • 在使用Socket和ServerSocket类构造完对象后,就可以使用它们提供的方法来实现端口的套接等功能。其中Socket类提供的主要方法有以下几种:
getInputStream():返回此套接口链接地址的输入流。getInputStream():返回此套接口链接地址的输入流。 • getInetAddress():返回此套接口链接的地址对象。 • getLocalAddress():返回套接口本地的地址对象。 • getLocalPort():返回套接口本地的地址端口。 • getOutputStream():返回此套接口链接地址的输出流。 • getPort():返回此套接口链接地址的端口。 • setSoTimeOut():设置套接口的有效期限值。 • toString():返回套接口字符串。 • close():关闭套接口。 • isClosed():判断此套接口是否关闭。
isConnected():判断此套接口是否断开。 • shutdownInput():关闭套接口的输入流。 • shutdownOutput():关闭套接口的输出流。 • ServerSocket类提供的主要方法有以下几种: • accept():返回与服务器端建立连接的机器套接口。 • getChannel():返回服务套接口通道。 • getInetAddress():返回本机的地址对象。 • getLocalPort():返回服务器所绑定的监听端口号。 • getSoTimeout():返回服务套接口限制的时间数。 • setSoTimeout(int timeout):设定服务套接口所需等到的时间限度。 • toString():返回服务套接口字符串。 • close():关闭服务套接口。 • isClosed():判断服务套接口是否关闭。
一对一的Socket C/S通信 • TCP是一种可靠的、基于连接的网络协议,在Internet上大都使用TCP/IP协议进行互联。网络上的两进程采用C/S模式进行通信,当两台主机准备进行交谈时,都必须建立一个Socket,其中一方作为服务器打开一个Socket并侦听来自网络的连接请求,另一方作为客户,它向网络上的服务器发送请求,通过Socket与服务器传递信息,要建立连接,只需指定主机的IP 地址和端口号即可。图11.1描述基于连接的服务端、客户端流程图。
图11.1是一个典型的面向连接的Socket通信机制的示意图。它首先由服务器方建立Socket并将该Socket联编到某个端口上,并进入监听状态,同时监听是否有与自己端口相对应的连接请求。连接是由客户方发出的,客户方在建立自己的Socket后,向服务器发出连接请求,服务器在检测到连接后接受连接,这样就建立起了一个完整的Socket连接。图11.1是一个典型的面向连接的Socket通信机制的示意图。它首先由服务器方建立Socket并将该Socket联编到某个端口上,并进入监听状态,同时监听是否有与自己端口相对应的连接请求。连接是由客户方发出的,客户方在建立自己的Socket后,向服务器发出连接请求,服务器在检测到连接后接受连接,这样就建立起了一个完整的Socket连接。 • 从图11.1可以看出,服务器方必须首先启动,然后守侯在某一个端口上监听客户方的连接请求,一旦连接建立,就可以像普通流机制那样进行读写,只需调用close()即可结束Socket连接。通过该方式建立起来的C/S程序即可实现一台服务器端和一台客户端的通信。
TCP协议通信的实现 • 利用Java的Socket编程方式,实现一对一的C/S通信是十分简单的。下面给出一个用Socket实现的客户和服务器交互的典型的C/S结构的演示程序,读者通过仔细阅读该程序,会对前面所讨论的各个概念有更深刻的认识。 • 〖例11.9〗 实现客户端与服务器端一对一的聊天程序。 //Li11_09C.java,以下为客户端程序 import java.io.*; import java.net.*;
public class Li11_09C { public static void main(String args[]) { try{ Socket socket=new Socket("127.0.0.1",4700); //向本机的4700端口发出客户请求 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); //由系统标准输入设备构造BufferedReader对象 PrintWriter os=new PrintWriter(socket.getOutputStream()); //由Socket对象得到输出流,并构造PrintWriter对象 BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); //由Socket对象得到输入流,并构造相应的BufferedReader对象 String readline; readline=sin.readLine(); //从系统标准输入读入一字符串
while(!readline.equals("bye")){ //若从标准输入读入的字符串为 "bye"则停止循环 os.println(readline); //将从系统标准输入读入的字符串输出到Server os.flush(); //刷新输出流,使Server马上收到该字符串 System.out.println("Client:"+readline); //在系统标准输出上打印读入的字符串 System.out.println("Server:"+is.readLine()); //从Server读入一字符串,并打印到标准输出上 readline=sin.readLine(); //从系统标准输入读入一字符串 } //继续循环 os.close(); //关闭Socket输出流 is.close(); //关闭Socket输入流 socket.close(); //关闭Socket }catch(Exception e) { System.out.println("Error"+e); //出错,则打印出错信息 } } }//Li11_09C.java,客户端程序结束
//Li11_09S.java,以下为服务器端程序 import java.io.*; import java.net.*; import java.applet.Applet; public class Li11_09S{ public static void main(String args[]) { try{ ServerSocket server=null; try{ server=new ServerSocket(4700);//创建一个ServerSocket在端口4700监听客户请求 }catch(Exception e) { System.out.println("can not listen t"+e); //在屏幕上显示出错信息 } Socket socket=null;
try{ socket=server.accept();//使用accept()阻塞等待客户请求,有客户 //请求到来则产生一个Socket对象,并继续执行 }catch(Exception e) { System.out.println("Error."+e); } String line; BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); //由Socket对象得到输入流,并构造相应的BufferedReader对象 PrintWriter os=new PrintWriter(socket.getOutputStream()); //由Socket对象得到输出流,并构造PrintWriter对象 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); //由系统标准输入设备构造BufferedReader对象 System.out.println("Client:"+is.readLine());//在标准输出上打印从客户端读入的字符串 line=sin.readLine(); //从标准输入读入一字符串