猜数字小游戏
下面这个示例是一个猜数字的控制台小游戏。该游戏的规则是:当客户端第一次连接到服务器端时,服务器端生产一个【0,50】之间的随机数字,然后客户端输入数字来猜该数字,每次客户端输入数字以后,发送给服务器端,服务器端判断该客户端发送的数字和随机数字的关系,并反馈比较结果,客户端总共有5次猜的机会,猜中时提示猜中,当输入”quit”时结束程序。
和前面的示例类似,在进行网络程序开发时,首先需要分解一下功能的实现,觉得功能是在客户端程序中实现还是在服务器端程序中实现。区分的规则一般是:客户端 程序实现接收用户输入等界面功能,并实现一些基础的校验降低服务器端的压力,而将程序核心的逻辑以及数据存储等功能放在服务器端进行实现。遵循该原则划分 的客户端和服务器端功能如下所示。
客户端程序功能列表:
- 接收用户控制台输入
- 判断输入内容是否合法
- 按照协议格式发送数据
- 根据服务器端的反馈给出相应提示
服务器端程序功能列表:
- 接收客户端发送数据
- 按照协议格式解析数据
- 判断发送过来的数字和随机数字的关系
- 根据判断结果生产协议数据
- 将生产的数据反馈给客户端
在该示例中,实际使用的网络命令也只有两条,所以显得协议的格式比较简单。
其中客户端程序协议格式如下:
- 将用户输入的数字转换为字符串,然后转换为byte数组
- 发送“quit”字符串代表退出
其中服务器端程序协议格式如下:
反馈长度为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){}
}
}
}
在该示例中,服务器端控制部分和前面的示例中一样。也是等待客户端连接,如果有客户端连接到达时,则启动新的线程去处理客户端连接。在逻辑线程中实现程序的 核心逻辑,首先当线程执行时生产一个随机数字,然后根据客户端发送过来的数据,判断客户端发送数字和随机数字的关系,然后反馈相应的数字的值,并记忆客户 端已经猜过的次数,当客户端猜中以后清零猜过的次数,使得客户端程序可以继续进行游戏。
总体来说,该程序示例的结构以及功能都与上一个程序比较类似,希望通过比较这两个程序,加深对于网络编程的认识,早日步入网络编程的大门。