请注意,本文编写于 633 天前,最后修改于 497 天前,其中某些信息可能已经过时。
大三老油条的课堂作业小 Demo,都是基础的代码,这里我单独维护了 Socket ,让线程更加安全可靠,可以群聊可以私聊可以断线重连。代码本身还是挺好的吧,就是设计这一块可能有点乱七八糟,好在没几行代码,看看就懂了,作业嘛能跑就行,至于为什么那么多 if ,别问,我也不知道,改着改着 bug,就出来这么多了,可能是因为 while(true)
吧。代码有必要的注释,想学习的可以看看,该捕获的异常该处理的都处理了。
public class ChatChannel implements Runnable {
//维护一个所有客户端的输出流,用于转发消息
static Map<String, ObjectOutputStream> allClientOos = new HashMap<>();
//接入的客户端socket
private final Socket socket;
//接入的客户端昵称
private String name;
//当前进来的客户端的输入流
private ObjectInputStream ois;
//当前进来的客户端的输出流
private ObjectOutputStream oos;
//首次连接服务端
private boolean first = true;
//当前客户端连接是否被关闭
private boolean currentClientCloesed = false;
public ChatChannel(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
while (true) {
try {
//首次连接服务端
if (first) {
ois = new ObjectInputStream(socket.getInputStream());
oos = new ObjectOutputStream(socket.getOutputStream());
//用户名
name = (String) ois.readObject();
//收集
allClientOos.put(name, oos);
System.out.println(name + ",进入聊天室!");
//群发消息,通知每个用户
for (ObjectOutputStream oos : allClientOos.values()) {
oos.writeObject("系统消息:" + name + ",进入聊天室");
oos.flush();
}
first = false;
currentClientCloesed = false;
}
if (!currentClientCloesed) {
//获取消息
String msg = (String) ois.readObject();
System.out.println("@" + name + ":" + msg);
//判断私聊,私聊具体对象
if (msg.startsWith("@") && msg.contains("#")) {
//私聊规则:@昵称#内容
String toName = msg.substring(1, msg.indexOf("#"));
msg = msg.substring(msg.indexOf("#") + 1);
if (allClientOos.containsKey(toName)) {
allClientOos.get(toName).writeObject("@" + name + "悄悄跟你说:" + msg);
} else {
oos.writeObject("系统消息:该用户未在线");
}
} else {
for (ObjectOutputStream oos : allClientOos.values()) {
oos.writeObject("@" + name + ":" + msg);
}
}
oos.flush();
}
} catch (Exception e) {
try {
if (name != null) {
System.out.println(name + ",离开聊天室!");
//关闭当前客户端
socket.close();
currentClientCloesed = true;
allClientOos.remove(name);
for (ObjectOutputStream oos : allClientOos.values()) {
oos.writeObject("系统消息:" + name + "离开聊天室");
}
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}
}
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* @author 乐心湖
* @version 1.0
* @date 2021/9/8 13:15
*/
public class ClientRecv implements Runnable {
private ObjectInputStream ois;
private boolean first = true;
@Override
public void run() {
while (true) {
try {
if (first || MyClientSocket.isOisUpdated()) {
//更新输入流
ois = new ObjectInputStream(MyClientSocket.getSocket().getInputStream());
first = false;
MyClientSocket.setOisUpdated(false);
}
if (!MyClientSocket.isOisUpdated()) {
try {
System.out.println(ois.readObject());
} catch (IOException | ClassNotFoundException e) {
//算是掉线了吧。。。不知道
System.out.println("服务器掉线了,正在重连中");
MyClientSocket.setSocket(ConnectedUtils.getConnection());
this.first = true;
System.out.println("重新连接上了,请按下回车键确定");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
import java.io.ObjectOutputStream;
import java.util.Scanner;
/**
* @author 乐心湖
* @version 1.0
* @date 2021/9/8 13:18
*/
public class ClientSend implements Runnable {
private ObjectOutputStream oos;
private boolean first = true;
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您的昵称:");
String name = scanner.nextLine();
while (true) {
try {
//首次发送或重连服务端
if (first || MyClientSocket.isOosUpdated()) {
//更新输出流
oos = new ObjectOutputStream(MyClientSocket.getSocket().getOutputStream());
oos.writeObject(name);
first = false;
MyClientSocket.setOosUpdated(false);
}
String msg = scanner.nextLine();
//消息为空则无视,流被更新了也无视
if (!MyClientSocket.isOosUpdated() && msg.trim().length() > 0) {
oos.writeObject(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
import java.net.Socket;
/**
* 一个连接服务端的工具类
* @author 乐心湖
* @version 1.0
* @date 2021/9/8 8:52
*/
public class ConnectedUtils {
public static Socket getConnection() throws InterruptedException {
//ConnectedUtils connectedUtils = new ConnectedUtils();
Socket socket = null;
while (socket == null) {
try {
System.out.println("正在连接服务器");
socket = new Socket("localhost", 9900);
} catch (Exception e) {
socket = null;
//连接不上,5秒重连
Thread.sleep(5000);
MyClientSocket.setOisUpdated(true);
MyClientSocket.setOosUpdated(true);
}
}
return socket;
}
}
服务端
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 乐心湖
* @version 1.0
* @date 2021/8/30 9:20
*/
public class ChatServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(9900);
while (true) {
//接受到的客户端,传给聊天通道
Socket socket = serverSocket.accept();
new Thread(new ChatChannel(socket),"聊天线程").start();
}
}
}
客户端
import java.net.Socket;
/**
* @author 乐心湖
* @version 1.0
* @date 2021/8/30 9:29
*/
public class ChatClient {
public static void main(String[] args) throws Exception {
Socket client = ConnectedUtils.getConnection();
MyClientSocket.setSocket(client);
new Thread(new ClientRecv(),"接受消息线程").start();
new Thread(new ClientSend(),"发送消息线程").start();
}
}
群聊和私聊演示
服务器未启动,5秒连接一次
服务器断线自动重连,可正常继续聊天
版权属于:乐心湖's Blog
本文链接:https://xn2001.com/archives/664.html
声明:博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!