Java 网络编程 -- 基于TCP 模拟多用户登录

Java TCP的基本操作参考前一篇:Java 网络编程 – 基于TCP实现文件上传

实现多用户操作之前先实现以下单用户操作,假设目前有一个用户:
账号:zs
密码:123

服务端:

public class LoginServer {
	public static void main(String[] args) throws IOException {
		System.out.println("=========server========");
		// 1、使用serverSocket 创建服务端
		ServerSocket server = new ServerSocket(8888);
		// 2、阻塞式连接
		Socket socket = server .accept();
		// 3、操作
		DataInputStream dis = new DataInputStream(socket.getInputStream());
		String datas = dis.readUTF();
		// 解析用户信息
		String[] dataArray = datas.split("&");
		String uname = null;
		String pwd = null;
		for (String info : dataArray) {
			String[] userInfo = info.split("=");
			if("uname".equals(userInfo[0])) {
				uname = userInfo[1];
				System.out.println("用户名:" + uname);
			}else if("pwd".equals(userInfo[0])) {
				pwd = userInfo[1];
				System.out.println("密码:" + pwd);
			}
		}
		// 输出 模拟和数据库比较信息
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		if(uname.equals("zs") && pwd.equals("123")) {
			dos.writeUTF("登录成功,欢迎回来");
		}else {
			dos.writeUTF("用户名或密码错误");
		}
		// 4、释放资源
		dos.close();
		dis.close();
		socket.close();
		server.close();
	}
}

客户端:

public class LoginClient {
	public static void main(String[] args) throws IOException, IOException {
		System.out.println("=========client========");
		// 1、使用Socket 创建客户端
		Socket client = new Socket("localhost", 8888);
		// 2、操作
		// 输入
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.print("请输入用户名:");
		String uname = br.readLine();
		System.out.print("请输入用密码:");
		String pwd = br.readLine();
		DataOutputStream dos = new DataOutputStream(client.getOutputStream());
		dos.writeUTF("uname=" + uname +"&pwd=" + pwd);
		dos.flush();
		// 接收服务端响应
		DataInputStream dis = new DataInputStream(client.getInputStream());
		String response = dis.readUTF();
		System.out.println(response);
		// 3、释放资源
		dis.close();
		dos.close();
		br.close();
		client.close();
	}
}

运行测试:
Java 网络编程 -- 基于TCP 模拟多用户登录

现在实现了一个用户登录操作,接下来模拟多用户登录。
对于服务端来说一个连接就是一个socket,要满足多用户登录的需求,如果直接在现有代码上加上while循环,需要一个用户操作完,下一个用户接着操作,这样很不合理,明显的我们要加入多线程,并且现在所有代码都写在main 方法里面,不好维护。

如果使用lambda表达式,代码比较多,看起来比较累。

我们还是用静态内部类封装一下每一个类

封装服务端:

public class LoginMultiServer {
	public static void main(String[] args) throws IOException {
		System.out.println("=========server========");
		// 1、使用serverSocket 创建服务端
		ServerSocket server = new ServerSocket(8888);
		boolean isRuning = true;
		while (isRuning) {
			// 2、阻塞式连接
			Socket socket = server.accept();
			System.out.println("一个客户端建立了连接");
			// 3、操作
			new Thread(new Channel(socket)).start();
		}
		server.close();
	}
	
	// 一个Channel 代表一个客户端
	static class Channel implements Runnable{
		private Socket socket;
		private DataInputStream dis;
		private DataOutputStream dos;
		
		public Channel(Socket socket) {
			this.socket = socket;
			try {
				// 输入
				dis = new DataInputStream(socket.getInputStream());
				// 输出
				dos = new DataOutputStream(socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
				release(); // 一个出错就不用玩了
			}
			
		}
		
		// 接收数据
		public String receive() {
			String datas = "";
			try {
				datas = dis.readUTF();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return datas;
		}
		
		// 发送数据
		public void send(String msg) {
			try {
				dos.writeUTF(msg);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		// 释放资源
		public void release() {
			try {
				if(null != dos)
				dos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(null != dis)
				dis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(null != dos)
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		@Override
		public void run() {
			
			// 3、操作
			String datas = receive();
			// 解析用户信息
			String[] dataArray = datas.split("&");
			String uname = null;
			String pwd = null;
			for (String info : dataArray) {
				String[] userInfo = info.split("=");
				if ("uname".equals(userInfo[0])) {
					uname = userInfo[1];
					System.out.println("用户名:" + uname);
				} else if ("pwd".equals(userInfo[0])) {
					pwd = userInfo[1];
					System.out.println("密码:" + pwd);
				}
			}
			// 输出 模拟和数据库比较信息
			if (uname.equals("zs") && pwd.equals("123")) {
				send("登录成功,欢迎回来");
			} else {
				send("用户名或密码错误");
			}
			// 4、释放资源
			release();
		}
		
	}
}

封装客户端:

public class LoginMultiClient {
	public static void main(String[] args) throws IOException, IOException {
		System.out.println("=========client========");
		// 1、使用Socket 创建客户端
		Socket client = new Socket("localhost", 8888);
		// 2、操作
		// 输入
		new Send(client).send();
		// 接收服务端响应
		new Receive(client).receice();
		// 3、释放资源
		client.close();
	}
	
	// 发送
	static class Send{
		private Socket client;
		private BufferedReader br;
		private DataOutputStream dos;
		private String msg;
		public Send(Socket client) {
			try {
				this.client = client;
				br = new BufferedReader(new InputStreamReader(System.in));
				this.msg = init();
				dos = new DataOutputStream(client.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		private String init() {
			try {
				System.out.print("请输入用户名:");
				String uname = br.readLine();
				System.out.print("请输入用密码:");
				String pwd = br.readLine();
				return "uname=" + uname +"&pwd=" + pwd;
			} catch (IOException e) {
				e.printStackTrace();
			}
			return "";
		}
		
		public void send() {
			try {
				dos.writeUTF(this.msg);
				dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	// 接收
	static class Receive{
		private Socket client;
		private DataInputStream dis;
		public Receive(Socket client) {
			this.client = client;
			try {
				dis = new DataInputStream(client.getInputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		public void receice() {
			String response;
			try {
				response = dis.readUTF();
				System.out.println(response);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

封装之后,main 方法里可以比较简洁的书写逻辑代码了。

运行测试:
Java 网络编程 -- 基于TCP 模拟多用户登录