牛骨文教育服务平台(让学习变的简单)

Java编程那些事儿109——网络编程示例2

陈跃峰

出自:http://blog.csdn.net/mailbomb

13.3.2 猜数字小游戏

下面这个示例是一个猜数字的控制台小游戏。该游戏的规则是:当客户端第一次连接到服务器端时,服务器端生产一个【0,50】之间的随机数字,然后客户端输入数字来猜该数字,每次客户端输入数字以后,发送给服务器端,服务器端判断该客户端发送的数字和随机数字的关系,并反馈比较结果,客户端总共有5次猜的机会,猜中时提示猜中,当输入”quit”时结束程序。

和前面的示例类似,在进行网络程序开发时,首先需要分解一下功能的实现,觉得功能是在客户端程序中实现还是在服务器端程序中实现。区分的规则一般是:客户端程序实现接收用户输入等界面功能,并实现一些基础的校验降低服务器端的压力,而将程序核心的逻辑以及数据存储等功能放在服务器端进行实现。遵循该原则划分的客户端和服务器端功能如下所示。

客户端程序功能列表:

1、  接收用户控制台输入

2、  判断输入内容是否合法

3、  按照协议格式发送数据

4、  根据服务器端的反馈给出相应提示

服务器端程序功能列表:

1、  接收客户端发送数据

2、  按照协议格式解析数据

3、  判断发送过来的数字和随机数字的关系

4、  根据判断结果生产协议数据

5、  将生产的数据反馈给客户端

在该示例中,实际使用的网络命令也只有两条,所以显得协议的格式比较简单。

其中客户端程序协议格式如下:

1、  将用户输入的数字转换为字符串,然后转换为byte数组

2、  发送“quit”字符串代表退出

其中服务器端程序协议格式如下:

1、  反馈长度为1个字节,数字0代表相等(猜中),1代表大了,2代表小了,其它数字代表错误。

实现该程序的代码比较多,下面分为客户端程序实现和服务器端程序实现分别进行列举。

客户端程序实现代码如下:

package guess;

import java.net.*;

import java.io.*;

/**

 * 猜数字客户端

 */

public class TCPClient {

  public static void main(String[] args) {

         Socket socket = null;

         OutputStream os = null;

         InputStream is = null;

         BufferedReader br = null;

         byte[] data = new byte[2];

         try{

                   //建立连接

                   socket = new Socket(

                                     "127.0.0.1",10001);

                   

                   //发送数据

                   os= socket.getOutputStream();

                   

                   //读取反馈数据

                   is = socket.getInputStream();

                   

                   //键盘输入流

                   br = new BufferedReader(

                                     new InputStreamReader(System.in));

                   

                   //多次输入

                   while(true){

                            System.out.println("请输入数字:");

                            //接收输入

                            String s = br.readLine();

                            //结束条件

                            if(s.equals("quit")){

                                     os.write("quit".getBytes());

                                     break;

                            }

                            //校验输入是否合法

                            boolean b = true;

                            try{

                                     Integer.parseInt(s);

                            }catch(Exception e){

                                     b = false;

                            }

                            if(b){ //输入合法

                                     //发送数据

                                     os.write(s.getBytes());

                                     //接收反馈

                                     is.read(data);

                                     //判断

                                     switch(data[0]){

                                     case 0:

                                               System.out.println("相等!祝贺你!");

                                               break;

                                     case 1:

                                               System.out.println("大了!");

                                               break;

                                     case 2:

                                               System.out.println("小了!");

                                               break;

                                     default:

                                               System.out.println("其它错误!");

                                     }

                                     //提示猜的次数

                                     System.out.println("你已经猜了" + data[1] + "次!");

                                     //判断次数是否达到5次

                                     if(data[1] >= 5){

                                               System.out.println("你挂了!");

                                               //给服务器端线程关闭的机会

                                               os.write("quit".getBytes());

                                               //结束客户端程序

                                               break;

                                     }

                            }else{  //输入错误

                                     System.out.println("输入错误!");

                            }

                   }

         }catch(Exception e){

                   e.printStackTrace();

         }finally{

                   try{

                            //关闭连接

                            br.close();

                            is.close();

                            os.close();

                            socket.close();

                   }catch(Exception e){

                            e.printStackTrace();

                   }

         }

  }

      }

在该示例中,首先建立一个到IP地址为127.0.0.1的端口为10001的连接,然后进行各个流的初始化工作,将逻辑控制的代码放入在一个while循环中,这样可以在客户端多次进行输入。在循环内部,首先判断用户输入的是否为quit字符串,如果是则结束程序,如果输入不是quit,则首先校验输入的是否是数字,如果不是数字则直接输出“输入错误!”并继续接收用户输入,如果是数字则发送给服务器端,并根据服务器端的反馈显示相应的提示信息。最后关闭流和连接,结束客户端程序。

服务器端程序的实现还是分为服务器控制程序和逻辑线程,实现的代码分别如下:

package guess;

import java.net.*;

/**

 * TCP连接方式的服务器端

 * 实现功能:接收客户端的数据,判断数字关系

 */

public class TCPServer {

  public static void main(String[] args) {

         try{

                   //监听端口

                   ServerSocket ss = new ServerSocket(10001);

                   System.out.println("服务器已启动:");

                   //逻辑处理

                   while(true){

                            //获得连接

                            Socket s = ss.accept(); 

                            //启动线程处理

                            new LogicThread(s);

                   }

                   

         }catch(Exception e){

                   e.printStackTrace();

         }

  }

      }

      package guess;

import java.net.*;

import java.io.*;

import java.util.*;

/**

 * 逻辑处理线程

 */

public class LogicThread extends Thread {

      Socket s;

      

      static Random r = new Random();

      

      public LogicThread(Socket s){

              this.s = s;

              start();  //启动线程

      }

      

      public void run(){ 

              //生成一个[0,50]的随机数

              int randomNumber = Math.abs(r.nextInt() % 51);

              //用户猜的次数

              int guessNumber = 0;

              InputStream is = null;

              OutputStream os = null;

              byte[] data = new byte[2];

              try{

                       //获得输入流

                       is = s.getInputStream();

                       //获得输出流

                       os = s.getOutputStream();

                       while(true){  //多次处理

                                 //读取客户端发送的数据

                                 byte[] b = new byte[1024];

                                 int n = is.read(b);

                                 String send = new String(b,0,n);

                                 //结束判别

                                 if(send.equals("quit")){

                                          break;

                                 }

                                 //解析、判断

                                 try{

                                          int num = Integer.parseInt(send);

                                          //处理

                                          guessNumber++; //猜的次数增加1

                                          data[1] = (byte)guessNumber;

                                          //判断

                                          if(num > randomNumber){

                                                   data[0] = 1;

                                          }else if(num < randomNumber){

                                                   data[0] = 2;

                                          }else{

                                                   data[0] = 0;

                                                   //如果猜对

                                                   guessNumber = 0; //清零

                                                   randomNumber = Math.abs(r.nextInt() % 51);

                                          }

                                          //反馈给客户端

                                          os.write(data);                                     

                                          

                                 }catch(Exception e){ //数据格式错误

                                          data[0] = 3;

                                          data[1] = (byte)guessNumber;

                                          os.write(data);  //发送错误标识

                                          break;

                                 }

                                 os.flush();   //强制发送

                       }

                       

              }catch(Exception e){

                       e.printStackTrace();

              }finally{

                       try{

                                 is.close();

                                 os.close();

                                 s.close();

                       }catch(Exception e){}

              }

      }

}

在该示例中,服务器端控制部分和前面的示例中一样。也是等待客户端连接,如果有客户端连接到达时,则启动新的线程去处理客户端连接。在逻辑线程中实现程序的核心逻辑,首先当线程执行时生产一个随机数字,然后根据客户端发送过来的数据,判断客户端发送数字和随机数字的关系,然后反馈相应的数字的值,并记忆客户端已经猜过的次数,当客户端猜中以后清零猜过的次数,使得客户端程序可以继续进行游戏。

总体来说,该程序示例的结构以及功能都与上一个程序比较类似,希望通过比较这两个程序,加深对于网络编程的认识,早日步入网络编程的大门。