大三老油条的课堂作业小 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秒连接一次

服务器断线自动重连,可正常继续聊天


Last modification:January 27, 2022
如果觉得我的文章对你有用,请随意赞赏