多用户及时通信系统之用户发文件功能实现(6)

思路分析

客户端

  1. 先把文件a.jpg读取到客户端的字节数组中
  2. 把文件对应的字节数组封装到message对象[包含文件内容,sender,getter]
  3. 将message对象发送给服务端
  4. 在接收到包含有文件的消息后,将该文件保存到磁盘

服务端

  1. 接收到message对象
  2. 拆解message对象的getterId,获取该用户的通信线程
  3. 把message对象转发给指定用户

功能实现

共通
MessageType.java : 新增消息类型

package com.ming.qqcommon;

/**
 * @Author: mei_ming
 * @DateTime: 2022/10/2 18:31
 * @Description: 表示消息类型
 */
public interface MessageType {

    String MESSAGE_LOGIN_SUCCEED = "1";  // 登录成功
    String MESSAGE_LOGIN_FAIL = "2";  // 登录失败
    String MESSAGE_COMM_MES = "3";  //普通信息包
    String MESSAGE_GET_ONLINE_FRIEND = "4";  //要求返回在线用户列表
    String MESSAGE_RET_ONLINE_FRIEND = "5";  //返回在线用户列表
    String MESSAGE_CLIENT_EXIT = "6";  //客户端请求退出
    String MESSAGE_TO_ALL_MES = "7";  //群发的消息
    String MESSAGE_FILE_MES = "8";  //文件消息(发送文件)
}

Message.java : 新增传输内容

package com.ming.qqcommon;

import java.io.Serializable;

/**
 * @Author: mei_ming
 * @DateTime: 2022/10/2 18:23
 * @Description: 表示客户端和服务端通信时的消息
 */
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;  //消息类型[可以在接口定义消息类型]

    //扩展 和文件相关的成员变量
    private byte[] fileBytes;
    private int fileLen = 0;
    private String src;   //源文件路径
    private String dest;  //将文件传输到哪里

    public byte[] getFileBytes() {
        return fileBytes;
    }

    public void setFileBytes(byte[] fileBytes) {
        this.fileBytes = fileBytes;
    }

    public int getFileLen() {
        return fileLen;
    }

    public void setFileLen(int fileLen) {
        this.fileLen = fileLen;
    }

    public String getSrc() {
        return src;
    }

    public void setSrc(String src) {
        this.src = src;
    }

    public String getDest() {
        return dest;
    }

    public void setDest(String dest) {
        this.dest = dest;
    }

    public Message() {
    }

    public Message(String sender, String getter, String content, String sendTime, String mesType) {
        this.sender = sender;
        this.getter = getter;
        this.content = content;
        this.sendTime = sendTime;
        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;
    }

    public String getMesType() {
        return mesType;
    }

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

}


客户端
FileClientService.java : 新增文件服务类,发送文件的方法

package com.ming.qqclient.service;

import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;

import java.io.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/10/3 22:19
 * @Description: 文件传输服务
 */
public class FileClientService {

    /**
     *
     * @param src  源文件
     * @param dest  目标文件
     * @param senderId  发送id
     * @param getterId  接收id
     */
    public void sendFileToOne(String src,String dest,String senderId,String getterId){
        //读取src 封装到message
        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.setFileBytes(fileBytes);  //将字节数组装到message
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream!=null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //提示信息
        System.out.println("\n" + senderId + "给 " + getterId+"发送文件:"+src
                +"到对方的目录:"+dest);

        //发送到服务端
        try {
            ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getOutputStream());
            oos.writeObject(message);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

ClientConnectServerThread.java : 新增对type的判断 MESSAGE_FILE_MES

package com.ming.qqclient.service;

import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;

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

/**
 * @Author: mei_ming
 * @DateTime: 2022/10/2 20:25
 * @Description: 线程生成类
 */
public class ClientConnectServerThread extends Thread{
    //该线程需要持有Socket
    private Socket socket;

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

    @Override
    public void run() {
        //因为Thread需要在后台和服务器通信,所以用while
        while(true){
            try {
                System.out.println("客户端线程,等待读取从服务端发送的消息");
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message = (Message) ois.readObject();

                //读取message,判断类型,做相应的处理
                if (message.getMesType().equals(MessageType.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 if (message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
                    //把从服务器转发的消息,显示到控制台即可
                    System.out.println("\n"+ message.getSendTime()+" "+ message.getSender()+" 对 "+
                            message.getGetter()+" 说: "+message.getContent());
                } else if (message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)){
                    //显示在客户端的控制台
                    System.out.println("\n"+ message.getSender()+"对大家说: "+message.getContent());
                } 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 保存文件成功");
                }else{
                    System.out.println("是其他类型的message,暂不处理");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    public Socket getSocket() {
        return socket;
    }

    public void setSocket(Socket socket) {
        this.socket = socket;
    }
}

QQView.java : 新增用户输入 ,调用sendFileToOne()方法

case "4":
    System.out.print("请输入想发送文件的用户号(在线): ");
    getterId = Utility.readString(50);
    System.out.print("请输入发送文件的完整路径(e:\\xx.jpg): ");
    String src = Utility.readString(100);
    System.out.print("请输入发送文件的完整路径[目标](e:\\xx.jpg): ");
    String dest = Utility.readString(100);
    //调用方法
    fileClientService.sendFileToOne(src,dest,userId,getterId);
    break;

服务端
ServerConnectClientThread.java : 新增对type的判断 MESSAGE_FILE_MES

package com.ming.qqserver.service;

import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;
import com.sun.deploy.net.proxy.ProxyUnavailableException;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;

/**
 * @Author: mei_ming
 * @DateTime: 2022/10/2 21:09
 * @Description: 该类的一个对象和某个客户端保持通信
 */
public class ServerConnectClientThread extends Thread {

    private Socket socket;
    private String userId;  //区分是哪一个客户端

    public Socket getSocket() {
        return socket;
    }

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

    @Override
    public void run() {  //这里线程处于run的状态,可以发送/接收消息
        while(true){
            try {
                System.out.println("服务端和客户端"+userId+"保持通信,读取数据....");
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message = (Message) ois.readObject();

                //根据message类型,做相应的业务处理
                if (message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)){
                    //客户端要在线用户列表
                    // 格式 "100 200 孙悟空"
                    System.out.println(message.getSender() + " 要在线用户列表");
                    String onlineUser = ManageClientThreads.getOnlineUser();
                    //返回message
                    Message message2 = new Message();
                    message2.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND);
                    message2.setContent(onlineUser);
                    message2.setGetter(message.getSender());
                    //返回客户端
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(message2);
                }else if(message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
                    //私聊功能
                    //根据message获取getterId,然后得到对应的线程
                    ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(message.getGetter());
                    //在线程里获取对应的socket,在获取socket对应的对象输出流
                    ObjectOutputStream oos = new ObjectOutputStream(serverConnectClientThread.getSocket().getOutputStream());
                    oos.writeObject(message);  //转发, 如果客户不在线,可以把内容存到数据库, 做成离线发送消息
                }else if(message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)){
                    //群发消息功能
                    //需要遍历 管理线程的集合,把所有线程的socket得到,把message转发到其他线程中
                    HashMap<String, ServerConnectClientThread> hm = ManageClientThreads.getHm();
                    Iterator<String> iterator = hm.keySet().iterator();

                    while(iterator.hasNext()){
                        //取出在线用户的id
                        String onlineUser = iterator.next().toString();
                        if (!onlineUser.equals(message.getSender())){  //排除群发消息的用户
                            // 发送message
                            ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(onlineUser);
                            OutputStream outputStream = serverConnectClientThread.getSocket().getOutputStream();
                            ObjectOutputStream oos = new ObjectOutputStream(outputStream);
                            oos.writeObject(message);
                        }
                    }
                }else if(message.getMesType().equals(MessageType.MESSAGE_FILE_MES)){
                    //发送文件消息功能
                    //将message转发给getterId的线程的socket
                    ObjectOutputStream oos = new ObjectOutputStream(ManageClientThreads.getServerConnectClientThread(message.getGetter()).getSocket().getOutputStream());
                    oos.writeObject(message);
                }else if(message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)){
                    //客户端退出功能
                    System.out.println(message.getSender()+" 退出");
                    //将这个客户端对应的线程从集合中删除
                    ManageClientThreads.removeServerConnectClientThread(message.getSender());
                    socket.close();  //关闭连接
                    break;  //退出线程
                }else{
                    System.out.println("是其他类型的message,暂不处理");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}

运行效果

  1. 登录孙悟空、猪八戒两个角色
  2. 孙悟空发文件给猪八戒
    wk
  3. 猪八戒 收到文件
    bj
    下一篇服务端推送新闻及离线发送功能
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值