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);
}
}
}
}