多用户通信系统

项目开发流程

image

image

多用户通信需求

image

通信系统整体分析

image

qq用户登录

1

拉取在线用户(client端)

public class ClientConnectServerThread extends Thread{
    //该线程需要持有Socket
    private Socket socket;

    //构造器接受一个socket
    public  ClientConnectServerThread(Socket socket){
        this.socket = socket;
    }

    public void run(){
        //因为Thread需要在后台和服务器通信,所以while循环
        while (true){
            try {
                System.out.println("客户端线程,等待读取从服务器端发送的消息");
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message = (Message) ois.readObject();//如果服务器没有发送Message对象,那么线程将会进入阻塞状态
                //后面将使用message
                //判断message类型,然后做相应的业务逻辑处理
                //如果读取到的是服务器返回的在线列表
                if (message.getMesType().equals(MyssageType.MESSAGE_RET_ONLINE_FRIEND)){
                    //取出在线列表,并显示
                    //规定
                    String[] OnlineUsers = message.getContent().split(" ");
                    System.out.println("\n========当前在线用户列表========");
                    for (int i = 0; i < OnlineUsers.length ; i++){
                        System.out.println("用户: "+OnlineUsers[i]);
                    }
                }else{

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

        }
    }

    //更方便得到Socket
    public Socket getSocket() {
        return socket;
    }
}

public class UserClientServise {
    //因为我们在别的地方也要用到User,所以设为属性
    private User u = new User();
    //Socket 同理
    private Socket socket;

    //根据userId 和pwd 到服务器验证该用户是否合法

    public boolean checkUser(String userid , String pwd){
        boolean b =false;
        //创建User对象
        u.setUserid(userid);
        u.setPassword(pwd);
        //连接到服务器,发送u对象
        try {
            Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);//try 快捷键 Ctrl+Alt+t
            //得到ObjectOutputStream对象
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            oos.writeObject(u); //发送User对象

            //读取从服务器回复的Message对象
            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            Message ms = (Message) ois.readObject();

            if (ms.getMesType().equals(MyssageType.MESSAGE_LOGIN_SUCCEED)){
                //成功
                //创建一个和服务器端保持通信的线程—>创建类 ClientConnectServerThread
                ClientConnectServerThread clientConnectServerThread = new ClientConnectServerThread(socket);
                //启动客户端线程
                clientConnectServerThread.start();
                //这里为了后面客户端的扩展,我们将线程放入集合进行管理
                ManageClientConnectServerThread.addclientConnectServerThread(userid,clientConnectServerThread);
                b = true;
            }else {
                //如果失败,我们就不能启动和服务器通信的线程,关闭socket
                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return b;
    }
    //向服务器请求在线用户列表
    public  void onlineFriends(){
        //发送一个message 类型是MESSAGE_GET_ONLINE_FRIEND
        Message message = new Message();
        message.setMesType(MyssageType.MESSAGE_GET_ONLINE_FRIEND);
        message.setSender(u.getUserid());
        try {
            //得到当前socket对应的ObjectOutputStream对象
            ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread.getclientConnectServerThread(u.getUserid())
                    .getSocket().getOutputStream());
            oos.writeObject(message);//发送一个Message对象,向服务器要求在在线列表
            //判断这个message类型,然后做相应的业务处理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

拉取在线用户(Server端)

package qqserver.server;

import java.util.HashMap;
import java.util.Iterator;

/**
 * @author lyq
 * @version 1.0
 * 管理和客户端通信的线程
 */
public class MangerServerConnectClientThread {
    //线程放入HashMap中,k是id,v是线程
    private static HashMap<String,ServerConnetClientThread> hm = new HashMap<>();

    //添加线程进入集合
    public static void addServerConnectClientThread(String userId ,ServerConnetClientThread serverConnetClientThread){
        hm.put(userId,serverConnetClientThread);
    }

    //根据用户名查找线程
    public static ServerConnetClientThread  getserverConnetClientThread(String userId){
        return hm.get(userId);
    }

    public static String getOnlineUsers() {
        //集合遍历,遍历hashmap中的key
        Iterator<String> iterator = hm.keySet().iterator();
        String onlineUserList = " ";
        while(iterator.hasNext()){
            onlineUserList += iterator.next().toString()+" ";
        }
        return  onlineUserList;
    }
}
package qqserver.server;

import qqcommon.Message;
import qqcommon.MyssageType;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * @author lyq
 * @version 1.0
 * 该类的一个对象和某个客户端保持通信
 */
public class ServerConnetClientThread extends Thread{
    Socket socket;
    String userId; //区分和哪个客户端保持连接

    public ServerConnetClientThread(Socket socket, String userId) {
        this.socket = socket;
        this.userId = userId;
    }

    @Override
    public void run() {  //线程处于运行状态,可以接受/发送消息
        while(true){
            try {
                System.out.println("服务端和客户端"+userId+"保持通信,读取数据");
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message =(Message) ois.readObject();
                //后面使用Message
                if(message.getMesType().equals(MyssageType.MESSAGE_GET_ONLINE_FRIEND)){
                    //客户端要在线用户列表
                    System.out.println(message.getSender()+" 要用户在线列表");
                    String onlineUsers = MangerServerConnectClientThread.getOnlineUsers();
                    //返回message
                    Message message2 = new Message();
                    message2.setMesType(MyssageType.MESSAGE_RET_ONLINE_FRIEND);
                    message2.setContent(onlineUsers);
                    message2.setGetter(message.getSender());
                    //写入数据通道,返回给客户端
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(message2);
                }else {

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

无异常退出

image
主线程退出了,但是子线程还在运行,导致进程没有真正退出

解决方法

image

给出了思路,结果自己的纰漏

1 进程应该是在客户端退出
2 服务端中收到退出线程的消息之后,管理线程的集合中也要把线程删掉
3 还要退出服务端的while 循环

私聊功能实现

功能说明

image

思维导图

image

实现漏洞

接收的content 应该是在界面中实现
看图向明白整个过程,首先是客户端给服务端,然后服务端再次输出,客户端在读取到

群发功能实现

功能实现

image

漏洞

总是忘记在菜单上还有定义输入的变量,Map方法的遍历 itit 还不熟悉 要重新复习看

发文件

image

框架

image

漏洞

不记得了文件的上传和读取怎么写了
要先明确 是要把 存放文件的字节数组 要转换撑message
最后保存文件的时候,先把message中的字节输出取出来
在写方法时,注意是设置类型和传入的值

服务器推送新闻

image

拓展提升(靠自己)

image

posted @   java同学!  阅读(76)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示