实现进阶的C/S通信

为了能准确,精准的传递信息,我们在服务器和客户端互相发消息的基础上,需要进行细化分类。

服务器需要能识别客户端传来的是什么请求,也就是要区分是登录消息,是注册消息,还是聊天消息。那么在客户端,简单的直接发送就满足不了这些要求,需要像"打暗号"一样,先告诉服务端我发送的消息是哪一类的,在发送消息主体

至于分类,要先对服务端和客户端进行统一要求,确保发送和收到的"暗号"是同一个,那么就先创建一个类,来存储对应的暗号,代码如下

java 复制代码
public class MessageType {
    public static final byte LOGIN = 1;  //登录
    public static final byte REGISTER = 2; //注册
    public static final byte PRIVATE = 3;  //私聊
    public static final byte GROUP = 4;   //广播
    public static final byte CHAT = 5;
    public static final byte YES = 6;
    public static final byte NO = 7;
}

这样,将这个类分别放在服务端和客户端中,统一口令

第二,服务端和客户端不是一对一的反应,一个服务端要同时接受多个客户端的请求,那么就要用到多线程。

服务端创建ServerSocket,用死循环等待每一个客户端的接入。在循环中,用accept阻塞操作,对每一个接入的用户创建一个线程。为了在线程中能找到对应的用户,在创建线程之前,就要对每个用户进行ID化,根据登录或注册的用户名和用户的Socket,创建一个Map,以键值对的形式存储用户接入的信息,以便后续的"广播"和"对话"。代码展示

java 复制代码
    //用户账号存储数组
    public List<User> userList = new ArrayList();

    public Server(){
        //初始化注册账号
        for(int i=1;i<=5;i++){
            userList.add(new User(i+"1",i+"1"));
        }
    }

    public void ServerStart() throws IOException {
        ServerSocket serverSocket = new ServerSocket(19989);
        System.out.println("服务器启动");

        Map<Integer,Socket> map = new HashMap();
        int userID=1;

        while (true) {
            Socket socket=serverSocket.accept();
            map.put(userID,socket);
            ServerThread serverThread=new ServerThread(socket,map,
                    userID++,userList);
            new Thread (serverThread).start();
        }
    }

第三,处理传来的消息。为了能实时监听用户的消息,对每一个线程也需要进行死循环,并用read方法阻塞。前文提到的先传递消息类型,后传递消息主体,也就是需要进行两次接收,先接收消息类型,通过switch case的分类,再在对应的类中处理消息。

例如,我再客户端进行登录操作,那么我就需要先发送一个Message.LOGIN的消息给服务器,在发送我的账号密码给服务器,在服务器端进行账号密码的拆解,进行判断,返回登录成功或者失败的消息返回给客户端。

客户端接受到登录成功的消息,进而跳转到对应的界面,进行下一步对话。

以下是服务端代码展示

java 复制代码
 public void run(){
        while (true) {
            try {
                int head = is.read();
                String msg = readMsg();
                switch (head) {
                    case MessageType.LOGIN:
                        handleLogin(msg);
                        break;
                    case MessageType.REGISTER:

                        break;
                    case MessageType.CHAT:
                        //判断聊天类型
                        String[] msgArr = msg.split(":");
                        if (msgArr[0].equals("g")) {
                            //广播
                            for (Map.Entry<Integer, Socket> entry : map.entrySet()) {
                                Socket s = entry.getValue();
                                if (s != socket) {
                                    OutputStream os = s.getOutputStream();
                                    sendMsg(os, msgArr[1]);
                                }
                            }
                        } else {
                            int id = Integer.parseInt(msgArr[0]);
                            //通过id找到socket
                            Socket s = map.get(id);
                            OutputStream os = s.getOutputStream();
                            sendMsg(os, msgArr[1]);
                        }
                        System.out.println("client:" + msg);
                        break;

                    default:
                        System.out.println("错误文件头");
                        break;
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
相关推荐
h7ml2 小时前
企业微信 API 与内部系统集成时的 OAuth2.0 安全上下文传递机制
java·安全·企业微信
初听于你2 小时前
Java 泛型详解
java·开发语言·windows·java-ee
rainbow68892 小时前
Java17新特性深度解析
java·开发语言·python
bin91532 小时前
C盘瘦身大作战:程序员的存储空间优化全攻略
c语言·开发语言·c盘清理·c盘清理技巧分享
爬山算法2 小时前
Hibernate(79)如何在ETL流程中使用Hibernate?
java·hibernate·etl
小秋学嵌入式-不读研版2 小时前
智能台灯功能重设计方案
开发语言
Z.风止2 小时前
Go-learning(1)
开发语言·笔记·后端·golang
rainbow68892 小时前
Java实战:5230台物联网设备时序数据处理方案
java
爬山算法2 小时前
Hibernate(80) 如何在数据迁移中使用Hibernate?
java·oracle·hibernate