需求
代码
- 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;
}
@Override
public 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;
}
@Override
public 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){
// 获取客户端的socket
Socket 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;
}
@Override
public void run() {
try {
// 最开始这个地方没有死循环,导致客户端只能发送一次请求
// 这个地方加入死循环 是为了即使是一个request也能一直获得用户的输入
// 这个地方如果不加入死循环,那么应该建立短连接
// 也就是说,客户端发送一个请求,都会建立一个新的socket,服务端处理完这个请求之后,返回给客户端结果,客户端处理完结果之后,关闭socket
while(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();
}
}
}