回顾多线程的知识
- 进程是应用程序执行的实例,有独立的内存空间和系统资源;
- 线程是CPU调度和分派的基本单位,进程中执行运算的最小单位,真正在CPU上运行的是线程;
- 使用线程的步骤:
①定义线程
②创建线程对象
③启动线程
④终止线程 - main()方法即为主线程入口
一个进程里可以有多个线程,但至少包含一个线程(主线程),Main(String[] args)方法是主线程; - 如果一个类既需要使用创建线程又需要继承某个类,可以实现Runnable接口;
多线程进行网络编程的必要
1、多次数据交互
在程序中设置一个循环,通过循环,不断地向对方发送请求。
2、使服务器同时响应多个客户端的请求
服务器端每接收到一个新的连接请求,就启动一个专门的线程与该客户端进行交互。
- 服务器:一直监听客户请求,一旦监听到有客户请求,立即创建一个新的线程,开启线程
- 线程:接收客户请求,给予客户一个响应
- 客户端:发送请求到服务器端,接收服务器端的响应
范例
实现一个简单的四则运算:客户端从键盘输入四则运算表达式,但不进行运算,而是将表达式传送到服务器端。服务器端接收到表达式后进行处理和计算,将运算结果返回给客户端进行显示。
优点:将简单的交互和复杂的运算进行了有效分离,能充分发挥服务器的强大处理能力。
本例由三个类组成:ClientDemo、ServerDemo、LogicThread
/**
* 下面是客户端程序ClientDemo。
*思路分析:
* ①客户端程序首先与服务器端建立连接,连接成功后生成socket对象,并进一步建立输入流和输出流对象;
* ②使用一个循环与服务器端进行多次数据交互:从键盘输入一个四则运算表达式,将该表达式通过输出流对象传递给服务器端进行计算;
* ③然后从输入流对象获取计算结果并进行显示,直到输入字符“0”结束数据交互后,关闭网络连接。
*/
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = null;
Socket socket = null;
DataInputStream in = null;
DataOutputStream out = null;
String severIP = "127.0.0.1";// 服务器地址
int port = 5050;// 服务器端口
try {
socket = new Socket(severIP, port);// 连接服务器
in = new DataInputStream(socket.getInputStream());// 创建输入流
out = new DataOutputStream(socket.getOutputStream());// 创建输出流
System.out.println("请输入一个正整数的四则运算表达式");
while (scanner.hasNext()) {
input = scanner.nextLine();// 从键盘输入一个待计算的四则运算表达式
if (!input.equals("0")) {
out.writeUTF(input);// 向服务器端发送运算请求
String result = in.readUTF();// 等待读取计算结果
System.out.println("服务器返回的计算结果:" + result);
System.out.println("请输入一个正整数的四则运算表达式(输入0退出):");
} else {
break;// 请求结束
}
}
} catch (Exception e) {
System.out.println("与服务器连接中断");
} finally {
try {// 关闭网络连接
in.close();
out.close();
socket.close();
System.out.println("连接结束");
} catch (Exception e) {
}
}
}
}
/**
* 下面是服务器端程序ServerDemo。
* 服务器端程序由两部分组成,ServerDemo类实现服务器端控制,接收客户端连接,然后启动专门的逻辑处理线程处理。
*/
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) {
ServerSocket server_socket = null;
Socket socket = null;
int port = 5050;
while (true) {
try {
server_socket = new ServerSocket(port);
System.out.println("服务器启动!");
} catch (Exception e1) {
System.out.println("正在监听");// ServerSocket对象不能重复创建
}
try {
System.out.println("等待客户请求");
socket = server_socket.accept();
System.out.println("客户的地址:" + socket.getInetAddress() + ":" + socket.getPort());
} catch (Exception e) {
System.out.println("正在等待客户");
}
if (socket != null) {
new LogicThread(socket);// 为每个客户启动一个专门的线程
}
}
}
}
/**
* 下面是线程类程序LogicThread 。
* 该类实现对一个客户端的逻辑处理,对接收的表达式进行计算,并将结果返回给客户端。
*/
package socketThread;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
public class LogicThread extends Thread {
Socket socket = null;
DataInputStream in = null;
DataOutputStream out = null;
String str;
String response;
String ip;
int port;
public LogicThread(Socket socket) {
this.socket = socket;
start();
}
public void run() {
try {
in = new DataInputStream(socket.getInputStream());// 创建输入流
out = new DataOutputStream(socket.getOutputStream());// 创建输出流
ip = socket.getInetAddress().getHostAddress();// 客户端IP地址
port = socket.getPort();// 客户端的端口号
while (true) {
str = in.readUTF();// 获取客户端的表达式
System.out.println("客户端" + ip + ":" + port + "发送的请求内容:");
System.out.println(str + "=?");
if (str.equals("0")) {
System.out.println("连接结束");
break;
} else {
response = doComputer(str);// 对表达式进行计算
out.writeUTF(response);// 响应计算结果
}
}
} catch (Exception e) {
System.out.println("连接结束");
} finally {
try {
in.close();
out.close();
socket.close();
} catch (Exception e) {
}
}
}
public String doComputer(String str) {
String input;
String[] sym;
String[] data;
int a = 0, b = 0, result = 0;
input = str;
data = input.split("\\D+");// 分解表达式中的正整数
sym = input.split("\\d+");// 分解表达式中的运算符
a = Integer.parseInt(data[0]);// 第一个正整数
b = Integer.parseInt(data[1]);// 第二个正整数
try {
switch (sym[1]) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
result = a / b;
break;
}
System.out.println("计算结果:" + input + "=" + result);
return String.valueOf(result);
} catch (java.lang.ArithmeticException e) {
System.out.println("数据错误!");
return "数据错误!";
}
}
}
先运行ServerDemo类,然后再运行ClientDemo类。
附 :Console的使用方法
(以上面的例子为引例进行介绍)
①先运行ServerDemo类,并用Pin Console订住当前的控制台。
②开启另一个新的Console,然后在新的Console中运行ClientDemo类。