实现进阶的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);
            }
        }
    }
相关推荐
计算机小白一个7 分钟前
蓝桥杯 Java B 组之哈希表应用(两数之和、重复元素判断)
java·数据结构·算法·蓝桥杯
Loge编程生活9 分钟前
打卡信奥刷题(3449)用C++实现信奥题 P10429 [蓝桥杯 2024 省 B] 拔河
开发语言·数据结构·c++·算法·青少年编程
在世修行1 小时前
从零打造 C# 工业视觉检测系统(八):Modbus RTU 协议解析与 PLC 剔除控制实战
开发语言·c#
sunburn-1 小时前
Java 队列全面详解:从入门到面试实战
java·开发语言·汇编·ide·idea
AI直播技术杂谈1 小时前
AI直播推流链路中延迟优化的通用技术方案
开发语言·人工智能·php
vx-程序开发2 小时前
springboot农产品运输服务平台---附源码75498
java·javascript·spring boot·python·eclipse·django·php
Dxy12393102162 小时前
python 中的 APScheduler 使用详解
开发语言·python
zzh___zzh3 小时前
SQL 窗口函数 ROW_NUMBER、RANK、DENSE_RANK 常用方法笔记
java
计科土狗3 小时前
GESP六级专题之类与对象
java·前端·数据库
福如意如我心意3 小时前
java虚拟线程和Go协程的区别
开发语言