网络编程|多线程网络编程

回顾多线程的知识

  • 进程是应用程序执行的实例,有独立的内存空间和系统资源;
  • 线程是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类。
在这里插入图片描述

详细的Console操作方法详见:

https://blog.csdn.net/Geekst/article/details/90140606

猜你喜欢

转载自blog.csdn.net/Geekst/article/details/89574850
今日推荐