QQServer
package com.qqserver.service;
import com.qqcommon.Message;
import com.qqcommon.MessageType;
import com.qqcommon.User;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @Author: Gin
* @Description: 服务端,监听 9999 端口,等待客户端连接并保持通信
* @Modified By: Gin
* @Date: Created in 14:12 2021/10/14
*/
public class QQServer {
private ServerSocket serverSocket = null;
public QQServer() {
// 注意:端口可以写在配置文件
try {
System.out.println("服务端在 9999 端口监听");
serverSocket = new ServerSocket(9999);
// 当和某个客户端连接后会继续监听
while(true){
// 如果没有客户端连接,就会在这里阻塞
Socket socket = serverSocket.accept();
// 得到和 socket 相关的对象输入流
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
// 得到 socket 相关的对象输出流
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
// 读取客户端发送的 user 对象
User user = (User) ois.readObject();
// 创建一个 Message 对象,准备回复客户端
Message message = new Message();
// 验证
if("Gin".equals(user.getUserId()) && "123".equals(user.getPasswd())){ // 登陆通过
message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);
// 将 message 对象回复给客户端
oos.writeObject(message);
// 创建一个线程,和客户端保持通信,该线程需要持有 Socket 对象
ServerConnectClientThread thread = new ServerConnectClientThread(socket, user.getUserId());
// 启动线程
thread.start();
// 把该集合对象放入到一个集合中进行管理
ManageServerConnectClientThread.addServerConnectClientThread(user.getUserId(), thread);
}else{ // 登陆失败
message.setMesType(MessageType.MESSAGE_LOGIN_FAIL);
oos.writeObject(message);
socket.close(); // 登陆失败时要关闭 socket
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 如果服务器端退出 while 循环,说明服务器不再监听,因此需要关闭 ServerSocket
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ServerConnectClientThread
package com.qqserver.service;
import com.qqcommon.Message;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
/**
* @Author: Gin
* @Description: 该类的一个对象和某个客户端保持通信
* @Modified By: Gin
* @Date: Created in 14:23 2021/10/14
*/
public class ServerConnectClientThread extends Thread{
private Socket socket;
private String userId; // 连接到服务端的用户 id
public ServerConnectClientThread(Socket socket, String userId) {
this.socket = socket;
this.userId = userId;
}
@Override
public void run() {
while(true){
try {
// 服务端线程,等待读取从客户端发送过来的消息
System.out.println("客户端" + userId + "和服务端保持通信,读取数据...");
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
// 如果服务端没有发送 Message 对象,则线程在这里会阻塞
Message msg = (Message) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
ManageServerConnectClientThread
package com.qqserver.service;
import java.util.HashMap;
/**
* @Author: Gin
* @Description: 该类用于管理和客户端通信的线程
* @Modified By: Gin
* @Date: Created in 14:56 2021/10/14
*/
public class ManageServerConnectClientThread {
private static HashMap<String, ServerConnectClientThread> map = new HashMap<>();
public static void addServerConnectClientThread(String userId, ServerConnectClientThread thread){
map.put(userId, thread);
}
// 根据 userId 返回 ServerConnectClientThread 线程
public static ServerConnectClientThread getServerConnectClientThread(String userId){
return map.get(userId);
}
}
在服务器端创建一个启动类
package com.qqframe;
import com.qqserver.service.QQServer;
/**
* @Author: Gin
* @Description: 该类创建 QQServer 对象,启动后台服务
* @Modified By: Gin
* @Date: Created in 15:18 2021/10/14
*/
public class QQFrame {
public static void main(String[] args) {
new QQServer();
}
}
验证:先启动服务端,在启动客户端(默认的用户名:Gin,密码:123)
用户名密码错误情况:
正确的用户名密码