不说废话,先看效果,目前实现的基本功能有一对一私聊。一对多群聊。
1、先启动服务端,等待客户端连接…。
在这里插入图片描述
2、启动三个客户端,代表不同的用户。右上角分别选择不同的用户进行登录。
在这里插入图片描述
在这里插入图片描述
3、登录的用户可对所有用户发送消息,也可以对某个用户发送消息。
在这里插入图片描述
下面是具体的代码实现(采用传统的BIO实现,也可参考我另一篇博客NIO实现其中的功能)。
server端代码:

package Server;

import java.io.*;
import java.net.*;
import java.util.*;

/*
server端
 */


public class ChatServer {
    private ServerSocket ss;
    private boolean flag = false;
    private Map<String, Client> clients = new HashMap<>();

    public static void main(String args[]) {
        new ChatServer().start();
    }

    public void start() {
        try {
            ss = new ServerSocket(8888);
            flag = true;
            System.out.println("Server启动成功...");
        } catch (BindException e) {
            System.out.println("端口使用中!!!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            //接收客户端的连接
            while (flag) {
                //accept方法是一个阻塞的方法,会阻塞当前的线程。
                Socket socket = ss.accept();
                Client c = new Client(socket);
                System.out.println("一个客户端已经连接...");
                new Thread(c).start();
            }
        } catch (IOException e) {
            System.out.println("Client closed...");
        } finally {
            try {
                if (ss != null)
                    ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    private class Client implements Runnable {
        private Socket s;
        private String userName = "";
        private DataInputStream input = null;
        private DataOutputStream output = null;
        private boolean connected = false;
        private BufferedOutputStream fout = null;
        private String saveFilePath = "";
        public Client(Socket s) {
            this.s = s;
            try {
                input = new DataInputStream(s.getInputStream());
                output = new DataOutputStream(s.getOutputStream());
                connected = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        @Override
        public void run() {
            byte buffer[] = new byte[1024];
            int len = 0;
            try {
                while (connected) {
                    String msg[] = input.readUTF().split("#");//msg={"  ","登录用户","目标用户","要发送的信息"}
                    switch (msg[0]) {
                        case "LOGIN":
                            String userName = msg[1];
                            if (clients.containsKey(userName)) {
                                output.writeUTF("FAIL");//已经登录
                                System.out.println("拒绝了一个重复连接...");
                                closeConnect();
                            } else {
                                output.writeUTF("SUCCESS");
                                clients.put(userName, this);
                                //将所有登录用户信息发送给新登录的用户
                                StringBuffer allUsers = new StringBuffer();
                                allUsers.append("ALLUSERS#");
                                for (String user : clients.keySet())
                                    allUsers.append(user + "#");
                                output.writeUTF(allUsers.toString());
                                //将新登录的用户信息发送给其他用户
                                String newLogin = "LOGIN#" + userName;
                                sendMsg(userName, newLogin);
                                this.userName = userName;
                            }
                            break;
                        case "LOGOUT":
                            clients.remove(this.userName);
                            String logoutMsg = "LOGOUT#" + this.userName;
                            sendMsg(this.userName, logoutMsg);
                            System.out.println("用户" + this.userName + "已下线...");
                            closeConnect();
                            break;
                        case "SENDONE":
                            Client c = clients.get(msg[1]);//获取目标用户的连接
                            String msgToOne="";
                            if (c != null) {
                                msgToOne="SENDONE#" + this.userName + "#" + msg[2];
                                c.output.writeUTF(msgToOne);
                                c.output.flush();
                            }
                            break;
                        case "SENDALL":
                            String msgToAll = "";
                            msgToAll = "SENDALL#" + this.userName + "#" + msg[1];
                            sendMsg(this.userName, msgToAll);
                            break;

                    }

                }
            } catch (IOException e) {
                System.out.println("Client closed...");
                connected = false;
            } finally {

                try {
                    if (input != null)
                        input.close();
                    if (output != null)
                        output.close();
                    if (fout != null)
                        fout.close();
                    if (s != null)
                        s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

        public void closeConnect() {
            connected = false;
            try {
                if (input != null)
                    input.close();
                if (output != null)
                    output.close();
                if (s != null)
                    s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void sendMsg(String fromUser, String msg) {
            String tempUser = "";
            try {
                for (String toUser : clients.keySet()) {
                    if (!toUser.equals(fromUser)) {
                        tempUser = toUser;
                        DataOutputStream out = clients.get(toUser).output;
                        out.writeUTF(msg);
                        out.flush();
                    }
                }
            } catch (IOException e) {
                System.out.println("用户" + tempUser + "已经离线!!!");
            }
        }
    }
}


客户client端:

package Client;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
/*
    客户client端
 */

public class ChatClient extends JFrame {
    private JTextArea sendArea, contentArea;
    JPanel p1, p11, p12, p2, p21, p22;
    JComboBox<String> user1, user2;
    private RecvThread clientThread = null;
    private String filePath = null;

    public void start() {
        Container con = getContentPane();
        con.setLayout(new BorderLayout());
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        int screenWidth = screenSize.width / 2;
        int screenHeight = screenSize.height / 2;
        int height = getHeight();
        int width = getWidth();
        setSize(350, 400);
        setLocation((screenWidth - width) / 2, (screenHeight - height) / 2);
        sendArea = new JTextArea(3, 10);//设置发送区域几行几列
        sendArea.setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
        sendArea.setLineWrap(true);
        sendArea.setWrapStyleWord(true);//激活断行不断字功能
        sendArea.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if ('\n' == e.getKeyCode())
                    if (clientThread != null) {
                        clientThread.sendMsg();
                    }
            }
        });
        contentArea = new JTextArea(6, 10);
        contentArea.setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
        //北边接收消息区域
        p1 = new JPanel();
        p1.setLayout(new BorderLayout());
        p11 = new JPanel();
        p11.setLayout(new GridLayout(1, 2));
        JLabel l1 = new JLabel("选择你的身份:");
        user1 = new JComboBox<>();
        user1.addItem("-----选择-----");
        user1.addItem("张三");
        user1.addItem("李四");
        user1.addItem("王五");
        user1.addItem("小六");

        user1.addItemListener(e -> {
            if (e.getStateChange() == ItemEvent.SELECTED) {
//                    System.out.println("选中的项:" + user1.getSelectedItem());
                if (user1.getSelectedIndex() == 0) {
                    return;
                }
                clientThread = new RecvThread((String) user1.getSelectedItem());
                new Thread(clientThread).start();
            }
        });
        p11.add(l1);
        p11.add(user1);
        p12 = new JPanel();
        p12.setLayout(new GridLayout(1, 1));
        p12.add(new JScrollPane(contentArea));
        p1.add(p11, BorderLayout.NORTH);
        p1.add(p12, BorderLayout.SOUTH);
        //南边发送消息区域
        p2 = new JPanel();
        p2.setLayout(new BorderLayout());
        p21 = new JPanel();
        p21.setLayout(new GridLayout(2, 2));
        user2 = new JComboBox<>();
        user2.addItem("所有用户");
        JLabel l2 = new JLabel("选择要发送的用户:");

        p21.add(l2);
        p21.add(user2);
        p22 = new JPanel();
        p22.setLayout(new GridLayout(1, 1));
        p22.add(new JScrollPane(sendArea));
        p2.add(p21, BorderLayout.NORTH);
        p2.add(p22, BorderLayout.SOUTH);
        con.add(p1, BorderLayout.NORTH);
        con.add(p2, BorderLayout.SOUTH);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                if (clientThread != null)
                    clientThread.exit();
                System.exit(0);
            }
        });
    }

    private class RecvThread implements Runnable {
        private Socket s = null;
        private DataInputStream in = null;
        private DataOutputStream out = null;
        private String userName;
        private boolean isLogin = false;
        StringBuilder msg = new StringBuilder();

        public RecvThread(String userName) {
            this.userName = userName;
        }

        //用户登录
        public void login() {
            try {
                s = new Socket("127.0.0.1", 8888);//监听本机的8888端口
                in = new DataInputStream(s.getInputStream());
                out = new DataOutputStream(s.getOutputStream());
                String sendMsg = "LOGIN#" + userName;
                out.writeUTF(sendMsg);
                out.flush();
                //服务器返回的信息
                String recv = in.readUTF();
                if (recv.equals("FAIL")) {
                    showMsg(userName + "已经登录!!!");
                    user1.setEnabled(true);
                    exit();
                    return;
                } else if (recv.equals("SUCCESS")) {
                    showMsg("登录成功!!!");
                    user1.setEnabled(false);
                    isLogin = true;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void exit() {//用户退出,释放资源
            try {
                if (isLogin) {
                    out.writeUTF("LOGOUT");
                    out.flush();
                    isLogin = false;
                }
                if (in != null)
                    in.close();
                if (out != null)
                    out.close();
                if (s != null)
                    s.close();

            } catch (IOException e) {
                System.out.println("连接已关闭!!!");
            }
        }

        //用户发送消息
        public void sendMsg() {
            int len = 0;
         //   String fileName = "";
            if (!isLogin) {
                showMsg("没有登录,请登录!!!");
                return;
            }
            msg.setLength(0);
            String sendInfo = sendArea.getText().trim();
            String user = (String) user2.getSelectedItem();
            if(sendInfo.equals(""))
                sendInfo=" ";
            try {

                if (user.equals("所有用户")) {//给所有用户发送消息
                    msg.append("SENDALL#");

                        msg.append(sendInfo);


                } else {//只给某个用户发送消息
                    msg.append("SENDONE#");
                        msg.append(user+"#" + sendInfo);//如果这里sendinfo=SENDFILE那么可能就会发生错误。有待改进....

                }
                out.writeUTF(msg.toString());
                showMsg("我说:" + sendInfo);

                sendArea.setText("");

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            login();
            try {
                while (isLogin) {
                    String msgs[] = in.readUTF().split("#");
                    switch (msgs[0]) {
                        case "LOGIN":
                            user2.addItem(msgs[1]);//后面可以加个提示信息
                            break;
                        case "ALLUSERS":
                            for (int i = 1; i < msgs.length; i++) {
                                if (!"".equals(msgs[i]))
                                    user2.addItem(msgs[i]);
                            }
                            break;
                        case "SENDONE":

                                showMsg(msgs[1] + ":" + msgs[2]);
                            break;
                        case "SENDALL":

                                showMsg(msgs[1] + "对所有人说:" + msgs[2]);

                            break;
                        case "LOGOUT":
                            showMsg("用户" + msgs[1] + "已下线!!!");
                            user2.removeItem(msgs[1]);
                            break;
                    }
                }
            } catch (SocketException e) {
                System.out.println(userName + "已退出...");
            } catch (IOException e) {
                isLogin = false;//返回数据出现异常,退出登录
                e.printStackTrace();
            }
        }
    }

    //设置显示信息
    public void showMsg(String msg) {
        contentArea.append(msg + "\n");
        contentArea.setCaretPosition(contentArea.getText().length());// 自动滚动到文本区的最后一行
    }


    public static void main(String args[]) {
        //将swing风格控件渲染成Windows风格
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        new ChatClient().start();
    }
}

上面代码我都放在gitte上面,需要的自己去拿聊天室源代码

Logo

致力于链接即构和开发者,提供实时互动和元宇宙领域的前沿洞察、技术分享和丰富的开发者活动,共建实时互动世界。

更多推荐