需求
代码
- SysUser 用户类
- Operation 操作类
- Client 客户端
- Server 服务端
- ServerReaderThread 服务端线程类
SysUser 用户类
- 需要实现Serializable 方便序列化,传输对象
public class SysUser implements Serializable {private String username;private String password;public SysUser() {}public SysUser(String username, String password) {this.username = username;this.password = password;}public String getUsername() {return this.username;}public String getPassword() {return this.password;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "SysUser{" +"username='" + username + '\'' +", password='" + password + '\'' +'}';}
}
操作类
操作方法和被操作的用户
public class Operation implements Serializable {private String operation;private SysUser sysUser;public Operation() {}public Operation(String operation, SysUser sysUser) {this.operation = operation;this.sysUser = sysUser;}public String getOperation() {return this.operation;}public SysUser getSysUser() {return this.sysUser;}public void setOperation(String operation) {this.operation = operation;}public void setSysUser(SysUser sysUser) {this.sysUser = sysUser;}@Overridepublic String toString() {return "Operation{" +"operation='" + operation + '\'' +", sysUser=" + sysUser +'}';}
}
客户端
- 本例是一个
长连接
:一直挂在服务器端,那么服务器端的读取线程中就必须要有一个while 循环
不断读取此长连接的内容- 如果不使用本例的方法,而是
每次都创建新的socket
连接服务器,那么读取线程中就必须要有一个while 循环就是非必要的,因为每次连接只会发送一次,等待服务器端响应之后,需要关闭此socket
,这就是短连接
// 该类是一个客户端类
public class Client {private static Scanner scanner = new Scanner(System.in);public static void main(String[] args) throws IOException {start();}// 该方法是一个启动方法public static void start() throws IOException {Scanner scanner = new Scanner(System.in);Socket socket = new Socket("127.0.0.1",8888);// 此处加死循环 是为了让客户端一直运行while (true){System.out.println("请输入你要进行的操作");System.out.println("1.注册");System.out.println("2.登录");System.out.println("3.退出");String i = scanner.next();switch (i){case "1":System.out.println("注册");register(socket);break;case "2":System.out.println("登录");login(socket);break;case "3":System.out.println("退出");socket.close();break;default:System.out.println("输入错误,请重新输入");}}}// 该方法是一个注册方法public static void register(Socket socket) throws IOException {System.out.println("请输入用户名");String username = scanner.next();System.out.println("请输入密码");String password = scanner.next();SysUser sysUser = new SysUser(username,password);Operation operation = new Operation("register",sysUser);// 获取输出流requestAndRespond(socket, operation);}private static void requestAndRespond(Socket socket, Operation operation) throws IOException {OutputStream outputStream = socket.getOutputStream();// 封装输出流为对象输出流ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);// 写出对象objectOutputStream.writeObject(operation);objectOutputStream.flush();// 不要关闭流 否则会关闭socket//objectOutputStream.close();// 获取响应InputStream inputStream = socket.getInputStream();DataInputStream dataInputStream = new DataInputStream(inputStream);String response = dataInputStream.readUTF();System.out.println(response);}// 该方法是一个登录方法public static void login(Socket socket) throws IOException {System.out.println("请输入用户名");String username = scanner.next();System.out.println("请输入密码");String password = scanner.next();SysUser sysUser = new SysUser(username,password);Operation operation = new Operation("login",sysUser);// 获取输出流requestAndRespond(socket, operation);}
}
服务器端
// 该类是一个服务端类
public class Server {// 模拟数据库public static final File file = new File("test999.txt");public static ServerSocket serverSocket;// 创建线程池public static final ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());static {try {serverSocket = new ServerSocket(8888);} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {start();}// 该方法是一个启动方法public static void start() throws IOException {System.out.println("服务端启动");listen();}public static void listen() throws IOException {System.out.println("监听中");// 此处加死循环 是为了让服务器已知监听有没有客户端连接while (true){// 获取客户端的socketSocket socket = serverSocket.accept();// 使用线程池执行线程pool.execute(new ServerReaderThread(socket));}}// 该方法是一个注册方法public static void register(SysUser sysUser,Socket socket) throws IOException, ClassNotFoundException {System.out.println("注册");// 获取数据库中的所有用户FileInputStream fileInputStream = new FileInputStream(file);if (fileInputStream.available() > 0) {ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);ArrayList<SysUser> sysUsers = (ArrayList<SysUser>) objectInputStream.readObject();for (SysUser user : sysUsers) {if (user.getUsername().equals(sysUser.getUsername())) {System.out.println("用户名已存在");OutputStream outputStream = socket.getOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(outputStream);dataOutputStream.writeUTF("用户名已存在");dataOutputStream.flush();
// dataOutputStream.close();return;}}}// 注册用户// 将用户写入数据库 追加模式FileOutputStream fileOutputStream = new FileOutputStream(file,true);ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);ArrayList<SysUser> sysUsersRegister = new ArrayList<>();sysUsersRegister.add(sysUser);objectOutputStream.writeObject(sysUsersRegister);System.out.println("注册成功");System.out.println(sysUser);OutputStream outputStream = socket.getOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(outputStream);dataOutputStream.writeUTF("注册成功");dataOutputStream.flush();
// dataOutputStream.close();}// 该方法是一个登录方法public static void login(SysUser sysUser,Socket socket) throws IOException, ClassNotFoundException {System.out.println("登录");// 获取数据库中的所有用户FileInputStream fileInputStream = new FileInputStream(file);if (fileInputStream.available() > 0) {ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);ArrayList<SysUser> sysUsers = (ArrayList<SysUser>) objectInputStream.readObject();for (SysUser user : sysUsers) {if (user.getUsername().equals(sysUser.getUsername())&&user.getPassword().equals(sysUser.getPassword())) {System.out.println("登录成功");System.out.println(sysUser);OutputStream outputStream = socket.getOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(outputStream);dataOutputStream.writeUTF("登录成功");dataOutputStream.flush();
// dataOutputStream.close();}else {System.out.println("用户名或密码错误");OutputStream outputStream = socket.getOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(outputStream);dataOutputStream.writeUTF("用户名或密码错误");dataOutputStream.flush();
// dataOutputStream.close();}}}else {System.out.println("数据库中没有用户");OutputStream outputStream = socket.getOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(outputStream);dataOutputStream.writeUTF("数据库中没有用户");dataOutputStream.flush();
// dataOutputStream.close();}}
}
服务器端读取类
- 本例是一个
长连接
:一直挂在服务器端,那么服务器端的读取线程中就必须要有一个while 循环
不断读取此长连接的内容- 如果不使用本例的方法,而是
每次都创建新的socket
连接服务器,那么读取线程中就必须要有一个while 循环就是非必要的,因为每次连接只会发送一次,等待服务器端响应之后,需要关闭此socket
,这就是短连接
ServerReaderThread extends Thread {private Socket socket;public ServerReaderThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {// 最开始这个地方没有死循环,导致客户端只能发送一次请求// 这个地方加入死循环 是为了即使是一个request也能一直获得用户的输入// 这个地方如果不加入死循环,那么应该建立短连接// 也就是说,客户端发送一个请求,都会建立一个新的socket,服务端处理完这个请求之后,返回给客户端结果,客户端处理完结果之后,关闭socketwhile(true){// 获取输入流InputStream inputStream = socket.getInputStream();// 封装输入流为对象输入流ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);// 读取对象Operation operation = (Operation) objectInputStream.readObject();// 获取操作String operation1 = operation.getOperation();// 获取用户SysUser sysUser = operation.getSysUser();System.out.println("线程方法");switch (operation1) {case "register":register(sysUser,socket);break;case "login":login(sysUser,socket);break;default:break;}}} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}