Java中的Socket编程(4)-实例

[size=medium]
[b]纸上得来终觉浅,绝知此事要躬行。—陆游[/b]
意思是说,从书本上得来的知识终归是浅薄的,要透彻的认知事务还必须亲自实践。
这句话告诉我们,除了要掌握好理论知识,还必须得亲自动手做实践,来证明结论。理论引导实践,实践推翻理论。正是这个道理!

前面三章,我们对Socket编程进行了一个比较全面的阐述,相信大家对Socket编程有了一个比较全面的认识,今天我们来实现一个实例,加深对Socket编程的理解。

[b]1、实例:实现一个质数判断程序。即[/b]
客户端在控制台输入一个数字,服务端告诉客户端该数字是否是质数。

[b]1)服务端程序流程:[/b]
(1)获取客户端输入的数据
(2)做判断处理
(3)返回处理结果

[b]2)客户端程序流程:[/b]
(1)用户在控制台输入数字
(2)获取用户输入的数字并发送给服务端
(3)获取服务端响应结果,并输出在控制台中

[b]3)说明:[/b]
(1)质数(素数):除了1和本身,没有其他公约数
(2)为节省流量,服务端程序不直接告诉客户端结果,而是通过规定的协议,返回状态码。
协议规定为(这个协议自己任意定):0 是质数, 1 不是质数, 2 输入非法

[b]2、代码实现:[/b]

[b]1)服务端代码:[/b]
[/size]

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* 服务端:判断客户端输入的数字是否是质数
* 步骤:
* 1.获取客户端输入的数据
* 2.做判断处理
* 3.返回处理结果
*
* @author Sam
*
*/
public class PrimeNumServer {

// 服务端监听的端口号
private static final int PORT = 10000;

/**
* @param args
*/
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(PORT);
System.out.println("Server 等待客户端接入...");
while (true) {
// 监听客户端请求
Socket socket = ss.accept();
// 监听到有客户端接入,则开启一个子线程处理请求
new Thread(new Task(socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 处理Socket请求任务类
*
*/
private static class Task implements Runnable {

private Socket socket;
private BufferedReader buffRreader;
private BufferedWriter buffWriter;

public Task(Socket socket) {
try {
this.socket = socket;
// 字符读取流
this.buffRreader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
// 字符写入流
this.buffWriter = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void run() {
handlRequest();
}

/**
* 处理请求
*/
private void handlRequest() {
try {
// 接收客户端数据
String data = receive();
// 处理逻辑
String result = checkIsPrimeNum(data);
// 发送响应结果
send(result);
} finally {
StreamUtils.close(buffRreader, buffWriter);
close(socket);
}
}

/**
* 检查是否是质数,根据协议规定,返回状态码
* @param numStr
* @return 0 是质数, 1 不是质数, 2 输入非法
*/
private String checkIsPrimeNum(String numStr) {
// 先判断输入的是否是数字
boolean isNumber = isNumber(numStr);
// 是数字,进一步判断是否是质数(素数)
if (isNumber) {
int number = Integer.parseInt(numStr);
return isPrimeNumber(number);

} else {// 输入非数字,则认为是非法的
return "2";
}
}

/**
* 判断是否是数字
* @param numStr
* @return
*/
private boolean isNumber(String numStr) {
Pattern pattern = Pattern.compile("[0-9]*");
Matcher matcher = pattern.matcher(numStr);
return matcher.matches();
}

/**
* 判断是否是质数
* @param number
* @return 0 是质数, 1 不是质数, 2 输入非法
*/
private String isPrimeNumber(int number) {
if (number < 2) {// 如果数字小于2,则认为是非法
return "2";
} else {
// 真正做质数的判断逻辑
for (int i=2; i<=number-1; i++) {
/*
* 用2至number-1之间的所有数去整除number,
* 如果有一个能被整除,说明是number非质数
*/
if (number % i == 0) {
return "1";
}
}
return "0";
}
}

/**
* 接收客户端发送过来的数据
* @return
*/
private String receive() {
try {
return buffRreader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

/**
* 向客户端发送响应结果
* @param result
*/
private void send(String result) {
try {
buffWriter.write(result);
buffWriter.newLine();// 写一个换行符
buffWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}

}

/**
* 关闭客户端Socket
* @param socket
*/
private static void close(Socket socket) {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
}

}

[size=medium]
[b]服务端代码分析:[/b]
1、在[b]isNumber(String numStr)[/b]方法中判断一个字符串是否是数字,用了一个正则匹配,匹配规则为:字符串是由0~9之间的数组成,并且可以出现零次或多次;有关更详细的匹配规则可以参看JDK文档中的:java.util.regex.Pattern类
2、在[b]isPrimeNumber(int number)[/b]方法中判断一个数是否是质数:
用2至number-1之间的所有数去整除number,
1)如果有一个能被整除说明是number非质数;
2)如果所有的数都不未来整除,则说明是质数。
3、为了避免代码冗余,在finally子句中使用了一个[b]StreamUtils[/b]流工具类来关闭资源,最后会把这个工具类的代码贴上。

[b]2)客户端代码:[/b]
[/size]

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
* 客户端:输入一个数字
* 步骤:
* 1.用户在控制台输入数字
* 2.获取用户输入的数字并发送给服务端
* 3.获取服务端响应结果
*
* @author Sam
*
*/
public class PrimeNumClient {

// 主机
private static final String HOST = "127.0.0.1";
// 端口号
private static final int PORT = 10000;

private static Socket socket;
private static BufferedReader buffReader;
private static BufferedWriter buffWriter;

private static BufferedReader keyboardReader;

/**
* @param args
*/
public static void main(String[] args) {
try {
init();
System.out.println("请在下面输入质数:");
// 获取键盘输入流
keyboardReader = new BufferedReader(new InputStreamReader(System.in));
// 发送数据
send(keyboardReader.readLine());
// 接收响应结果
String result = receive();
// 处理结果
handleResult(result);

} catch (IOException e) {
e.printStackTrace();
} finally {// 释放资源
StreamUtils.close(buffReader, buffWriter);
StreamUtils.close(keyboardReader);
close(socket);
}
}

/**
* 初始化操作
*/
private static void init() {
try {
// 连接到指定的主机与端口号的服务器上
socket = new Socket(HOST, PORT);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();

buffReader = new BufferedReader(new InputStreamReader(in));
buffWriter = new BufferedWriter(new OutputStreamWriter(out));

} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 向服务端发送数据
* @param data
*/
private static void send(String data) {
try {
buffWriter.write(data);
buffWriter.newLine();// 写一个换行符
buffWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 接收服务端响应结果
* @return
*/
private static String receive() {
try {
return buffReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

/**
* 处理服务端返回的结果
*/
private static void handleResult(String result) {

if ("0".equals(result)) {// 是质数
System.out.println("您输入的是质数");

} else if ("1".equals(result)) {// 不是质数
System.out.println("您输入的不是质数");

} else if ("2".equals(result)) { // 输入非法
System.out.println("警告!您输入非法!");

}
}

/**
* 关闭客户端Socket
* @param socket
*/
private static void close(Socket socket) {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
}

}

[size=medium]
[b]客户端代码分析:[/b]
1、获取用户输入的数据,往服务端写数据时,一定要记得用[b]newLine()[/b]方法写一个换行符,否则服务端一直处于读取状态,不会停止,因为读不到结束符;同理,服务端在往客户端写入响应结果时,也要用newLine()方法写一个换行符,否则客户端也会一直处于阻塞状态;
2、获取服务端返回的响应结果,根据之前规定的协议,在控制台中输出相应的提示信息。

[b]3)IO流工具类StreamUtils代码:[/b]
[/size]


import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

/**
* IO流工具
* @author Sam
*
*/
public class StreamUtils {

/**
* 同时关闭字符读取流与字符写入流
* @param reader
* @param writer
*/
public static void close(Reader reader, Writer writer) {
close(reader);
close(writer);
}

/**
* 关闭字符读取流
* @param reader
*/
public static void close(Reader reader) {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
reader = null;// 置空
}
}

/**
* 关闭字符写入流
* @param writer
*/
public static void close(Writer writer) {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
writer = null;// 置空
}
}
}

[size=medium]
[b]IO流工具类代码分析:[/b]
一般操作中,比较常用的,而且代码变动不大的,我们都会封装成一个工具类,以后直接使用,而不用重复的写。既避免代码冗余,又提高工作效率。

[b]3、运行结果:[/b]
注意:先运行Server程序,再运行Client程序。为了测试结果,多次运行Client程序,并输入不同的数据,得到不同的提示信息。

[b]1)Server程序控制台:[/b]
[img]http://dl2.iteye.com/upload/attachment/0096/3383/e81c6196-085b-335b-8632-024c12a228b3.jpg[/img]

[b]2)Client程序控制台[/b]
[b](1)运行第1次:[/b]

[img]http://dl2.iteye.com/upload/attachment/0096/3385/6f4bd434-3be1-36b7-88b3-638836bca8b1.jpg[/img]

[b](2)运行第2次:[/b]

[img]http://dl2.iteye.com/upload/attachment/0096/3387/1c672a2d-3fa7-3ae7-a123-08a1a8c93b69.jpg[/img]

[b](3)运行第3次:[/b]

[img]http://dl2.iteye.com/upload/attachment/0096/3393/44a71217-dd99-31dd-a6a4-7945e0072494.jpg[/img]

[b](4)运行第4次:[/b]

[img]http://dl2.iteye.com/upload/attachment/0096/3395/637f378d-b8c3-3894-81db-fc9401467551.jpg[/img]

[b]4、总结:[/b]
程序代码的逻辑并不复杂,但是基本做到了[b]代码结构化[/b]。每个功能点都封装成一个方法,不同的业务放在不同的代码块中。这样以后要是某个模块出现了问题,就找相应的模块修改,而不被其他逻辑所干扰;实际开始中,也应该是这种思想。
[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值