Bootstrap

JAVA即时通讯系统(无界面)————拉取在线用户+无异常退出

1.拉取在线用户

1.1客户端拉取在线用户

首先增加双方通信的消息的种类,客户端和服务器端的消息的类型都增加,如下:

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";//客户端请求退出
}

在客户端的UserClientService中写一个方法来获取在线用户列表

//向服务器端请求在线用户列表
    public void onlineFriendList() {
        //向服务器端发送一个消息,消息的类型是MESSAGE_GET_ONLINE_FRIEND="4"
        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);
        message.setSender(user.getUserId());
        //发送给服务器端
        try {
            //从管理的线程集合当中,通过UseId获取到当前的线程对象
            ClientConnectServerThread clientConnectServerThread =
                    ManageClientConnectServerThread.getClientConnectServerThread(user.getUserId());
            //通过线程对象得到关联的socket
            Socket socket = clientConnectServerThread.getSocket();
            //得到当前线程的Socket对应的ObjectOutputStream对象
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            //发送一个Message对象,向服务器要求在线用户列表
            oos.writeObject(message);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

在客户端线程(即ClientConnectServerThread)中做相应的处理:判断获取到的Message对象的类型,然后再做相应的处理 如果读取到的是服务器端返回的在线用户列表

public void run() {
        //因为Thread需要在后台和服务器进行通信,用while循环一直等待
        while(true) {
            System.out.println("客户端线程,等待读取从服务器端发送的消息");
            try {
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message=(Message)ois.readObject();//当服务器没有发送message对象,线程就会阻塞在此处
                //判断获取到的Message对象的类型,然后再做相应的处理
                //如果读取到的是服务器端返回的在线用户列表
                if(message.getMesType().equals(MessageType.MESSAGE_GET_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 {
                    System.out.println("其他类型的信息,暂时不做处理");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

在QQView里面输入1时调用这个方法

package com.qq.qqclient.view;

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

public class QQView {
    private boolean loop=true;//用于控制用户退出与登陆
    private String key="";//用于接收用户输入
    private UserClientService userClientService=new UserClientService();//对象用来登陆服务/注册用户
    public static void main(String args[]) {
        new QQView().mainMenu();
    }

    //显示主菜单
    public void mainMenu(){
        while(loop) {
            System.out.println("===============欢迎进入即时通信系统===============");
            System.out.println("\t\t1.登陆系统");
            System.out.println("\t\t9.退出系统");
            System.out.print("请输入你的选择:");
            key= Utility.readString(1);
            switch (key) {
                case "1":
                    System.out.print("请输入你的用户名:");
                    String userId=Utility.readString(20);
                    System.out.print("请输入你的密  码:");
                    String pwd= Utility.readString(20);
                    //此处省略若干....需要到服务器端去验证该用户是否合法
                    //编写一个类UserClientService

                    if(userClientService.checkUser(userId,pwd)) {
                        //进入二级菜单
                        while(loop) {
                            System.out.println("===============欢迎用户"+userId+"进入系统===============");
                            System.out.println("\t\t1.显示在线用户列表");
                            System.out.println("\t\t2.群发消息");
                            System.out.println("\t\t3.私聊消息");
                            System.out.println("\t\t4.发送文件");
                            System.out.println("\t\t9.退出系统");
                            System.out.print("请输入你的选择:");
                            key=Utility.readString(1);
                            switch(key) {
                                case "1":
                                    //写一个方法来拉取在线用户列表
                                    userClientService.onlineFriendList();
                                    //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=false;
                                    break;
                            }
                        }
                    } else {
                        System.out.println("登陆失败...");
                    }
                    break;
                case "9":
                    loop=false;
                    System.out.println("退出系统...");
                    break;
            }
        }
    }
}
1.2服务器端拉取在线用户列表

首先判断获得的消息的类型,对于是客户端发来的获取消息列表做相应的处理

public void run() {//该线程一直处于运行状态,可以接收和发送消息
        while(true) {
            System.out.println("服务器端与客户端 "+userId+" 保持通信,读取数据....");
            try {
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message=(Message)ois.readObject();
                //后面通过message传发消息
                //根据message的类型,做相应的判断
                if(message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)) {
                    //客户端需要在线用户列表
                    System.out.println(message.getSender()+" 要在线用户列表");
                    //得到需要返回的信息
                    String onlineFriend = ManageServerThread.getOnlineFriend();
                    //返回message给客户端
                    message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);
                    message.setContent(onlineFriend);
                    message.setGetter(message.getGetter());
                    //写入到数据通道,返回给客户端
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(message);
                }else {
                    System.out.println("其他类型的message,暂时不处理");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

在ManageServerThread里面编写一个方法,返回消息列表

//在这里编写方法,可以返回在线用户列表
    public static String getOnlineFriend() {
        //集合遍历,遍历hashmap的key
        Iterator<String> iterator=hm.keySet().iterator();
        String onlineUserList="";
        while(iterator.hasNext()) {
            onlineUserList+= iterator.next().toString()+" ";
        }
        return onlineUserList;
    }

2.无异常退出

 2.1客户端无异常退出

在UserClientService里面写一个方法,退出客户端,并给服务端发送一个退出系统的message对象

//编写一个方法,退出客户端,并给服务端发送一个退出系统的message对象
    public void logout() {
        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_CLIENT_EXIT);
        message.setSender(user.getUserId());//指定是哪一个客户端id
        //发送message
        ClientConnectServerThread clientConnectServerThread =
                ManageClientConnectServerThread.getClientConnectServerThread(user.getUserId());
        Socket socket1 = clientConnectServerThread.getSocket();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(socket1.getOutputStream());
            oos.writeObject(message);
            System.out.println(user.getUserId()+" 退出系统 ");
            System.exit(0);//结束进程
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

在QQView里面的输入9时,调用该方法

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

public class QQView {
    private boolean loop=true;//用于控制用户退出与登陆
    private String key="";//用于接收用户输入
    private UserClientService userClientService=new UserClientService();//对象用来登陆服务/注册用户
    public static void main(String args[]) {
        new QQView().mainMenu();
    }

    //显示主菜单
    public void mainMenu(){
        while(loop) {
            System.out.println("===============欢迎进入即时通信系统===============");
            System.out.println("\t\t1.登陆系统");
            System.out.println("\t\t9.退出系统");
            System.out.print("请输入你的选择:");
            key= Utility.readString(1);
            switch (key) {
                case "1":
                    System.out.print("请输入你的用户名:");
                    String userId=Utility.readString(20);
                    System.out.print("请输入你的密  码:");
                    String pwd= Utility.readString(20);
                    //此处省略若干....需要到服务器端去验证该用户是否合法
                    //编写一个类UserClientService

                    if(userClientService.checkUser(userId,pwd)) {
                        //进入二级菜单
                        while(loop) {
                            System.out.println("===============欢迎用户"+userId+"进入系统===============");
                            System.out.println("\t\t1.显示在线用户列表");
                            System.out.println("\t\t2.群发消息");
                            System.out.println("\t\t3.私聊消息");
                            System.out.println("\t\t4.发送文件");
                            System.out.println("\t\t9.退出系统");
                            System.out.print("请输入你的选择:");
                            key=Utility.readString(1);
                            switch(key) {
                                case "1":
                                    //写一个方法来拉取在线用户列表
                                    userClientService.onlineFriendList();
                                    //System.out.println("显示在线用户列表");
                                    break;
                                case "2":
                                    System.out.println("群发消息");
                                    break;
                                case "3":
                                    System.out.println("私聊消息");
                                    break;
                                case "4":
                                    System.out.println("发送文件");
                                    break;
                                case "9":
                                    //调用一个方法,给服务器发送一个退出系统的message
                                    userClientService.logout();
                                    loop=false;
                                    break;
                            }
                        }
                    } else {
                        System.out.println("登陆失败...");
                    }
                    break;
                case "9":
                    loop=false;
                    System.out.println("退出系统...");
                    break;
            }
        }
    }
}
2.2服务器端无异常退出

在ManageServerThread里面加一个方法,从集合中删除指定的线程对象

//从集合中移除某个线程对象
    public static void removeServerConnectClientThread(String userId) {
        hm.remove(userId);
    }

在ServerConnectClientThread里面加一个判断的条件,当收到的是客户端请求退出的消息时,做出相应的判断

import com.qq.common.Message;
import com.qq.common.MessageType;

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

//该类的对象和某个客户端保持通信
public class ServerConnectClientThread extends Thread{
    private Socket socket;
    private String userId;//用来区别与哪一个用户保持通讯

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

    @Override
    public void run() {//该线程一直处于运行状态,可以接收和发送消息
        while(true) {
            System.out.println("服务器端与客户端 "+userId+" 保持通信,读取数据....");
            try {
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message=(Message)ois.readObject();
                //后面通过message传发消息
                //根据message的类型,做相应的判断
                if(message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)) {
                    //客户端需要在线用户列表
                    System.out.println(message.getSender()+" 要在线用户列表");
                    //得到需要返回的信息
                    String onlineFriend = ManageServerThread.getOnlineFriend();
                    //返回message给客户端
                    message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);
                    message.setContent(onlineFriend);
                    message.setGetter(message.getGetter());
                    //写入到数据通道,返回给客户端
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(message);
                }
                else if(message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)) {//客户端退出
                    System.out.println(message.getSender()+"退出");
                    //将这个客户端对应的线程从集合中删除
                    ManageServerThread.removeServerConnectClientThread(message.getSender());
                    //关闭连接
                    socket.close();
                    //退出循环
                    break;
                }
                else {
                    System.out.println("其他类型的message,暂时不处理");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

;