Java 第三阶段增强分析需求,代码实现能力【多用户即时通信系统】

服务端代码链接https://download.csdn.net/download/qq_52354698/86438353?spm=1001.2014.3001.5501
客户端代码链接https://download.csdn.net/download/qq_52354698/86438352?spm=1001.2014.3001.5501

1. 需求分析

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

  1. 用户登录
  2. 拉取在线用户列表
  3. 无异常退出(客户端、服务端)
  4. 私聊
  5. 群聊
  6. 文件
  7. 服务器推送新闻

2. 界面设计

  1. 用户登录

    在这里插入图片描述

  2. 拉取在线用户列表

    在这里插入图片描述

  3. 私聊

    在这里插入图片描述

  4. 群聊

    在这里插入图片描述

  5. 文件

    在这里插入图片描述

  6. 文件服务器推送新闻

    在这里插入图片描述

3. 功能实现-用户登录

1. 功能说明

我们认为规定用户名/id = 100,密码 = 12345 就可以登录,其它用户不能登录

2. 思路分析、程序框架图

在这里插入图片描述

3. 代码分析

表示客户端和服务器端通信时的消息对象

package com.qdu.qqcommon;

import java.io.Serializable;

/**
 * @author dell
 * @version 1.0
 * 表示客户端和服务器端通信时的消息对象
 */
public class Message implements Serializable {

    private static final long serialVersionUID = 1L;

    private String sender;//发送者
    private String getter;//接收者
    private String content;//消息内容
    private String sendTime;//发生时间
    private String mesType;//消息类型[可以在接口中定义消息类型]

    public String getMesType() {
        return mesType;
    }

    public void setMesType(String mesType) {
        this.mesType = mesType;
    }

    public String getSender() {
        return sender;
    }

    public void setSender(String sender) {
        this.sender = sender;
    }

    public String getGetter() {
        return getter;
    }

    public void setGetter(String getter) {
        this.getter = getter;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getSendTime() {
        return sendTime;
    }

    public void setSendTime(String sendTime) {
        this.sendTime = sendTime;
    }
}

在接口中定义一些常量
不同的常量值代表不同的消息类型

package com.qdu.qqcommon;

/**
 * @author dell
 * @version 1.0
 * 在接口中定义一些常量
 * 不同的常量值代表不同的消息类型
 */
public interface MessageType {

    String MESSAGE_LOGIN_SUCCEED = "1";//表示登录成功
    String MESSAGE_LOGIN_FAIL = "2";//表示登录失败

}

表示一个用户信息

package com.qdu.qqcommon;

import java.io.Serializable;

/**
 * @author dell
 * @version 1.0
 * 表示一个用户信息
 */
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String userId;//用户名
    private String passwd;//密码

    public User(){
    }

    public User(String userId, String passwd) {
        this.userId = userId;
        this.passwd = passwd;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getpasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
}

客户端

客户端的菜单界面

package com.qdu.qqclient.view;

import com.qdu.qqclient.service.UserClientService;
import com.qdu.qqclient.utils.Utility;

import java.io.IOException;

/**
 * @author dell
 * @version 1.0
 * 客户端的菜单界面
 */
public class QQView {

    private boolean loop = @H_502_856@true;//控制是否显示菜单
    private String key = "";
    private UserClientService userClientService = new UserClientService();//用于用户登录注册

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        new QQView().mainMenu();
        System.out.println("客户端系统退出......");
    }

    //显示菜单
    private void mainMenu() throws IOException, ClassNotFoundException {

        System.out.println("==========欢迎登录网络通信系统==========");
        System.out.println("\t\t 1.登录系统");
        System.out.println("\t\t 9.退出系统");
        System.out.print("请输入您的选择:");
        key = Utility.readString(1);
        //根据用户输入,处理不同的逻辑
        switch (key) {
            case "1":
                System.out.print("请输入用户名:");
                String userId = Utility.readString(50);
                System.out.print("请输入密  码:");
                String pwd = Utility.readString(50);
                //单独一个类来验证用户登录
                if (userClientService.checkUser(userId, pwd)) {
                    System.out.println("==========欢迎 " + userId + "==========");
                    //二级菜单
                    while (loop){
                        System.out.println("==========网络通信系统二级菜单(用户 " + userId + " 登录成功)==========");
                        System.out.println("\t\t 1 显示在线用户列表");
                        System.out.println("\t\t 2 群发消息");
                        System.out.println("\t\t 3 私聊消息");
                        System.out.println("\t\t 4 发送文件");
                        System.out.println("\t\t 9 退出系统");
                        key = Utility.readString(1);
                        switch (key){
                            case "1":
                                System.out.println("显示在线用户列表");
                                break;
                            case "2":
                                System.out.println("群发消息");
                                break;
                            case "3":
                                System.out.println("私聊消息");
                                break;
                            case "4":
                                System.out.println("发送文件");
                                break;
                            case "9":
                                loop = @H_502_856@false;
                                break;
                        }
                    }
                } else {
                    System.out.println("==========登录失败==========");
                }
                break;
            case "9":
                loop = @H_502_856@false;
                break;
        }

    }

}



该类完成用户登录验证和用户注册功能

package com.qdu.qqclient.service;

import com.qdu.qqcommon.Message;
import com.qdu.qqcommon.MessageType;
import com.qdu.qqcommon.User;

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

/**
 * @author dell
 * @version 1.0
 * 该类完成用户登录验证和用户注册功能.
 */
public class UserClientService {

    //因为我们可能在其他地方用使用user信息, 因此作出成员属性
    private User user = new User();
    //因为Socket在其它地方也可能使用,因此作出属性
    private Socket socket;

    //根据userId 和 pwd 到服务器验证该用户是否合法
    public boolean checkUser(String userId, String pwd) {
        boolean b = @H_502_856@false;
        //创建User对象
        user.setUserId(userId);
        user.setPasswd(pwd);
        try {
            //连接到服务端,发送u对象
            socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
            //得到ObjectOutputStream对象
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getoutputStream());
            objectOutputStream.writeObject(user);//发送User对象
            //读取从服务器回复的Message对象
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Message message = (Message) objectInputStream.readobject();

            if (message.getMesType().equals(MessageType.MESSAGE_LOGIN_SUCCEED)) {//登录OK
                //创建一个和服务器端保持通信的线程-> 创建一个类 ClientConnectServerThread
                ClientConnectServerThread clientConnectServerThread = new ClientConnectServerThread(socket);
                //启动客户端的线程
                clientConnectServerThread.start();
                //这里为了后面客户端的扩展,我们将线程放入到集合管理
                ManageClientConnectServerThread.addClientConnectServerThread(userId, clientConnectServerThread);
                b = @H_502_856@true;
            } else {
                //如果登录失败, 我们就不能启动和服务器通信的线程, 关闭socket
                socket.close();
            }
        } catch (Exception e) {
            e.printstacktrace();
        }
        return b;
    }

}

在线程中创建socket

package com.qdu.qqclient.service;

import com.qdu.qqcommon.Message;

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

/**
 * @author dell
 * @version 1.0
 * 在线程中创建socket
 */
public class ClientConnectServerThread extends Thread{

    //该线程需要持有Socket
    private Socket socket;

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

    @Override
    public void run() {
        //因为Thread在后台和服务器通信,因此我们while循环
        while (@H_502_856@true){
            try {
                System.out.println("客户端线程,等待读取从服务器端发送的消息");
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                //如果服务器端没有发生Message对象,线程就会阻塞在这里
                Message message = (Message) objectInputStream.readobject();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public Socket getSocket() {
        return socket;
    }
}

该类管理客户端连接到服务器端的线程的类

package com.qdu.qqclient.service;

import java.util.HashMap;

/**
 * @author dell
 * @version 1.0
 * 该类管理客户端连接到服务器端的线程的类
 */
public class ManageClientConnectServerThread {
    //我们将多个线程放入到HashMap集合中,key就是用户id,value就是线程
    private static HashMap<String, ClientConnectServerThread> hashMap = new HashMap<>();
    //将某个线程放入到集合中
    public static void addClientConnectServerThread(String userId, ClientConnectServerThread clientConnectServerThread){
        hashMap.put(userId, clientConnectServerThread);
    }
    //通过userId可以得到对应的线程
    public static ClientConnectServerThread getClientConnectServerThread(String userId) {
        return hashMap.get(userId);
    }
}

服务端

该类创建QQServer,启动后台服务

package com.qdu.qqframe;

import com.qdu.qqserver.service.QQServer;

/**
 * @author dell
 * @version 1.0
 * 该类创建QQServer,启动后台服务
 */
public class Qqframe {

    public static void main(String[] args) {

        new QQServer();

    }

}

这是服务器, 在监听9999,等待客户端的连接,并保持通信

package com.qdu.qqserver.service;

import com.qdu.qqcommon.Message;
import com.qdu.qqcommon.MessageType;
import com.qdu.qqcommon.User;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author dell
 * @version 1.0
 * 这是服务器, 在监听9999,等待客户端的连接,并保持通信
 */
public class QQServer {

    private ServerSocket serverSocket = null;
    //创建一个集合,存放多个用户,如果是这些用户登录,就认为是合法
    //这里我们也可以使用 ConcurrentHashMap, 可以处理并发的集合,没有线程安全
    //HashMap 没有处理线程安全,因此在多线程情况下是不安全
    //ConcurrentHashMap 处理的线程安全,即线程同步处理, 在多线程情况下是安全
    private static ConcurrentHashMap<String, User> validUsers = new ConcurrentHashMap<>();
    //private static ConcurrentHashMap<String, ArrayList<Message>> offLineDb = new ConcurrentHashMap<>();

    static { //在静态代码块,初始化 validUsers

        validUsers.put("100", new User("100", "123456"));
        validUsers.put("200", new User("200", "123456"));
        validUsers.put("300", new User("300", "123456"));
        validUsers.put("至尊宝", new User("至尊宝", "123456"));
        validUsers.put("紫霞仙子", new User("紫霞仙子", "123456"));
        validUsers.put("菩提老祖", new User("菩提老祖", "123456"));

    }

    //验证用户是否有效的方法
    private boolean checkUser(String userId, String passwd) {

        User user = validUsers.get(userId);
        //过关的验证方式
        if (user == null) {//说明userId没有存在validUsers 的key中
            return @H_502_856@false;
        }
        if (!user.getpasswd().equals(passwd)) {//userId正确,但是密码错误
            return @H_502_856@false;
        }
        return @H_502_856@true;
    }

    public QQServer() {
        //注意:端口可以写在配置文件.
        try {
            System.out.println("服务端在9999端口监听...");
            //启动推送新闻的线程
//            new Thread(new SendNewsToAllService()).start();
            serverSocket = new ServerSocket(9999);

            while (@H_502_856@true) { //当和某个客户端连接后,会继续监听, 因此while
                Socket socket = serverSocket.accept();//如果没有客户端连接,就会阻塞在这里
                //得到socket关联的对象输入流
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                //得到socket关联的对象输出
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getoutputStream());
                User user = (User) objectInputStream.readobject();//读取客户端发送的User对象
                //创建一个Message对象,准备回复客户端
                Message message = new Message();
                //验证用户 方法
                if (checkUser(user.getUserId(), user.getpasswd())) {//登录通过
                    message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);
                    //将message对象回复客户端
                    objectOutputStream.writeObject(message);
                    //创建一个线程,和客户端保持通信, 该线程需要持有socket对象
                    ServerConnectClientThread serverConnectClientThread = new ServerConnectClientThread(socket, user.getUserId());
                    //启动该线程
                    serverConnectClientThread.start();
                    //把该线程对象,放入到一个集合中,进行管理.
                    ManageClientThreads.addClientThread(user.getUserId(), serverConnectClientThread);
                } else { // 登录失败
                    System.out.println("用户 id=" + user.getUserId() + " pwd=" + user.getpasswd() + " 验证失败");
                    message.setMesType(MessageType.MESSAGE_LOGIN_FAIL);
                    objectOutputStream.writeObject(message);
                    //关闭socket
                    socket.close();
                }
            }
        } catch (Exception e) {
            e.printstacktrace();
        } finally {
            //如果服务器退出了while,说明服务器端不在监听,因此关闭ServerSocket
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printstacktrace();
            }
        }
    }
}

该类对应的对象和某个客户端保持通信

package com.qdu.qqserver.service;

import com.qdu.qqcommon.Message;

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

/**
 * @author dell
 * @version 1.0
 * 该类对应的对象和某个客户端保持通信
 */
public class ServerConnectClientThread extends Thread{
    private Socket socket;
    private String userId;//连接到服务端的用户id

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

    @Override
    public void run() {//线程处于run状态,可以发送、接收消息
        while (@H_502_856@true){
            System.out.println("服务端和客户端 " + userId + " 保持通信,读取数据...");
            try {
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                Message message = (Message) objectInputStream.readobject();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}



该类用于管理和客户端通信的线程

package com.qdu.qqserver.service;

import java.util.HashMap;

/**
 * @author dell
 * @version 1.0
 * 该类用于管理和客户端通信的线程
 */
public class ManageClientThreads {
    private static HashMap<String, ServerConnectClientThread> hashMap = new HashMap<>();

    //添加线程对象到hashmap集合
    public static void addClientThread(String userId, ServerConnectClientThread serverConnectClientThread){
        hashMap.put(userId, serverConnectClientThread);
    }

    //根据userId返回serverConnectClientThread线程
    public static ServerConnectClientThread getServerConnectClientThread(String userId){
        return hashMap.get(userId);
    }
}

4. 功能实现-拉取在线用户列表

1. 功能说明

在这里插入图片描述

2. 思路分析+程序框架图

在这里插入图片描述

客户端要获取所有在线的列表,只能向服务器发送请求获得在线列表(因为只有服务器知道全部的在线列表)

3. 代码实现

扩展 MessageType 种类

    String MESSAGE_COMM_MES = "3";//普通信息包
    String MESSAGE_GET_ONLINE_FRIEND = "4";//要求返回在线用户列表
    String MESSAGE_RET_ONLINE_FRIEND = "5";//返回的在线用户列表
    String MESSAGE_CLIENT_EXIT = "6";//客户端请求退出

编写向服务器端请求在线用户列表方法

    //向服务器端请求在线用户列表
    public void onlineFriendList() {

        //发送一个Message,类型MESSAGE_GET_ONLINE_FRIEND
        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);

        //发送给服务器
        //应该得到当前线程的socket对应的ObjectOutputStream对象
        try {
            //从管理线程的集合中,通过userid,得到这个线程对象
            ClientConnectServerThread clientConnectServerThread = ManageClientConnectServerThread.getClientConnectServerThread(user.getUserId());
            //通过当前线程的socket对应的ObjectOutputStream对象
            Socket socket1 = clientConnectServerThread.getSocket();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket1.getoutputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

服务器,编写方法,返回在线用户列表

    //编写方法,返回在线用户列表
    public static String getonlineUser() {
        //遍历hashmap
        Iterator<String> iterator = hashMap.keySet().iterator();
        String onlineUserList = "";
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            onlineUserList += next.toString() + " ";
        }
        return onlineUserList;
    }

5. 功能实现-无异常退出系统

1. 功能说明

直接退出时,是退出的主线程,而主线程中和服务端通信的线程还在一直循环中,无法正常退出
解决:在退出时,客户端给服务端发送一个message对象,告诉服务端退出

2. 思路分析+程序框架图

在这里插入图片描述

3. 代码实现

编写客户端退出程序,客户端退出,并告知服务端

	//编写方法退出客户端,并给服务端发送一个退出的message对象
    public void logout() {

        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_CLIENT_EXIT);
        message.setSender(user.getUserId());//一定要指定是哪一个客户端要退出

        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(user.getUserId()).getSocket().getoutputStream());
            objectOutputStream.writeObject(message);
            System.out.println(user.getUserId() + " 退出了系统");
            System.exit(0);//结束客户端进行
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

服务端接收到客户端的告知,将该客户端对应的线程从服务端线程管理集合中删除,并关闭与该客户端的连接

else if (message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)) {
                    System.out.println(message.getSender() + " 退出");
                    //将这个客户端对应的线程,从集合中删除
                    ManageClientThreads.removeServerConnectClientThread(message.getSender());
                    socket.close();//关闭连接
                    break;//退出while循环->退出run方法
                }

6. 功能实现-私聊

1. 功能说明

在这里插入图片描述

2. 思路分析+程序框架图

在这里插入图片描述

3. 代码实现

客户端发送消息给服务器端,发送者id,接收者id,发送内容,发送时间,消息类型

package com.qdu.qqclient.service;

import com.qdu.qqcommon.Message;
import com.qdu.qqcommon.MessageType;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;

/**
 * @author dell
 * @version 1.0
 * 该类/对象,提供和消息相关的服务方法
 */
public class MessageClientService {

    /**
     *
     * @param content 内容
     * @param senderId 发送用户id
     * @param getterId 接收用户id
     */
    public void sendMessagetoOne(String content, String senderId, String getterId) {

        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_COMM_MES);
        message.setSender(senderId);
        message.setGetter(getterId);
        message.setContent(content);
        message.setSendTime(new Date().toString());//设置发送时间
        System.out.println(senderId + " 对 " + getterId + " 说 " + content);
        //发送给服务器
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getoutputStream());
            objectOutputStream.writeObject(message);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

}

服务端转发消息

else if (message.getMesType().equals(MessageType.MESSAGE_COMM_MES)) {
                    //根据message获取getterid,然后再得到对应的线程
                    ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(message.getGetter());
                    //得到对应socket的对象输出流,将message转发给指定的客户端
                    ObjectOutputStream objectOutputStream = new ObjectOutputStream(serverConnectClientThread.getSocket().getoutputStream());
                    objectOutputStream.writeObject(message);//转发
                }

7. 功能实现-群聊

1. 功能说明

在这里插入图片描述

2. 思路分析+程序框架图

在这里插入图片描述


发消息和私聊消息基本类似,不同点在于,群发消息,要将消息转发给除自己外的所有在线用户ing

3. 代码实现

客户端发送消息给服务器端,发送者id,发送内容,发送时间,消息类型

    public void sendMessagetoAll(String content, String senderId) {

        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_TO_ALL_MES);
        message.setSender(senderId);
        message.setContent(content);
        message.setSendTime(new Date().toString());//设置发送时间
        System.out.println(senderId + " 对大家说 " + content);
        //发送给服务器
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getoutputStream());
            objectOutputStream.writeObject(message);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

服务端将消息转发给除自己外的所有在线用户

else if (message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)) {
                    //遍历管理线程的集合,把所有的线程socket得到,除去自己
                    HashMap<String, ServerConnectClientThread> hashMap = ManageClientThreads.getHashMap();
                    Iterator<String> iterator = hashMap.keySet().iterator();
                    while (iterator.hasNext()) {
                        Object next = iterator.next();
                        //取出在线用户的id,除去自己
                        String onLineUserId = iterator.next().toString();
                        if (!onLineUserId.equals(message.getSender())) {
                            ObjectOutputStream objectOutputStream = new ObjectOutputStream(hashMap.get(onLineUserId).getSocket().getoutputStream());
                            objectOutputStream.writeObject(message);
                        }
                    }

8. 功能实现-发文件

1. 功能说明

在这里插入图片描述

2. 思路分析+程序框架图

在这里插入图片描述

3. 代码实现

客户端从电脑读取文件并发送给服务器端

package com.qdu.qqclient.service;

import com.qdu.qqcommon.Message;
import com.qdu.qqcommon.MessageType;

import java.io.*;

/**
 * @author dell
 * @version 1.0
 */
public class FileClientService {

    /**
     * @param src      源文件
     * @param dest     存放目录
     * @param senderId 发送者id
     * @param getterId 接收者id
     */
    public void sendFiletoOne(String src, String dest, String senderId, String getterId) {

        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_FILE_MES);
        message.setSender(senderId);
        message.setGetter(getterId);
        message.setSrc(src);
        message.setDest(dest);

        //将文件读取
        FileInputStream fileInputStream = null;
        byte[] fileBytes = new byte[(int) new File(src).length()];

        try {
            fileInputStream = new FileInputStream(src);
            fileInputStream.read(fileBytes);//将src文件读入到程序的字节数组
            //将文件对应的字节数组设置message
            message.setFileBytes(fileBytes);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fileInputStream != null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        System.out.println("\n" + getterId + " 给 " + senderId + " 发送文件:" + src + " 到对方的电脑的目录 " + dest);

        //发送
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getoutputStream());
            objectOutputStream.writeObject(message);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

服务器端转发文件给对应的客户端

else if (message.getMesType().equals(MessageType.MESSAGE_FILE_MES)) {
                    //将文件转发给指定的客户端
                    ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(message.getGetter());
                    ObjectOutputStream objectOutputStream = new ObjectOutputStream(serverConnectClientThread.getSocket().getoutputStream());
                    //转发
                    objectOutputStream.writeObject(message);
                }

客户端将服务端转发的文件保存到电脑

else if (message.getMesType().equals(MessageType.MESSAGE_FILE_MES)) {
                    System.out.println("\n" + message.getSender() + " 给 " + message.getGetter() + " 发送文件 " + message.getSrc() + " 到我的电脑目录 " + message.getDest());
                    //取出message的文件字节数组,通过文件输出流写出到磁盘
                    FileOutputStream fileOutputStream = new FileOutputStream(message.getDest());
                    fileOutputStream.write(message.getFileBytes());
                    fileOutputStream.close();
                    System.out.println("\n保存文件成功~");
                }

9. 功能实现-服务器推送新闻

1. 功能说明

在这里插入图片描述

2. 思路分析+程序框架图

在这里插入图片描述


相当于服务器端的群发消息

3. 代码实现

package com.qdu.qqserver.service;

import com.qdu.qqcommon.Message;
import com.qdu.qqcommon.MessageType;
import com.qdu.utils.Utility;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Scanner;

/**
 * @author dell
 * @version 1.0
 */
public class SendNewsToAllService implements Runnable {

    @Override
    public void run() {

        //方便可以多次推送新闻,使用while
        while (@H_502_856@true) {
            System.out.println("请输入服务器要推送的消息/新闻[输入exit表示退出推送]:");
            String news = Utility.readString(1000);
            if ("exit".equals(news)) {
                break;
            }
            Message message = new Message();
            message.setSender("服务器");
            message.setMesType(MessageType.MESSAGE_TO_ALL_MES);
            message.setContent(news);
            message.setSendTime(new Date().toString());
            System.out.println("服务器推送消息给所有人 说 " + news);

            //遍历当前的所有通信线程,得到socket,并发送message
            HashMap<String, ServerConnectClientThread> hashMap = ManageClientThreads.getHashMap();
            Iterator<String> iterator = hashMap.keySet().iterator();
            while (iterator.hasNext()) {
                Object next = iterator.next();
                String onLineUserId = next.toString();
                try {
                    ObjectOutputStream objectOutputStream = new ObjectOutputStream(hashMap.get(onLineUserId).getSocket().getoutputStream());
                    objectOutputStream.writeObject(message);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...