Java网络编程(java.net )

news/2024/7/7 9:55:16 标签: 网络, java, 编程, socket, 服务器, string

 http://java.ccidnet.com/images/java/javanet/
Java网络编程java.net )
事实上网络编程简单的理解就是两台计算机相互通讯数据而已,Java SDK

提供一些相对简单的Api来完成这些工作。Socket就是其中之一,这些Api

存在与java.net 这个包里面,因此只要导入这个包就可以准备网络编程

Java网络编程的概述
   网络编程的目的就是指直接或间接地通过网络协议与其他计算机进

行通讯。网络编程中有两个主要的问题,一个是如何准确的定位网络上一

台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输……

Java网络编程API的使用
   Java 开发网络软件非常方便和强大, Java 有一套强大的用于网络

API ,这些 API 是一系列的类和接口,均位于包 java.net 和 javax.net 中


套接字 (Socket) 慨念:
 Network API是典型的用于基于TCP/IP网络Java程序与其他程序通讯,

Network API依靠Socket进行通讯。Socket可以看成在两个程序进行通讯连

接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信

息发送给另外一个Socket中,使这段信息能传送到其他程序中。

*同时以实例说明如何使用 Network API 操纵套接字,在完成本文后,可

以编写网络低端通讯软件。


本章主要介绍如下内容:
一 Java网络类和接口
    Java中有关网络方面的功能都定义在java.net程序包中。Java所提供的

网络功能可大致分为三大类:
URL和URLConnection 这是三大类功能中最高级的一种。通过URL的网络

资源表达方式,很容易确定网络上数据的位置。利用URL的表示和建立,

Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络

另一端。  
Socket 所谓Socket,可以想像成两个不同的程序通过网络的通道,而这是

传统网络程序中最常用的方法。一般在TCP/IP网络协议下的客户服务器

件采用Socket作为交互的方式。  
Datagram 是这些功能中最低级的一种。其他网络数据传送方式,都假想

在程序执行时,建立一条安全稳定的通道。但是以Datagram的方式传送

数据时,只是把数据的目的地记录在数据包中,然后就直接放在网络上进

行传输,系统不保证数据一定能够安全送到,也不能确定什么时候可以送

到。也就是说,Datagram不能保证传送质量。
 二 。InetAddress类
java.net.InetAddress类是Java的IP地址封装类。
两个字段:hostName(String)和address(int),即主机名和IP地址。这两个

字段是不公开的。
    下面我们介绍InetAddress类提供的Internet地址的操作。
1 创建InetAddress对象的方法
方法一:
try {
    InetAddress address=InetAddress.getLocalHost( );
//  获得本地机的InetAddress对象,当查找不到本地机器的地址时,触发

一个UnknownHostException异常。
}
catch(UnknownException e) {
}
方法二:
try {
    InetAddress  address=InetAddress.getByName( host );
//host是计算机的域名,其作用跟IP地址一样
}
catch(UnknownException e) {
}
 
方法三:
try {
    InetAddress  address=InetAddress.getAllByName( host );
   //获得具有相同名字的一组InetAddress对象。Web中,可以用相同的名

字代表一组计算机。Internet上不允许多台计算机共用一个名字(或者说

是IP地址)
}
catch(UnknownException e) {
}
    2 实例:查询IP地址的版本
    InteAddress类有一个getAddress()方法,该方法将IP地址以网络字节顺

序作为字节数组返回。当前IP只有4个字节,但是当实行IPV6时,就有16

个字节了。如果需要知道数组的长度,可以用数组的length字段。使用

getAddress( )方法的一般性用法如下所示:

InetAddress inetaddress=InetAddress.getLocalHost( );
byte[ ] address=inetaddress.getAddress( );
 

    我们要注意的是返回的byte[ ]字节是无符号的。但是Java没有无符号字

节的基本数据类型,因此如果要对返回的字节操作时,必须要将int做适当

的调整。下面的方法就实现了这个目的:
    int unsignbyte = signbyte < 0 ? signbyte + 256 : signbyte;

    如果signbyte是负数,就加256使其成为正数,否则就保持不变。
    下面的程序示例12-1可以查询IP地址是IPV4还是IPV6,还能查询IP的类

别。
    【程序源代码】
package test;
import java.net.*;
import java.io.*;
public class IPVersion {
 public static void main(String args[]) {
  try {
   InetAddress inetadd =

InetAddress.getLocalHost();
   byte[] address = inetadd.getAddress();
   if (address.length == 4) {
    System.out.println("The ip version

is ipv4");
    int firstbyte = address[0];
    if (firstbyte < 0)
     firstbyte += 256;
    int firstbyte2 = firstbyte & 0x80;
    int firstbyte3 = firstbyte & 0xC0;
    int firstbyte4 = firstbyte & 0xE0;
    int firstbyte5 = firstbyte & 0xF0;
    if ((firstbyte & 0x80) == 0)
     System.out.println("the

ip class is A");
    else if ((firstbyte & 0xC0) == 0x80)
     System.out.println("The

ip class is B");
    else if ((firstbyte & 0xE0) ==

0xC0)
     System.out.println("The

ip class is C");
    else if ((firstbyte & 0xF0) == 0xE0)
     System.out.println("The

ip class is D");
    else if ((firstbyte & 0xF8) == 0xF0)
     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
The ip class is B
 表12-1判定可以得到B、C、D、E类地址。
表12-1  判断网络类型的位串
类 高  位  串
A 0……
B 10……
C 110……
D 1110……
E 11110……

    下面我们再介绍InetAddress类的另外两个方法。
String localname= inetadd.getHostName( );
public String toString()
    toSring()方法得到主机名和IP地址的字符串,其具体形式如下:主机名

/点分地址
package test;
import java.net.*;
import java.io.*;
class Internet {
 public static void main(String args[]) {
  try {
   InetAddress inetadd;
   inetadd = InetAddress.getLocalHost();
   System.out.println
   ("hostname=" + inetadd.getHostName());
   System.out.println(inetadd.toString());
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

hostname=pp
pp/169.254.36.105

三URL和URLConnection类

    URL是Uniform Resource Locator(统一资源定位器)的缩写。在WWW风行后,以URL表示Internet上各种数据资源的位置,已经成为一种标准的方式。为了处理方便,Java将URL封装成URL类, 我们可以用一个URL对象记录下完整的URL信息。

    URL类为我们提供了不同的构造方法:
   
public URL(String spec)
    这个构造方法用指定的URL来创建一个URL对象,比如:

try {
URL rul=new URL("http://www.sina.com.cn/***/***.html");
catch(MalformedURLException e){
……
}
 


   
public URL(String protocol,String host,int port,String file) 该构造方法用指定的协议、主机名、端口号、文件路径及文件名创建一个URL对象,记住file变量必须以下划线开始。比如我们要表示URL:http://www.ntu.edu.cn:80/local/searchresult.html
    则可以这样表示:

try {
URL url=new URL("http","www.ntu.edu.cn",80,"/local/searchresult.html");
}
catch(MalformedURLException e) {
……
}
 


   
public URL(String protocol, String host, String file)
    该构造方法用指定的协议、主机名、路径及文件名创建URL对象。
   
public URL( URL contoxt, String spec)
    该构造方法用已存在的URL对象context创建URL对象。示例如下:

try{
URL base=new URL("http://www.strange.com.cn:80/x-file/1112.html");
URL loc=new  URL(base , "#change");
}
catch(MalformedURLException e) {
}
 


    URL类中一些很基本的方法如下:
   
public final Obect getContent() 这个方法取得传输协议。
   
public String getFile() 这个方法取得资源的文件名。
   
public String getHost() 这个方法取得机器的名称。
   
public int getPort() 这个方法取得端口号。
   
public String getProtocol() 这个方法取得传输协议。
   
public String toString() 这个方法把URL转化为字符串。

    1 实例:URL对象的创建及使用

    下面我们举例介绍URL对象的创建及使用,见示例12-3。
    【程序源代码】

1 // ==================== Program Description ==========================
2 // 程序名称:示例12-3: Myurl.java
3 // 程序目的:熟悉URL对象的创建及使用
4 // ==============================================================
5 import java.net. *;
6 import java.io.*;

8 class Myurl
9 {
10    public static void main(String args[])
11    {
12       try {
13          URL url=new URL("http://www.tsinghua.edu.cn/chn/index.htm");
14          System.out.println("the Protocol: "+url.getProtocol());
15          System.out.println("the hostname: " +url.getHost());
16          System.out.println("the port: "+url.getPort());
17          System.out.println("the file:"+url.getFile());
18          System.out.println(url.toString());
19       }
20       catch(MalformedURLException e) {
21          System.out.println(e);
22       }
23    }
24 }
 


    【程序输出结果】

the Protocol: http
the hostname: www.tsinghua.edu.cn
the port: -1
the file:/chn/index.htm
http://www.tsinghua.edu.cn/chn/index.htm
 


    【程序注解】

    在第13行先实例化一个URL对象url,然后在第14~18行分别调用了URL对象的一些基本方法:getProtocol()、getHost()、getPort()和getFile(),分别取得了与URL相应的协议、主机名、端口和文件。如果URL地址不对,在第21行抛出MalformedURLException异常。

    URLConnection是一个抽象类,代表与URL指定的数据源的动态连接,URLConnection类提供比URL类更强的服务器交互控制。URLConnection允许用POST或PUT和其他HTTP请求方法将数据送回服务器。在java.net包中只有抽象的URLConnection类,其中的许多方法和字段与单个构造器一样是受保护的,这些方法只可以被URLConnection类及其子类访问。

    使用URLConnection 对象的一般方法如下:
    (1)创建一个URL对象。


    (2)调用URL对象的openConnection()方法创建这个URL的URLConnection对象。
    (3)配置URLConnection。
    (4)读首部字段。
    (5)获取输入流并读数据。
    (6)获取输出流并写数据。
    (7)关闭连接。

    当然我们并不需要完成所有这些步骤。比如我们可以接受URL类的默认设置,则可以不设置URLConnection;还有我们有时仅仅需要从服务器读取数据,并不需要向服务器发送数据,则我们就可以省去获取输出流并写数据这一步。

    当创建URLConnection对象后,我们可以使用URLConnection对象的操作方法:
   
public int getContentLength() 获得文件的长度。
   
public String getContentType() 获得文件的类型。
   
public long getDate() 获得文件创建的时间。
   
public long getLastModified() 获得文件最后修改的时间。
   
public InputStream getInputStream() 获得输入流,以便读取文件的数据。

    如果URL类的构造函数的参数有问题,比如字符内容不符合URL位置表示法的规定、指定的传输协议不是Java所能接受时,那么构造函数就会抛出MalformedURLException异常,这时一定要用try 和catch语句处理。

    2 实例:使用URLConnection从Web服务器读取文件

    接下来,我们用上面的方法从Web服务器上读取文件的信息,将文件的信息打印到屏幕。见示例12-4。
    【程序源代码】

1 // ==================== Program Description =====================
2 // 程序名称:示例12-4: URLDemo .java
3 // 程序目的:URLConnection类的用法
4 //=============================================================
5 import java.io.*;
6 import java.net.*;
7 import java.util.Date;

9 class  URLDemo
10 {
11  public static void  main(String args[]) throws Exception
12   { 
13       System.out.println("starting....");
14    int c;
15    URL url=new URL("http://www.sina.com.cn");
16    URLConnection  urlcon=url.openConnection();
17    System.out.println("the date is :"+new Date(urlcon.getDate()));
18    System.out.println("content_type :"+urlcon.getContentType());
19    InputStream in=rulcon.getInputStream();
20    while (((c=in.read())!=-1))
21    {
22     System.out.print((char)c);
23    }
24    in.close();
25   }
26  }
 


    【程序输出结果】

starting....
the date is :Thu Mar 27 00:00:13 CST 2003
content_type :text/html
………………//网页文件内容
//-->
</body>
 


    【程序注解】

    在第15行中我们实例化了一个URL,接着就通过调用URL对象的openConnection()方法返回一个URLConnection类的对象urlcon。然后就分别调用了URLConnectin类的常用方法,返回了URL的一些基本信息。这些方法是:getDate()返回日期,getContentType()返回文件类型text/html,getInputStream()获得输入流,然后通过输入流取得文件(in.read()),并在标准输出上输出(System.out.println()),我们看到的是一个网页源代码文件。在IE中我们可以查看源代码文件,其实就是通过这种方法实现的。
四 TCP/IP服务器与客户端套接字 Socket

    套接字(Socket)是由伯克利大学首创的。它允许程序把网络连接当成一个流,可以向这个流写字节,也可以从这个流读取字节。套接字为程序员屏蔽了网络的底层细节,例如媒体类型、信息包的大小、网络地址、信息的重发等。

    Socket是网络上运行的两个程序间双向通信的一端,它既可以接受请求,也可以发送请求,利用它可以较为方便地编写网络上数据的传递。在Java中,有专门的Socket类处理用户的请求和响应。利用Socket类的方法,就可以实现两台计算机之间的通信。

    套接字能执行7种基本操作:
   
连接到远程机器。
   
绑定到端口。
   
接收从远程机器来的绑定端口上的连接。
   
监听到达的数据。
   
发送数据。
   
接收数据。
   
关闭连接。

    java.net.Socket类是Java的基础类,用于执行客户端的TCP操作。套接字有两种:一种套接字在服务器端创建,叫做服务器套接字(ServerSocket);还有一种在客户端被创建,就是客户端套接字。

    1 客户端套接字

    1.1 构造函数
    1.public Socket(String host ,int port) throws unknownHostException IOException

    这个方法建立一个到主机host、端口号为port的套接字,连接到远程主机。
    示范代码如下:

try {
   Socket soc=new Socket ("www.sdfd.net" , 80);
   //发送数据
}
catch(unknownHostException uex) {
}
catch(IOException e) {
}
 


    2.public Socket (InetAddress host ,int port ) throws IOException
    建立一个套接字,与前一个不同的是它用InetAddress对象指定套接字。如果出错则抛出IOException异常。

    1.2 常用的方法
    1.public InetAddress getInetAddress ( )

    调用Socket对象的getInetAddress ( )方法返回连接到远程主机的地址,如果连接失败,则返回以前连接的主机。实例如下:

try {
   Socket socket = new Socket ("www.snm.com" ,80);
   InetAddress host =socket.getInetAddress ( );
   //操作
}
catch( IOException e) {
//异常处理
}
catch (unknownHostException e) {
//异常处理
}
 


    2.public int getPort ( )
    返回Socket连接到远程主机的端口号。示范代码如下:

try {
   Socket socket =new Socket ("www.snm.com",80);
  int port =socket.getPort ( );
   //操作
}
catch( IOException e) {
//异常处理
}
catch (unknownHostException e) {
   //异常处理
}
 


    3.public int getLocalPort ( )
    一个Socket连接两个终端,方法getLocalPort( )返回本地连接终端的端口号。示范代码如下:

try {
   Socket socket =new Socket ("www.snm.com" ,80);
   int port =socket.getLocalPort ( );
   //操作
}
catch( IOException e) {
   //异常处理
}
catch (unknownHostException e) {
   //异常处理
}
 


    4.public InetAddress getLocalAddress ( )
    此方法告诉用户套接字绑定到哪个网络接口。用户通常在多目录的主机或带有多目录的网络接口上使用这个方法。

    5.public InputStream getInputStream( ) throws IOException
    这个方法返回一个输入流,利用这个流就可以从套接字读取数据。通常链接这个流到一个BufferedInputStream或者BufferedReader。

    6.public OutputStream getOutputStream ( ) throws IOException
    返回一个原始的OutputStream,可以从应用程序写数据到套接字的另一端。通常将它链接到DataOutputStream或者OutputStreamWriter等更方便的类,还可以利用缓冲。示范代码如下:

OutputStreamWriter  out;
try {
Socket socket = new  Socket ("www.sdhn.net",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) {
}
}
 


    7.public synchronized void close( ) throws IOException
    虽然套接字会在两个数据流之一被关闭、程序结束时会被自动地关闭,但是我们应该用close()方法断开连接,特别是要运行无限长时间的程序时。关闭套接字的示范代码如下:

Socket  socket=null;
try {
   socket=new Socket("www.sdhj.com",80 );
//套接字操作
}
catch( IOException e) {
//异常处理
}
catch (unknownHostException e) {
   //异常处理
}
finally {
if (socket!=null)
socket.close ( );
}
 


2 服务器套接字

    每个服务器套接字运行在服务器上特定的端口,监听在这个端口的TCP连接。当远程客户端的Socket试图与服务器指定端口建立连接时,服务器被激活,判定客户程序的连接,并打开两个主机之间固有的连接。一旦客户端与服务器建立了连接,则两者之间就可以传送数据,而数据是通过这个固有的套接字传递的。

    在ServerSocket类中包含了创建ServerSocket对象的构造方法、在指定端口监听的方法、建立连接后发送和接收数据的方法。

    ServerSocket的工作过程如下:
    (1)用ServerSocket()方法在指定端口创建一个新的ServerSocket对象。
    (2)ServerSocket对象调用accept( )方法在指定的端口监听到来的连接。accept( )一直处于阻塞状态,直到有客户端试图建立连接。这时accept( )方法返回连接客户端与服务器的Socket对象。
    (3)调用getInputStream( )方法或者getOutputStream( )方法或者两者全调用建立与客户端交互的输入流和输出流。具体情况要看服务器的类型而定。
    (4)服务器与客户端根据一定的协议交互,直到关闭连接。
    (5)服务器、客户机或者两者都关闭连接。
    (6)服务器回到第2步,继续监听下一次的连接。

    下面我们介绍一下与客户端套接字不同的服务器套接字的方法。
    构造方法如下:
    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

    这些构造方法允许指定端口,用来保存到来连接请求队列的长度,绑定本地网络的地址。如果想在端口5300创建一个服务器端口,同时使队列中所能存储的到来的请求数为100,则示范代码如下:

try {
ServerSocket   socket=new ServerSocket(5300 ,100);
}
catch(IOException e) {
//异常处理
}
 


    常用方法如下:

   
public Socket accept ( ) throws IOException
    服务器建立并准备接收连接时,调用ServerSocket的 accept( )方法。这个方法是阻塞的:它停止执行流等待下一个客户端的连接。当客户端请求连接时,accept( )方法返回一个Socket对象。然后就用这个Socket对象的getInputStream( )和getOutputStream( )方法返回的流与客户端交互。示范代码如下:

ServerSocket  server=new ServerSocket( );
While (true)
{
 Socket  connection =server.accept ( );
OutputStream  out=new OutputStream (connection.getOutputStream( ));
 out.write("the java networking" );
   connection.close( );
}
 


   
public void close ( ) throws IOException 实现了服务器套接字后要关闭它。
   
public InetAddress getInetAddress( )
   
public int getLocalPort ( )

    3 实例:C/S环境下的套接字应用程序

    下面我们介绍客户/服务器环境下的套接字应用程序。

    客户/服务器在分布处理过程中,使用基于连接的网络通信模式。该通信模式首先在客户机和服务器之间定义一套通信协议,并创建一个Socket类,利用这个类建立一条可靠的连接;然后,客户/服务器再在这条连接上可靠地传输数据。客户机发出请求,服务器监听来自客户机的请求,并为客户机提供响应服务。这就是典型的"请求-应答"模式。下面是客户/服务器的一个典型运作过程:
    (1)服务器监听相应端口的输入。
    (2)客户机发出一个请求。
    (3)服务器接收到此请求。
    (4)服务器处理这个请求,并把结果返回给客户机。
    (5)重复上述过程,直至完成一次会话过程。

    按照以上过程,我们使用Java语言编写一个分别针对服务器和客户机的应用程序(Application)。服务器程序负责监听客户机请求,为每个客户机请求建立Socket连接,从而为客户机提供服务。本程序提供的服务为:读取来自客户机的命令,根据客户机的命令,决定服务器要发给客户机的信息,并发送给客户机,见示例12-5。
    【程序源代码】

1 // ==================== Program Description ========================
2 // 程序名称:示例12-5: KKMultiServerThread.java
3 // 程序目的:创建server端线程类
4 //=============================================================
5 import java.net.*;
6 import java.io.*;

8 public class KKMultiServerThread extends Thread
9 {
10     private Socket socket = null;
11 
12     public KKMultiServerThread(Socket socket) {
13        super("KKMultiServerThread");
14        this.socket = socket;
15     }
16 
17     public void run()
18     {
19        try {
20          PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
21          BufferedReader in = new BufferedReader( new InputStreamReader(
22                 socket.getInputStream()));
23         String inputLine, outputLine;
24          KnockKnockProtocol kkp = new KnockKnockProtocol();
25          outputLine = kkp.processInput(null);
26          out.println(outputLine);
27 
28          while ((inputLine = in.readLine()) != null) 
29         {
30         outputLine = kkp.processInput(inputLine);
31         out.println(outputLine);
32            if (outputLine.equals("Bye"))
33             break;
34          }
35        out.close();
36        in.close();
37        socket.close();
38        } catch (IOException e) {
39         e.printStackTrace();
40        }
41     }
42 }
 


    【程序注解】
    类KKMultiServerThread继承自线程类,有一个Socket成员变量socket,在构造函数中初始化了socket变量。下面看看如何实现线程的run()方法。

    首先定义了PrintWriter out和BufferedReader in,使输入输出流与socket相关联。然后声明了KnockKnockProtocol对象 kkp,读取客户端的命令,根据kkp中定义的协议,决定要发送给客户的信息,并发送出去。最后调用out.close()、in.close()和socket.close()关闭输入输出流和套接字。

    示例12-6是多线程服务器程序。
    【程序源代码】

1 // ==================== Program Description =====================
2 // 程序名称:示例12-6: KKMultiServer.java
3 // 程序目的:多线程服务器
4 //=========================================================
5 import java.net.*;
6 import java.io.*;

8 public class KKMultiServer
9 {
10     public static void main(String[] args) throws IOException
11     {
12         ServerSocket serverSocket = null;
13         boolean listening = true;
14 
15         try {
16             serverSocket = new ServerSocket(4444);
17         } catch (IOException e) {
18             System.err.println("Could not listen on port: 4444.");
19             System.exit(-1);
20         }
21 
22         while (listening)
23             new KKMultiServerThread(serverSocket.accept()).start();
24 
25         serverSocket.close();
26     }
27 }
 


    【程序注解】
    第12行声明了一个ServerSocket对象serverSocket。第16行实例化了serverSocket,监听端口为4444。然后就是监听客户端的连接(使用serverSocket.accept()方法)。如果有客户端的连接,则serverSocket.accept()就返回一个Socket套接字,以这个套接字实例化一个KKMultiServerThread对象,实际处理客户端的数据。

    下面的程序示例12-7实现了服务器端与客户端交互的协议。
    【程序源代码】

1 // ==================== Program Description =====================
2 // 程序名称:示例12-7: KnockKnockProtocol.java
3 // 程序目的:服务端与客户端相互交互的协议
4 //=============================================================
5 import java.net.*;
6 import java.io.*;

8 public class KnockKnockProtocol
9 {
10     private static final int WAITING = 0;
11     private static final int SENTKNOCKKNOCK = 1;
12     private static final int SENTCLUE = 2;
13     private static final int ANOTHER = 3;
14 
15     private static final int NUMJOKES = 5;
 
16 
17     private int state = WAITING;
18     private int currentJoke = 0;
19 
20     private String[] clues = { "Turnip", "Little Old Lady",
"Atch", "Who", "Who" };
21     private String[] answers = { "Turnip the heat, it's cold in here!",
22                            "I didn't know you could yodel!",
23                            "Bless you!",
24                            "Is there an owl in here?",
25                            "Is there an echo in here?" };
26 
27     public String processInput(String theInput)
28     {
29         String theOutput = null;
30 
31         if (state == WAITING) {
32             theOutput = "Knock! Knock!";
33             state = SENTKNOCKKNOCK;
34         }
35         else if (state == SENTKNOCKKNOCK) {
36             if (theInput.equalsIgnoreCase("Who's there?")) {
37                 theOutput = clues[currentJoke];
38                 state = SENTCLUE;
39             }
40             else {
41                 theOutput = "You're supposed to say /"Who's there?/"! " +
42                     "Try again. Knock! Knock!";
43             }
44         }
45         else if (state == SENTCLUE) {
46             if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) {
47                 theOutput = answers[currentJoke] + " Want another? (y/n)";
48                 state = ANOTHER;
49             } else {
50                 theOutput = "You're supposed to say /"" +
                  clues[currentJoke] + " who?/"" +
51                     "! Try again. Knock! Knock!";
52                 state = SENTKNOCKKNOCK;
53             }
54         }
55         else if (state == ANOTHER) {
56             if (theInput.equalsIgnoreCase("y")) {
57                 theOutput = "Knock! Knock!";
58                 if (currentJoke == (NUMJOKES - 1))
59                     currentJoke = 0;
60                 else
61                     currentJoke++;
62                 state = SENTKNOCKKNOCK;
63             }
64             else {
65                 theOutput = "Bye.";
66                 state = WAITING;
67             }
68         }
69         return theOutput;
70     }
71 }
 


    【程序注解】
    KnockKnockProtocol类具体定义了客户端与服务器交互的协议。定义了两个字符串数组clues = { "Turnip", "Little Old Lady", "Atch", "Who", "Who" }; answers = { "Turnip the heat, it's cold in here!", "I didn't know you could yodel!", "Bless you!", "Is there an owl in here?", "Is there an echo in here?" }。在processInput(String theInput)方法中根据这两个字符串和客户端的命令String theInput 决定要发送给客户端的字符串。程序假定了客户端应该发送的字符串,如果发现接收的字符串不符合,则返回提醒的字符串,交由socket发送给客户端。

    示例12-8是客户端源程序。
    【程序源代码】

1 // ==================== Program Description =====================
2 // 程序名称:示例12-8: KnockKnockClient.java
3 // 程序目的:客户端源程序
4 //=========================================================
5 import java.io.*;
6 import java.net.*;

8 public class KnockKnockClient
9 {
10     public static void main(String[] args) throws IOException
11     {
12         Socket kkSocket = null;
13         PrintWriter out = null;
14         BufferedReader in = null;
15 
16         try {
17             kkSocket = new Socket("hostname", 4444);
18             out = new PrintWriter(kkSocket.getOutputStream(), true);
19             in = new BufferedReader(new
InputStreamReader(kkSocket.getInputStream()));
20         }
21         catch (UnknownHostException e) {
22             System.err.println("Don't know about host: taranis.");
23             System.exit(1);
24         }
 
25         catch (IOException e) {
26             System.err.println("Couldn't get I/O for the
connection to: biao.");
27             System.exit(1);
28         }
29 
30         BufferedReader stdIn = new BufferedReader(new
InputStreamReader(System.in));
31         String fromServer;
32         String fromUser;
33 
34         while ((fromServer = in.readLine()) != null)
35         {
36             System.out.println("Server: " + fromServer);
37             if (fromServer.equals("Bye."))
38                 break;
39      
40             fromUser = stdIn.readLine();
41             if (fromUser != null) {
42                 System.out.println("Client: " + fromUser);
43                 out.println(fromUser);
44              }
45         }
46 
47         out.close();
48         in.close();
49         stdIn.close();
50         kkSocket.close();
51     }
52 }
 


    【程序输出结果】如下所示。

Server: Knock! Knock!
Who's there?
Client: Who's there?
Server: Turnip
Turnip who?
Client: Turnip who?
Server: Turnip the heat, it's cold in here! Want another? (y/n)
 


    【程序注解】
    在客户端程序中,第17行建立了一个套接字与服务器保持联系,然后用一个PrintWriter和一个BufferedReader与套接字相关联,以便能更好地与服务器相交互。而BufferedReader stdIn与客户端的标准输入System.in相关联。在while循环中,处理从服务器读取的数据,并把从stdIn中读取的数据发给服务器。最后调用out.close() 、in.close()、stdIn.close() 和kkSocket. close( )关闭套接字和输入输出流。

    客户端假定按先后顺序输入为"who's there ?"和"Turnip who?",如果你不是这样输入的话,服务器端不认识你输入的命令,会提示你该输入的命令。

 


http://www.niftyadmin.cn/n/1425647.html

相关文章

hive实验

利用Hive对某网站的用户数据进行分析。 1.创建dblab数据库 命令&#xff1a;create database dblab; 2. 在dblab数据库下创建bigdata_user表&#xff0c;该表中的各种属性如下&#xff1a; 字段名 类型 id int uid string item_id string behavior_type …

zookeeper安装部署与使用

Zookeeper安装部署 安装包下载地址&#xff1a;https://apache.org/dist/zookeeper/ 1.下载安装包然后上传到主节点rz&#xff0c;centos上可使用 wget 地址 2.解压缩&#xff1a;tar xf 安装包 3.移动到/opt目录下mv zookeeper-3.4.12 /opt 4.修改目录权限 命令&#xff…

十进制十六进制转换

以321为例&#xff1a;先记住1、16、256、4096这几个数字&#xff0c;即16的平方、16的立方等等。 321/256 1 余 65 》写下1 65/16 4 余 1 》写下14 1/1 1 余 0 》写下141 即十进制321等于十六进制141 1.比如&#xff1a;216是16进制&#xff0c;转10进制: &#xff1d;2*…

ubuntu连接xshell出现的问题

使用Ubuntu连接xshell的时候出现以下问题&#xff1a; 解决方法&#xff1a; 在ubuntu的管理员用户下&#xff0c;安装openssh-server&#xff08;不是管理员在命令前使用sudo&#xff09; 安装成功后&#xff0c;查看是否有启动ssh,使用ps -e | grep ssh查看&#xff0c;如果…

ABC类IP地址

A类IP地址一个A类IP地址由1字节的网络地址和3字节主机地址组成&#xff0c;网络地址的最高位 必须是"0"&#xff0c; 地址范围从1.0.0.0 到126.0.0.0。可用的A类网络有126个&#xff0c;每个 网络能容纳1亿多个主机B类IP地址  一个B类IP地址由2个字节的网络地址和…

wampserver图标呈现黄色

问题&#xff1a;在使用wampserver的时候&#xff0c;启动服务的时候&#xff0c;图标一直显示黄色&#xff0c;登录phpmyAdmin登录不成功 原因&#xff1a;wampserver设置的端口号被占用&#xff0c;&#xff08;默认是80端口&#xff09; 解决方法&#xff1a;修改端口号 …

轻松架设FTP服务器

轻松架设FTP服务器TYPSoft FTP Server下载地址:http://www.skycn.com/soft/1289.html添加用户Setup→Users New User ,PasswordRoot Directory”&#xff08;根目录&#xff09;、“Directory Access” &#xff08;访问目录&#xff09;、Files(文件)和 Directory(目录)等项…

再一次有关sqlserver安装错误问题

有关sqlserver安装错误问题错误&#xff1a;以前的某个程序安装已在安装计算机上创建挂起的文件操作。运行安装程序 之前必须重新启动计算机。解决&#xff1a;打开注册表编辑器&#xff0c;在 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager中找到…