## Java编程那些事儿109——网络编程示例2
陈跃峰
出自:[http://blog.csdn.net/mailbomb](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){}
}
}
}
~~~
在该示例中,服务器端控制部分和前面的示例中一样。也是等待客户端连接,如果有客户端连接到达时,则启动新的线程去处理客户端连接。在逻辑线程中实现程序的核心逻辑,首先当线程执行时生产一个随机数字,然后根据客户端发送过来的数据,判断客户端发送数字和随机数字的关系,然后反馈相应的数字的值,并记忆客户端已经猜过的次数,当客户端猜中以后清零猜过的次数,使得客户端程序可以继续进行游戏。
总体来说,该程序示例的结构以及功能都与上一个程序比较类似,希望通过比较这两个程序,加深对于网络编程的认识,早日步入网络编程的大门。
- 前言
- (1)序言
- (2)程序设计是什么?
- (3)你适合学习程序设计吗?
- (4)如何学好程序设计?
- (5)程序设计介绍小结
- (6)计算机软件基本概念
- (7)进制的概念
- (8)计算机内部的数据表达
- (9)网络编程基础
- (10)Java语言简介
- (11)JDK的获得、安装和配置
- (12)第一个HelloWorld程序
- (13)Eclipse基本使用
- (14)Eclipse基础使用进阶
- (15)如何学好Java语法
- (16)代码框架、关键字和标识符
- (17)基本数据类型
- (18)变量和常量
- (19)数据类型转换
- (20)空白、语句结束和注释
- (21)算术运算符
- (22)比较运算符
- (23)逻辑运算符
- (24)赋值运算符
- (25)位运算符
- (26)移位运算符
- (27)其它运算符
- (28)运算符优先级
- (29)表达式
- (30)流程控制基础
- (31)if语句语法(1)
- (32)if语句语法(2)
- (33)if语句语法(3)
- (34)switch语句语法
- (35)while语句语法
- (36)do-while语句语法
- (37)for语句语法
- (38)break和continue语句
- (39)流程控制综合示例1
- (40)流程控制综合示例2
- (41)流程控制综合示例3
- (42)流程控制综合练习
- (43)数组概述
- (44)数组基础语法
- (45)数组使用示例1
- (46)数组使用示例2
- (47)数组使用示例3
- (48)多维数组基础
- (49)多维数组使用示例1
- (50)多维数组使用示例2
- (51)多维数组练习
- (52)方法声明
- (53)方法声明示例
- (54)方法调用
- (55)方法重载和参数传递
- (56)方法练习
- (57)面向对象基础
- (58)类(一)
- (59)类(二)
- (60)对象
- (61)面向对象设计方法和面向对象特性(一)
- (62)继承(二)
- (63)多态性
- (64)访问控制符、修饰符和其它关键字
- (65)static修饰符
- (66)final修饰符
- (67)this和super
- (68)抽象类和接口(一)
- (69)抽象类和接口(二)
- (70)抽象类和接口(三)
- (71)内部类简介
- (72)包的概念
- (73)JDK文档使用
- (74)java.lang包介绍1
- (75)String类使用
- (76)StringBuffer类和System类
- (77)包装类
- (78)时间和日期处理
- (79)Random随机处理
- (80)集合框架简述
- (81)异常处理概述
- (82)异常处理语法1
- (83)异常处理语法2
- (84)IO简介
- (85)IO类体系
- (86)文件操作之File类使用
- (87)文件操作之读取文件
- (88)文件操作之写文件
- (89)读取控制台输入
- (90)装饰流使用1
- (91)装饰流使用2
- (92)IO使用注意问题
- (93)多线程基础
- (94)多线程实现方式1
- (95)多线程实现方式2
- (96)多线程使用示例1
- (97)多线程使用示例2
- (98)多线程问题及处理1
- (99)多线程问题及处理2
- (100)多线程问题及处理3
- (101)网络编程概述
- (102)网络编程技术1
- (103)网络编程技术2
- (104)网络编程技术3
- (105)网络编程技术4
- (106)网络编程技术5
- (107)网络协议概念
- (108)网络编程示例1
- (109)网络编程示例2
- (110)网络编程小结