由于篇幅过长,完整版代码分为上下两篇文章
本篇文章主要是展示服务端的所有代码😊
相比韩顺平老师的服务端代码,我新增了以下几个内容
- 对用户的登录进行了检查,判断是否重复登录或者出现账号不存在的问题。如果登陆失败会返回失败的消息类型给客户端。
- 对于用户发送的消息对象,如果对象不存在就会返回发送消息失败的消息类型给客户端。发送文件也加了这条限制。
- 在最后的离线发送消息扩展也已经完成,并且保证用户能够收到多条离线信息。
- 新增了注册账号的功能。
- 新增了退出账号的功能。
下面开始展示我的代码模块已经各个模块介绍🚚
服务端代码图示🏡
服务端代码
公共部分 -> common
Message消息模块🍧
package com.all.qqcommon;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/03/18:57
* @Description: 消息管理模块
*/
public class Message implements Serializable {
private static final long serializable = 1L;
private String sender; // -- 发送者
private String getter; // -- 接收者
private String content; // -- 发送内容
private String sendTime; // -- 发送时间
private String messageType; // -- 消息类型
// -- 进行文件信息传输扩展
private byte[] fileData; // -- 文件信息
private int fileLength = 0; // -- 文件大小
private String fileFromPath; // -- 文件传输来源地址
private String fileToPath; // -- 文件传输目标地址
public String getSendMessageNowTime() {
// -- 消息发送时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date Date = new Date();
return simpleDateFormat.format(Date);
}
public byte[] getFileData() {
return fileData;
}
public void setFileData(byte[] fileData) {
this.fileData = fileData;
}
public int getFileLength() {
return fileLength;
}
public void setFileLength(int fileLength) {
this.fileLength = fileLength;
}
public String getFileFromPath() {
return fileFromPath;
}
public void setFileFromPath(String fileFromPath) {
this.fileFromPath = fileFromPath;
}
public String getFileToPath() {
return fileToPath;
}
public void setFileToPath(String fileToPath) {
this.fileToPath = fileToPath;
}
public String getMessageType() {
return messageType;
}
public void setMessageType(String messageType) {
this.messageType = messageType;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public String getGetter() {
return getter;
}
public void setGetter(String getter) {
this.getter = getter;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getSendTime() {
return sendTime;
}
public void setSendTime(String sendTime) {
this.sendTime = sendTime;
}
public Message() {
}
public Message(String sender, String content, String sendTime, String messageType) {
this.sender = sender;
this.content = content;
this.sendTime = sendTime;
this.messageType = messageType;
}
}
User用户模块🍧
package com.all.qqcommon;
import java.io.Serializable;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/03/18:56
* @Description: 用户模块
*/
public class User implements Serializable {
/*
* 序列化id (serialVersionUID)
*
* 序列化ID,相当于身份认证,主要用于程序的版本控制,保持不同版本的兼容性,在程序版本升级时避免程序报出版本不一致的错误。
* 如果定义了private static final long serialVersionUID = 1L,那么如果你忘记修改这个信息,而且你对这个类进行修改的话,
* 这个类也能被进行反序列化,而且不会报错。一个简单的概括就是,如果你忘记修改,那么它是会版本向上兼容的。
*
* 如果没有定义一个名为serialVersionUID,类型为long的变量,Java序列化机制会根据编译的class自动生成一个serialVersionUID,
* 即隐式声明。这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。此时如果对某个类进行修改的话,那么版本上面
* 是不兼容的,就会出现反序列化报错的情况。
*
* 在实际的开发中,重新编译会影响项目进度部署,所以我们为了提高开发效率,不希望通过编译来强制划分软件版本,就需要显式地定义一个名
* 为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
* */
private static final long serializable = 1L;
private String userId;// -- 用户Id
private String password;// -- 用户密码
private boolean isRegister; // -- 是否创建新的账号
public User() {
}
public User(String userId, String password) {
this.userId = userId;
this.password = password;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public boolean isRegister() {
return isRegister;
}
public void setRegister(boolean register) {
isRegister = register;
}
// -- 此处序列化要求客户端和服务端 代码完全相同,否则会报错~~~~
}
MessageType消息类型接口🍧
package com.all.qqcommon;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/03/19:13
* @Description: 定义消息类型
*/
public interface MessageType {
String MESSAGE_FILE_MES = "000"; // -- 发送文件信息
String MESSAGE_FILE_MES_FAIL = "007"; // -- 发送文件信息失败
String MESSAGE_COMM_MES = "100"; // -- 普通信息包
String MESSAGE_COMM_MES_FAIL = "101"; // -- 普通信息包发送失败
String MESSAGE_COMM_MES_TO_ALL = "111"; // -- 群发消息
String MESSAGE_LOGIN_SUCCEED = "200"; // -- 表示登录成功
String MESSAGE_LOGIN_EXIST = "202"; // -- 表示退出登录
String MESSAGE_GET_ONLINE_FRIEND = "310"; // -- 获取在线用户列表
String MESSAGE_RET_ONLINE_FRIEND = "311"; // -- 返回在线用户列表
String MESSAGE_GET_REGISTER_REQUEST = "400"; // -- 注册用户请求
String MESSAGE_RET_REGISTER_SUCCEED = "401"; // -- 注册用户成功
String MESSAGE_RET_REGISTER_FAIL = "404"; // -- 注册用户失败
String MESSAGE_LOGIN_FAIL = "500"; // -- 表示登录失败
String MESSAGE_GET_FEED_BACK = "666"; // -- 表示用户发送反馈信息
String MESSAGE_RET_FEED_BACK = "777"; // -- 表示用户收到的反馈信息
String MESSAGE_RET_USER_LOGIN_STATE = "EXIST"; // -- 返回登录用户重复信息
String MESSAGE_CLIENT_EXIT = "888"; // -- 客户端请求退出
}
服务端服务模块 -> service
ManageServerConnectClientThread管理服务端和客户端之间的线程🍧
package com.all.qqserver.service;
import com.all.qqcommon.Message;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/04/16:23
* @Description: 用于管理服务端连接客户端线程的集合
*/
public class ManageServerConnectClientThread {
private static HashMap<String, ServerConnectClientThread> hm = new HashMap<>();
// -- 将线程加入集合中去
public static void addServerConnectClientThread(String userId, ServerConnectClientThread serverConnectClientThread) {
hm.put(userId, serverConnectClientThread);
}
public static HashMap<String, ServerConnectClientThread> getHm() {
return hm;
}
public static void setHm(HashMap<String, ServerConnectClientThread> hm) {
ManageServerConnectClientThread.hm = hm;
}
// -- 通过用户Id获取线程连接
public static ServerConnectClientThread byUserIdGetThread(String userId) {
return hm.get(userId);
}
// -- 返回拼接字符串的在线列表
public static String getOnlineUserIdList() {
Iterator<String> iterator = hm.keySet().iterator();
String onlineUserIdList = "";
while (iterator.hasNext()) {
onlineUserIdList += iterator.next() + " ";
}
return onlineUserIdList;
}
// -- 返回除指定用户的在线列表
public static String[] getOnlineUserIdList(String userId) {
int count = 0;
Iterator<String> iterator = hm.keySet().iterator();
String[] userIdData = new String[hm.size() - 1];
// System.out.println("userId为: " + userId);
while (iterator.hasNext()) {
String otherUserId = iterator.next();
// System.out.println("取出的Id为: " + otherUserId);
if (!otherUserId.equals(userId)) {
userIdData[count++] = otherUserId;
// System.out.println("当前取出的用户Id为: " + userIdData[count - 1] + hm.size());
}
}
return userIdData;
}
// -- 返回所有的在线列表, count为在线数量
public static String[] getOnlineUserIdList(int count) {
Iterator<String> iterator = hm.keySet().iterator();
String[] userIdData = new String[hm.size()];
while (iterator.hasNext()) {
String otherUserId = iterator.next();
userIdData[count++] = otherUserId;
}
return userIdData;
}
// -- 判断在线列表中是否有该用户名
public static boolean isExistOnlineUserId(String userId) {
String[] userIdData = getOnlineUserIdList(0);
for (int i = 0; i < userIdData.length; i++) {
if (userIdData[i].equals(userId)) {
return true;
}
}
return false;
}
// -- 移除已经退出的用户
public static void removeExitUserId(String userId) {
hm.remove(userId);
}
// -- 检查离线消息中是否有新登录用户的消息
public static boolean checkOfflineUserMassage(String userId) {
return ServerConnect.getOfflineCacheDB().containsKey(userId);
}
}
SendNewsToAllUser服务端新闻发布(大喇叭)🍧
package com.all.qqserver.service;
import com.all.qqcommon.Message;
import com.all.qqcommon.MessageType;
import com.all.qqserver.utils.readOperate;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/06/18:29
* @Description: 发送消息给所有用户
*/
public class SendNewsToAllUser implements Runnable {
// -- TODO 这里新闻推送消息可以更改为离线人员也可以收到信息
@Override
public void run() {
while (true) {
System.out.println("请输入你想要推送的新闻: 输入[exit]退出推送服务");
String News = readOperate.readString(100);
if (News.equals("exit")) {
System.out.println("推送服务已经关闭!");
break;
}
Message message = new Message();
message.setSender("服务器");
message.setSendTime(message.getSendMessageNowTime());
message.setMessageType(MessageType.MESSAGE_COMM_MES_TO_ALL);
message.setContent(News);
// -- 遍历所有在线的用户
String[] userIdData = ManageServerConnectClientThread.getOnlineUserIdList(0);
for (int i = 0; i < userIdData.length; i++) {
// -- 将消息转发给对方
try {
ObjectOutputStream oos
= new ObjectOutputStream(ManageServerConnectClientThread.byUserIdGetThread(userIdData[i]).getSocket().getOutputStream());
oos.writeObject(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
ServerConnect服务端监听模式启动🍧
package com.all.qqserver.service;
import com.all.qqcommon.Message;
import com.all.qqcommon.MessageType;
import com.all.qqcommon.User;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/04/16:00
* @Description: 这里需要和客户端保持连接 端口号为 4399
*/
@SuppressWarnings({"all"})
public class ServerConnect {
private ServerSocket ss;
// -- 创建一个集合用来存放用户数据
// -- ConcurrentHashMap可以在并发的情况下处理线程问题
// -- HashMap 会有线程安全问题存在
private static ConcurrentHashMap<String, User> userData = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, ArrayList<Message>> offlineCacheDB = new ConcurrentHashMap<>();
static {
// -- 用静态代码块来初始化集合
userData.put("admin", new User("admin", "123456"));
userData.put("tom", new User("tom", "541688"));
userData.put("张三", new User("张三", "zhangsan"));
userData.put("bear", new User("bear", "bear"));
userData.put("思思", new User("思思", "beautifulGirl"));
}
public ServerConnect() {
try {
System.out.println("服务端正在4399端口进行监听");
// -- 启动推送服务
new Thread(new SendNewsToAllUser()).start();
// -- 开启端口监听服务
ss = new ServerSocket(4399);
// -- 监听服务是循环的,并不是监听一次就退出
while (true) {
Socket socket = ss.accept();
// -- 得到socket的对象输入流和输出流
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
User user = (User) ois.readObject();
// -- 创建一个Message对象进行信息回复
Message message = new Message();
// System.out.println("是否接收到了message" + message.getMessageType());
// -- 对客户端用户是否重复登录进行判断
if (user.isRegister()) {
if (userData.containsKey(user.getUserId())) {
// -- 表示该用户名已经存在
System.out.println("用户" + user.getUserId() + "注册账号失败");
message.setMessageType(MessageType.MESSAGE_RET_REGISTER_FAIL);
message.setSendTime(message.getSendMessageNowTime());
message.setContent("不能重复创建,因为该用户名已经存在!");
oos.writeObject(message);
socket.close();
continue;
} else {
System.out.println("用户" + user.getUserId() + "注册账号成功");
userData.put(user.getUserId(), new User(user.getUserId(), user.getPassword()));
message.setMessageType(MessageType.MESSAGE_RET_REGISTER_SUCCEED);
message.setSendTime(message.getSendMessageNowTime());
message.setSender(user.getUserId());
message.setContent(user.getPassword());
oos.writeObject(message);
socket.close();
continue;
}
}
if (!IsExistUserLoginInfo(user.getUserId()) && !user.isRegister()) {
// -- 该用户已经登录过,无法再次登录
System.out.println("用户" + user.getUserId() + "重复登陆");
message.setMessageType(MessageType.MESSAGE_RET_USER_LOGIN_STATE);
oos.writeObject(message);
socket.close();
continue;
}
// -- 对客户端发来的用户对象进行验证
if (checkUser(user.getUserId(), user.getPassword()) && !user.isRegister()) {
message.setMessageType(MessageType.MESSAGE_LOGIN_SUCCEED);
// -- 回复信息给客户端
oos.writeObject(message);
// -- 此时应该创建一个线程和客户端进行通信
ServerConnectClientThread serverConnectClientThread = new ServerConnectClientThread(user.getUserId(), socket);
serverConnectClientThread.start();
// -- 将线程对象放入集合中
ManageServerConnectClientThread.addServerConnectClientThread(user.getUserId(), serverConnectClientThread);
// -- 检查离线消息
if (ManageServerConnectClientThread.checkOfflineUserMassage(user.getUserId())) {
System.out.println("检测到用户登录!");
serverConnectClientThread.sendOfflineMassage(serverConnectClientThread);
}
} else {
System.out.println("用户" + user.getUserId() + "登录失败");
message.setMessageType(MessageType.MESSAGE_LOGIN_FAIL);
oos.writeObject(message);
// -- 关闭socket
socket.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// -- 如果服务端退出while循环,则退出serverSocket
try {
ss.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
// -- 判断用户是否重复登录
private boolean IsExistUserLoginInfo(String userId) {
// String Info = ManageServerConnectClientThread.getOnlineUserIdList();
// // -- 如果返回的信息为空,则直接为true
// if (Info.equals("")) {
// return true;
// }
// String[] userInfos = Info.split(" ");
// for (int i = 0; i < userInfos.length; i++) {
// if (userId.equals(userInfos[i])) {
// return false;
// }
// }
// -- 更换为新方法
if (ManageServerConnectClientThread.isExistOnlineUserId(userId)) {
return false;
}
return true;
}
// -- 对用户的账号和密码进行check
private boolean checkUser(String userId, String password) {
User user = userData.get(userId);
if (user == null || !user.getPassword().equals(password)) {
return false;
}
return true;
}
// -- 将离线消息进行存储
public static void setUserOffonlineMessage(String userId, ArrayList<Message> messageArrayList) {
// Message[] messages = new Message[messageInfo.size()];
// for (int i = 0; i < messages.length; i++) {
// Message message = new Message();
// for (int j = 0; j < messageInfo.size(); j++) {
// message.setSender(messageInfo.get(i));
// }
// messages[i] = new Message(messageInfo.get(i),)
// }
offlineCacheDB.put(userId, messageArrayList);
}
// -- 在离线数据库中移除离线消息
public static void removeUserOffonlineMessage(String userId) {
offlineCacheDB.remove(userId);
}
// -- 判断集合中是否有该用户名
public static boolean isExistUserId(String userId) {
return userData.containsKey(userId);
}
public static ConcurrentHashMap<String, ArrayList<Message>> getOfflineCacheDB() {
return offlineCacheDB;
}
public static void setOfflineCacheDB(ConcurrentHashMap<String, ArrayList<Message>> offlineCacheDB) {
ServerConnect.offlineCacheDB = offlineCacheDB;
}
public ServerConnect(ServerSocket socket) {
this.ss = socket;
}
}
ServerConnectClientThread服务端与客户端多线程启动🍧
package com.all.qqserver.service;
import com.all.qqcommon.Message;
import com.all.qqcommon.MessageType;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/04/16:14
* @Description: 服务端与客户端进行连接的线程
*/
public class ServerConnectClientThread extends Thread {
private Socket socket;
private String userId;// -- 与哪个用户进行连接
private static ArrayList<Message> messageArrayList = new ArrayList<>();
public ServerConnectClientThread(String userId, Socket socket) {
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 = (Message) ois.readObject();
// -- 客户端要拉取当前在线列表
if (message.getMessageType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)) {
System.out.println(message.getSender() + " 要求返回在线列表");
// -- 通过管理线程获取用户在线列表,进行消息封装和填充消息类型
String onlineUserIdList = ManageServerConnectClientThread.getOnlineUserIdList();
Message onlineUserMessage = new Message();
onlineUserMessage.setMessageType(MessageType.MESSAGE_RET_ONLINE_FRIEND);
onlineUserMessage.setContent(onlineUserIdList);
onlineUserMessage.setSender(message.getSender());
// -- 然后通过socket输出流对象返回信息
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(onlineUserMessage);
}
if (message.getMessageType().equals(MessageType.MESSAGE_COMM_MES)) {
System.out.println(message.getSender() + "要发送消息给" + message.getGetter());
// -- 先判断对象是否存在
if (ServerConnect.isExistUserId(message.getGetter())) {
// -- 判断对方是否在线
if (ManageServerConnectClientThread.isExistOnlineUserId(message.getGetter())) {
// -- 先获得发送对象的线程
ServerConnectClientThread serverConnectClientThread
= ManageServerConnectClientThread.byUserIdGetThread(message.getGetter());
// -- 将消息转发给对方
ObjectOutputStream oos
= new ObjectOutputStream(serverConnectClientThread.socket.getOutputStream());
oos.writeObject(message);
} else {
// -- TODO 如果对方不在线,可以保存在数据库,这里暂时用了HashMap来模拟数据库
// -- 对方并不在线,先将信息存储至集合中
messageArrayList.add(message);
ServerConnect.setUserOffonlineMessage(message.getGetter(), messageArrayList);
}
} else {
message.setMessageType(MessageType.MESSAGE_COMM_MES_FAIL);
message.setSendTime(message.getSendMessageNowTime());
message.setContent("无此用户!");
ObjectOutputStream oos
= new ObjectOutputStream(ManageServerConnectClientThread.byUserIdGetThread(message.getSender()).socket.getOutputStream());
// -- 将消息进行退回
oos.writeObject(message);
}
}
if (message.getMessageType().equals(MessageType.MESSAGE_COMM_MES_TO_ALL)) {
String[] othersUserIdList = ManageServerConnectClientThread.getOnlineUserIdList(userId);
for (int i = 0; i < othersUserIdList.length; i++) {
// -- 先获得发送对象的线程
ServerConnectClientThread serverConnectClientThread
= ManageServerConnectClientThread.byUserIdGetThread(othersUserIdList[i]);
// -- 将消息转发给对方
ObjectOutputStream oos
= new ObjectOutputStream(serverConnectClientThread.socket.getOutputStream());
oos.writeObject(message);
}
}
if (message.getMessageType().equals(MessageType.MESSAGE_FILE_MES)) {
if (ServerConnect.isExistUserId(message.getGetter())) {
ObjectOutputStream oos
= new ObjectOutputStream(ManageServerConnectClientThread.byUserIdGetThread(message.getGetter()).socket.getOutputStream());
// -- 将文件进行转发
oos.writeObject(message);
} else {
message.setMessageType(MessageType.MESSAGE_FILE_MES_FAIL);
message.setSendTime(message.getSendMessageNowTime());
message.setContent("无此用户!");
ObjectOutputStream oos
= new ObjectOutputStream(ManageServerConnectClientThread.byUserIdGetThread(message.getSender()).socket.getOutputStream());
// -- 将文件进行退回
oos.writeObject(message);
}
}
if (message.getMessageType().equals(MessageType.MESSAGE_GET_FEED_BACK)) {
System.out.println("用户" + message.getSender() + "反馈的内容为: "
+ message.getContent() + "\n如果需要反馈,可以用大喇叭!");
}
if (message.getMessageType().equals(MessageType.MESSAGE_LOGIN_EXIST)) {
System.out.println("用户" + message.getSender() + "已经退出登录!");
ObjectOutputStream oos
= new ObjectOutputStream(ManageServerConnectClientThread.byUserIdGetThread(message.getSender()).socket.getOutputStream());
// -- 将消息发送回去
oos.writeObject(message);
ManageServerConnectClientThread.removeExitUserId(message.getSender());
// ManageServerConnectClientThread.byUserIdGetThread(message.getSender()).socket.close();
break;
}
if (message.getMessageType().equals(MessageType.MESSAGE_CLIENT_EXIT)) {
System.out.println("用户" + message.getSender() + "已经退出系统!");
ManageServerConnectClientThread.removeExitUserId(message.getSender());
/*
* TODO 这里出现了BUG,如果关闭socket就会出现异常
* , 但并不影响程序运行,如果不关闭就不会出现
* 异常,但并不合理.
* 最新的bug为不关闭socket没事,开启socket还会报错!
*/
// ManageServerConnectClientThread.byUserIdGetThread(message.getSender()).socket.close();
break;
}
/*
TODO 这里可以新增一个T出用户选项,需要遍历一遍在线用户(方法已经实现过了)
思路就是将这个用户从集合中删除并且关闭线程,发送给客户端消息,并且在客户端
将View中的outlook给改变,然后强制输入一个key选项(不给客户看的特殊空选项)
发送回客户端一个消息,提示已经被T出,整体实现很简单,唯一需要注意的就是大喇叭模式的兼容
*/
} catch (Exception e) {
throw new RuntimeException();
}
}
}
// -- 发送离线消息
public void sendOfflineMassage(ServerConnectClientThread serverConnectClientThread) {
// ConcurrentHashMap<String, Message> concurrentHashMap = ServerConnect.getOfflineCacheDB();
ConcurrentHashMap<String, ArrayList<Message>> concurrentHashMap = ServerConnect.getOfflineCacheDB();
// -- 先获得存储的所有离线消息
ArrayList<Message> sendMessage = concurrentHashMap.get(userId);
Message[] messages = new Message[sendMessage.size()];
for (int i = 0; i < sendMessage.size(); i++) {
messages[i] = sendMessage.get(i);
}
try {
// -- 将消息转发给对方
for (int i = 0; i < messages.length; i++) {
ObjectOutputStream oos = new ObjectOutputStream(serverConnectClientThread.socket.getOutputStream());
oos.writeObject(messages[i]);
ServerConnect.removeUserOffonlineMessage(userId);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
}
QQFrame服务端主程序入口🍧
package com.all.qqserver.qqFrame;
import com.all.qqserver.service.ServerConnect;
/**
* Created with IntelliJ IDEA.
*
* @Author: 捶捶自己
* @version: 1.0
* @Date: 2022/07/04/16:45
* @Description: 启动QQ 后台服务程序
*/
public class QQFrame {
public static void main(String[] args) {
new ServerConnect();
}
}
readOperate 工具输入类🍧 工具类我新增了一些代码,有200行之多,放到了另外一篇文章中。这篇文章实在太长了!!!
其中服务端存储的初始化用户账号和密码截图如下:
小总结:
写这个网络编程的项目作业,爆红了不知道多少次,修修改改好多天。总归是完成了自己的功能增加和整个项目。o( ̄▽ ̄)ブ