一,常见问题

二,解决方法

三,总结

一,常见问题

在之前实现控制台聊天时,我们主要解决了如何通过 @ 符号区分广播和私聊。但是我们在真正使用时不可能使用控制台,于是会有下面这些问题:

  1. 如何判断一个客户端连接是执行登录还是注册,如何确保服务器能准确记录每个用户的用户名并将其与 Socket 绑定。

  2. 如何将原本在控制台通过Scanner 获取的字符串,通过 Swing 窗体的按钮触发并发送出去。

于是我们需要设计一套基础的接口来实现一个简单协议。

二,解决方法

我们定义了一个接口 MassageType,通过简单的字节码来标记动作类型。

package Contect260130.sever;

public interface MassageType {
    public static final byte LOGIN=2;    // 登录请求
    public static final byte REGISTER=3; // 注册请求
    public static final byte SUCCESS=1;  // 操作成功
    public static final byte FALSE=0;    // 操作失败
    public static final byte CHAT=4;     // 聊天消息
}

在服务器端的 ServerThread 中,编写了 chickMsg 方法。服务器会先读取一个字节的 action,如果是登录则比对 uMsg 中的密码,如果是注册则存入 uMsg

    public String chickMsg(InputStream is,OutputStream os,Map<String,String> uMsg) throws IOException {
        while(true){
            int action=is.read();
            if(action==MassageType.LOGIN){
                System.out.println("用户选择登录");
                String name=readString(is);
                String password=readString(is);
                String pass=uMsg.get(name);
                //判断用户是否存在且密码正确
                if(pass==null||pass.equals(password)){
                    os.write(MassageType.FALSE);
                    os.flush();
                }else {
                    os.write(MassageType.SUCCESS);
                    os.flush();
                    return name;
                }
            } else if (action==MassageType.REGISTER) {
                //将新用户存入map
                String name=readString(is);
                String password=readString(is);
                uMsg.put(name,password);
                os.write(MassageType.SUCCESS);
                os.flush();
                return name;
            }
        }
    }

CFrame 界面中,我们不再需要 Scanner来在控制台进行消息的输入,通过为“发送”按钮添加 ActionListener,直接从 JTextField 获取文本。

        sendBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String sendMsg = sendText.getText(); 
                try {
                    writeString(client.client.getOutputStream(),sendMsg);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
        });

当用户通过校验后,其用户名被存储在 ServerThreadid 变量中,并存入全局 map。 服务器的核心逻辑是循环读取消息并利用 split("@") 进行分发:

            // ServerThread.java 的 run 方法核心片段
            String[] strings = new String(newBytes, StandardCharsets.UTF_8).split("@");
            want = strings[0];
            if (!want.equals("-1")) {
                for (Map.Entry<String, Socket> value : map.entrySet()) {
                    if (value.getKey().equals(want)) {
                        OutputStream outPut = value.getValue().getOutputStream();
                        String str = new String("Client" + id + ":"); // id 是发送者的用户名
                        outPut.write(str.getBytes());
                        outPut.write(strings[1].getBytes());
                        outPut.flush();
                    }
                }
            } else {
                for (Map.Entry<String, Socket> value : map.entrySet()) {
                    if (value.getValue() != this.socket) {
                        OutputStream outPut = value.getValue().getOutputStream();
                        outPut.write(("Client" + id + ":" + strings[1]).getBytes());
                        outPut.flush();
                    }
                }
            }

三,总结

通过引入 MassageType 接口,我们成功建立了一套简单的通讯协议,解决了登录与注册区分问题。通过编写页面使得程序从枯燥的控制台输入进化到了图形化交互。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐