文件传输
服务器推送
离线信息(登录之后把离线的时候别人发的信息发送,支持多条)
服务端:
因为序列化和反序列化的原因,在转送user和Message类型的对象的时候,包名也要一样,不然会报错。
客户端和服务端之间的通信对象
package com.hwk.qqcommon;
import java.io.Serializable;
/*
*客户端和服务端之间的通信对象
*/
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[] fileByte;
public byte[] getFileByte() {
return fileByte;
}
public void setFileByte(byte[] fileByte) {
this.fileByte = fileByte;
}
public int getFileLen() {
return fileLen;
}
public void setFileLen(int fileLen) {
this.fileLen = fileLen;
}
public String getDest() {
return dest;
}
public void setDest(String dest) {
this.dest = dest;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
private int fileLen;
private String dest;
private String source;
public Message(String sender, String getter, String content, String sendTime) {
this.sender = sender;
this.getter = getter;
this.content = content;
this.sendTime = sendTime;
}
public Message(){
}
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;
}
@Override
public String toString() {
return "Message{" +
"sender='" + sender + '\'' +
", getter='" + getter + '\'' +
", content='" + content + '\'' +
", sendTime='" + sendTime + '\'' +
", mesType='" + mesType + '\'' +
'}';
}
}
登录用到的
package com.hwk.qqcommon;
public interface MessageType {
String MESSAGE_LONG_SUCCESS = "1";//登陆成功的
String MESSAGE_LONG_FAILE = "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_T0_ALL_MES = "7";//群发的消息
String MESSAGE_FILE_MES = "8";//文件的发送
}
登录的类型
package com.hwk.qqcommon;
import java.io.Serializable;
/*
*@author hwk
* 表示一个用户信息
*/
public class User implements Serializable {
private String userId;
private String passwd;
private static final long serialVersionUID = 1L;//提高兼容性
public User(String userId, String passwd) {
this.userId = userId;
this.passwd = passwd;
}
public User(){
}
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;
}
@Override
public String toString() {
return "User{" +
"userId='" + userId + '\'' +
", passwd='" + passwd + '\'' +
'}';
}
}
服务器的登录管理
package com.hwk.qqserve;
import com.hwk.qqcommon.Message;
import com.hwk.qqcommon.MessageType;
import com.hwk.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.Iterator;
import java.util.concurrent.ConcurrentHashMap;
//服务器管理
public class qqserver {
//提升成员变量,扩大作用域
private ServerSocket serverSocket;
//线程安全,比hashmap安全
private static ConcurrentHashMap validUser = new ConcurrentHashMap<>(15);
private static ConcurrentHashMap> messdb = new ConcurrentHashMap<>();
// 离线信息的存储
public static ConcurrentHashMap> getMessdb() {
return qqserver.messdb;
}
public static ArrayList getlxList(String key1){
return qqserver.messdb.get(key1);
}
public static void addMessdb(String key ,Message message) {
ArrayList arrayList = new ArrayList<>();
if(qqserver.getlxList(key) != null){
arrayList = qqserver.getlxList(key);
}
arrayList.add(message);
qqserver.messdb.put(key,arrayList);
}
//初始化
static {
validUser.put("100",new User("100","123"));
validUser.put("hwk",new User("hwk","12345"));
validUser.put("200",new User("200","123"));
validUser.put("300",new User("300","123"));
validUser.put("舞剑",new User("舞剑","123"));
validUser.put("紫霞",new User("紫霞","321"));
}
//构造函数里执行
public qqserver() {
System.out.println("服务器在9990端口监听");
try {
serverSocket = new ServerSocket(9990);
new Thread(new sendNewstoAll()).start();
Socket socket;
while (true){
socket = serverSocket.accept();
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//接收客户端发的用户信息是否在服务端有记录
User user1 = (User) ois.readObject();
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
Message mes = new Message();
//有记录就放回MESSAGE_LONG_SUCCESS类型的mes
if(checkUser(user1.getUserId(),user1.getPasswd())){
mes.setMesType(MessageType.MESSAGE_LONG_SUCCESS);
oos.writeObject(mes);
servserConnectClinetThread servserConnectClinetThread = new servserConnectClinetThread(socket, user1.getUserId());
servserConnectClinetThread.start();
ManageClientConnectServe.addClentConnetServierThread(user1.getUserId(),servserConnectClinetThread);
ArrayList arrayList = null;
if((arrayList = qqserver.getlxList(user1.getUserId())) != null){
for (Message mes1 : arrayList ) {
ObjectOutputStream oos1 = new ObjectOutputStream(servserConnectClinetThread.getSocket().getOutputStream());
oos1.writeObject(mes1);
System.out.println(mes1.getSender()+"对"+mes1.getGetter()+"说:"+mes1.getContent());
}
qqserver.messdb.remove(user1.getUserId());
}
}
//发送登录失败的记录
else{
mes.setMesType(MessageType.MESSAGE_LONG_FAILE);
oos.writeObject(mes);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
//循环最后关闭serverSocket
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//检测用户传的user是否在map里面
public boolean checkUser(String userId,String pwd){
User user = validUser.get(userId);
if(user == null){
return false;
}
if(!user.getPasswd().equals(pwd)){
return false;
}
return true;
}
}
服务器的线程管理
package com.hwk.qqserve;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
//服务端线程管理,包括socekt的属性
public class ManageClientConnectServe {
private static ConcurrentHashMap hm = new ConcurrentHashMap<>();
//将线程加到map里面
public static void addClentConnetServierThread(String userId, servserConnectClinetThread servserConnectClinetThread){
hm.put(userId,servserConnectClinetThread);
}
//获得id线程
public static servserConnectClinetThread getservserConnectClinetThread(String userId){
return hm.get(userId);
}
//获得当前在线用户名
public static String getOnlineUsers(){
Set keySet = hm.keySet();
Iterator iterator = keySet.iterator();
String onlineUserList = "";
while (iterator.hasNext()){
onlineUserList += iterator.next().toString()+" ";
}
return onlineUserList;
}
//
public static void removeConnectClinetThread(String userid){
hm.remove(userid);
}
public static ConcurrentHashMap getHm() {
return hm;
}
}
服务器线程管理
package com.hwk.qqserve;
import com.hwk.qqcommon.Message;
import com.hwk.qqcommon.MessageType;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
//自定义线程
public class servserConnectClinetThread extends Thread{
private Socket socket;
private String userId;
public servserConnectClinetThread(Socket socket, String userId) {
this.socket = socket;
this.userId = userId;
}
@Override
public void run() {
while (true){
//不断循环执行
System.out.println("服务器和客服端保持通信");
try {
//读取用户发的信息
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
System.out.println("服务器和客户端"+userId+"保持通信,读取数据中");
Message mes = (Message) ois.readObject();
//判断是否是请求用户列表的
if(mes.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)){
System.out.println(mes.getSender()+"要在线用户的列表");
//放回用户列表
String onlineUser = ManageClientConnectServe.getOnlineUsers();
Message mes1 = new Message();
//十设置相关属性
mes1.setContent(onlineUser);
mes1.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND);
mes1.setGetter(mes.getSender());
mes1.setSender(userId);
//发出
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(mes1);
}
else if(mes.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)){
System.out.println(mes.getSender()+"退出系统了");
ManageClientConnectServe.removeConnectClinetThread(mes.getSender());
socket.close();
break;
}
else if(mes.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
try {
servserConnectClinetThread servserConnectClinetThread = ManageClientConnectServe.getservserConnectClinetThread(mes.getGetter());
//看接收方是否登录
if(servserConnectClinetThread != null){
System.out.println(mes.getSender()+"对"+mes.getGetter()+"说:"+mes.getContent());
ObjectOutputStream oos = new ObjectOutputStream(servserConnectClinetThread.getSocket().getOutputStream());
oos.writeObject(mes);
}
//离线信息的存储
else{
qqserver.addMessdb(mes.getGetter(),mes);
}
} catch (IOException e) {
}
}
else if(mes.getMesType().equals(MessageType.MESSAGE_T0_ALL_MES)){
String onlineName = "";
ConcurrentHashMap hm = ManageClientConnectServe.getHm();
Iterator iterator = hm.keySet().stream().iterator();
while(iterator.hasNext()){
onlineName = iterator.next().toString();
if(!onlineName.equals(mes.getSender())){
ObjectOutputStream oos = new ObjectOutputStream(hm.get(onlineName).getSocket().getOutputStream());
oos.writeObject(mes);
}
}
System.out.println(mes.getSender()+"对大家说:"+mes.getContent());
}
else if(mes.getMesType().equals(MessageType.MESSAGE_FILE_MES)){
ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServe.getservserConnectClinetThread(mes.getGetter()).getSocket().getOutputStream());
oos.writeObject(mes);
}
else{
System.out.println("其他message,不做处理!");
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
}
}
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
服务器实现消息推送和退出
package com.hwk.qqserve;
import com.hwk.qqcommon.Message;
import com.hwk.qqcommon.MessageType;
import com.hwk.utils.utility;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class sendNewstoAll implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("请输入服务器要推送的新闻/消息 输入exit退出推送/quit退出服务器");
String onlineUser = "";
String str = utility.readString(50);
if(str.equals("exit")){
break;
}
if(str.equals("quit")){
System.exit(0);
}
Message message = new Message();
message.setSender("服务器");
message.setContent(str);
message.setSendTime(new Date().toString());
message.setMesType(MessageType.MESSAGE_T0_ALL_MES);
System.out.println("服务器给所有人说" + message.getContent());
//遍历所有线程
ConcurrentHashMap hm = ManageClientConnectServe.getHm();
Set keySet = hm.keySet();
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
onlineUser = iterator.next();
try {
servserConnectClinetThread servserConnectClinetThread = hm.get(onlineUser);
ObjectOutputStream oos = new ObjectOutputStream(servserConnectClinetThread.getSocket().getOutputStream());
oos.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
入口
package com.hwk.qqframe;
import com.hwk.qqserve.qqserver;
//创建server,启动后台服务
public class qqframe {
public static void main(String[] args) {
new qqserver();
}
}
package com.hwk.qqclient.view;
import com.hwk.qqclient.service.FileClientService;
import com.hwk.qqclient.service.UserClientService;
import com.hwk.qqclient.service.messageClient;
import com.hwk.qqclient.utils.utility;
public class view {
private boolean loop =true;
private String key = "";
//定义用户工具成员
private final UserClientService userClientService = new UserClientService();
//信息控制的
private messageClient messageClient1 = new messageClient();
//文件处理的
private FileClientService fileClientService = new FileClientService();
public static void main(String[] args) throws Exception{
new view().mainMenu();
}
//菜单
private void mainMenu() throws Exception{
while (loop){
System.out.println("-------------欢迎登录网络通信系统-------------");
System.out.println("\t\t 1 登录系统");
System.out.println("\t\t 9 退出系统");
System.out.println("输入你的需求:");
key = utility.readString(1);
switch(key){
case "1":
System.out.println("输入用户号");
String userId = utility.readString(50);
System.out.println("输入密码");
String pwd = utility.readString(50);
//判断登陆是否成功
if(userClientService.checkedUser(userId,pwd)){
System.out.println("-------------欢迎"+userId+"登录网络通信系统-------------");
while (loop){
System.out.println("\n-------------欢迎"+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 退出系统");
System.out.println("输入你的选择:");
key = utility.readString(1);
switch (key){
case "1":
//在线用户显示
userClientService.onlineFreindList();
break;
case "2":
System.out.println("输入想对大家说的话:");
String str = utility.readString(100);
messageClient1.sendMessagetoAll(str,userId);
//群聊方法
break;
case "3":
System.out.println("输入想私聊的用户名");
String getterId = utility.readString(50);
System.out.println("输入想说的话:");
String content = utility.readString(100);
//编写方法,发送请求给服务器,私聊
messageClient1.sendMessagetoOne(content,userId,getterId);
break;
case "4":
System.out.println("输入目标用户");
String getterId1 = utility.readString(50);
System.out.println("输入你的文件地址(形式 d:\\\\xx.jpg)");
String src = utility.readString(50);
System.out.println("输入目标用户的文件地址");
String dest = utility.readString(51);
// messageClient1.
fileClientService.sendFiletoOne(src,dest,userId,getterId1);
break;
case "9":
loop = false;
//通知服务端退出系统
userClientService.logout();
break;
}
}
}
else{
System.out.println("-------------登录失败-------------");
}
break;
case "9":
loop = false;
break;
}
}
}
}
工具类
package com.hwk.qqclient.utils;
/**
工具类的作用:
处理各种情况的用户输入,并且能够按照程序员的需求,得到用户的控制台输入。
*/
import java.util.*;
/**
*/
public class utility {
//静态属性。。。
private static Scanner scanner = new Scanner(System.in);
/**
* 功能:读取键盘输入的一个菜单选项,值:1——5的范围
* @return 1——5
*/
public static char readMenuSelection() {
char c;
for (; ; ) {
String str = readKeyBoard(1, false);//包含一个字符的字符串
c = str.charAt(0);//将字符串转换成字符char类型
if (c != '1' && c != '2' &&
c != '3' && c != '4' && c != '5') {
System.out.print("选择错误,请重新输入:");
} else break;
}
return c;
}
/**
* 功能:读取键盘输入的一个字符
* @return 一个字符
*/
public static char readChar() {
String str = readKeyBoard(1, false);//就是一个字符
return str.charAt(0);
}
/**
* 功能:读取键盘输入的一个字符,如果直接按回车,则返回指定的默认值;否则返回输入的那个字符
* @param defaultValue 指定的默认值
* @return 默认值或输入的字符
*/
public static char readChar(char defaultValue) {
String str = readKeyBoard(1, true);//要么是空字符串,要么是一个字符
return (str.length() == 0) ? defaultValue : str.charAt(0);
}
/**
* 功能:读取键盘输入的整型,长度小于2位
* @return 整数
*/
public static int readInt() {
int n;
for (; ; ) {
String str = readKeyBoard(10, false);//一个整数,长度<=10位
try {
n = Integer.parseInt(str);//将字符串转换成整数
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的 整数或默认值,如果直接回车,则返回默认值,否则返回输入的整数
* @param defaultValue 指定的默认值
* @return 整数或默认值
*/
public static int readInt(int defaultValue) {
int n;
for (; ; ) {
String str = readKeyBoard(10, true);
if (str.equals("")) {
return defaultValue;
}
//异常处理...
try {
n = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的指定长度的字符串
* @param limit 限制的长度
* @return 指定长度的字符串
*/
public static String readString(int limit) {
return readKeyBoard(limit, false);
}
/**
* 功能:读取键盘输入的指定长度的字符串或默认值,如果直接回车,返回默认值,否则返回字符串
* @param limit 限制的长度
* @param defaultValue 指定的默认值
* @return 指定长度的字符串
*/
public static String readString(int limit, String defaultValue) {
String str = readKeyBoard(limit, true);
return str.equals("")? defaultValue : str;
}
/**
* 功能:读取键盘输入的确认选项,Y或N
* 将小的功能,封装到一个方法中.
* @return Y或N
*/
public static char readConfirmSelection() {
System.out.println("请输入你的选择(Y/N): 请小心选择");
char c;
for (; ; ) {//无限循环
//在这里,将接受到字符,转成了大写字母
//y => Y n=>N
String str = readKeyBoard(1, false).toUpperCase();
c = str.charAt(0);
if (c == 'Y' || c == 'N') {
break;
} else {
System.out.print("选择错误,请重新输入:");
}
}
return c;
}
/**
* 功能: 读取一个字符串
* @param limit 读取的长度
* @param blankReturn 如果为true ,表示 可以读空字符串。
* 如果为false表示 不能读空字符串。
*
* 如果输入为空,或者输入大于limit的长度,就会提示重新输入。
* @return
*/
private static String readKeyBoard(int limit, boolean blankReturn) {
//定义了字符串
String line = "";
//scanner.hasNextLine() 判断有没有下一行
while (scanner.hasNextLine()) {
line = scanner.nextLine();//读取这一行
//如果line.length=0, 即用户没有输入任何内容,直接回车
if (line.length() == 0) {
if (blankReturn) return line;//如果blankReturn=true,可以返回空串
else continue; //如果blankReturn=false,不接受空串,必须输入内容
}
//如果用户输入的内容大于了 limit,就提示重写输入
//如果用户如的内容 >0 <= limit ,我就接受
if (line.length() < 1 || line.length() > limit) {
System.out.print("输入长度(不能大于" + limit + ")错误,请重新输入:");
continue;
}
break;
}
return line;
}
}
客户端线程管理
package com.hwk.qqclient.service;
import java.util.concurrent.ConcurrentHashMap;
//线程管理,是一个ConcurrentHashMap,线程安全
public class ManageClientConnectServe {
private static ConcurrentHashMap hm = new ConcurrentHashMap<>();
public static void addClentConnetServierThread(String userId, clientContentServerThread clientContentServerThread){
hm.put(userId,clientContentServerThread);
}
public static clientContentServerThread getclientContentServerThread(String userId){
return hm.get(userId);
}
}
客户端的线程
package com.hwk.qqclient.service;
import com.hwk.qqcommon.Message;
import com.hwk.qqcommon.MessageType;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
//用户线程,线程里有socket属性来联系服务
public class clientContentServerThread extends Thread{
private Socket socket;
public clientContentServerThread(Socket socket){
this.socket = socket;
}
public Socket getSocket() {
return socket;
}
@Override
public void run() {
ObjectInputStream ois = null;
System.out.println("客户端线程等待,从服务器端来的数据");
while (true){
try {
ois = new ObjectInputStream(socket.getInputStream());
// 如果服务器没有发送Message对象,线程会阻塞在这里
Message mes = (Message) ois.readObject();
if(mes.getMesType().equals(MessageType.MESSAGE_RET_ONLINE_FRIEND)){
String[] onlienUser = mes.getContent().split(" ");
System.out.println("当前在线的用户列表");
for(int i = 0;i < onlienUser.length; i++){
System.out.println("用户"+onlienUser[i]);
}
}
else if(mes.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
System.out.println("\n"+mes.getSender()+"对"+mes.getGetter()+"说:"+mes.getContent());
}
else if(mes.getMesType().equals(MessageType.MESSAGE_T0_ALL_MES)){
System.out.println("\n"+mes.getSender()+"对大家说:"+mes.getContent());
}
else if(mes.getMesType().equals(MessageType.MESSAGE_FILE_MES)){
System.out.println("\n"+mes.getSender()+"给"+mes.getGetter()+"发文件"+mes.getSource()+"到"+mes.getDest());
FileOutputStream fileOutputStream = new FileOutputStream(mes.getDest());
fileOutputStream.write(mes.getFileByte());
fileOutputStream.close();
System.out.println("保存文件成功");
}
else{
System.out.println("其他类型,不做处理的");
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
线程的登录工具类
package com.hwk.qqclient.service;
import com.hwk.qqcommon.Message;
import com.hwk.qqcommon.MessageType;
import com.hwk.qqcommon.User;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
//登录处理的
public class UserClientService {
private User user1 = new User();
private Socket socket;
public boolean checkedUser(String name,String pwd) throws Exception{
boolean b =false;
user1.setUserId(name);
user1.setPasswd(pwd);
socket = new Socket(InetAddress.getByName("127.0.0.1"), 9990);
//发送用户信息给服务器
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(user1);
//读取服务器的信息
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message mes = (Message) ois.readObject();
//判断服务器发送的信息的类型,是MESSAGE_LONG_SUCCESS就登录成功
if(mes.getMesType().equals(MessageType.MESSAGE_LONG_SUCCESS)){
//创建自定义线程
clientContentServerThread ccst = new clientContentServerThread(socket);
ccst.start();
//放入线程池,为了多线程
b = true;
ManageClientConnectServe.addClentConnetServierThread(name,ccst);
}
else{
//登录不成功关闭主线程的套字节
socket.close();
}
//返回是否登录成功
return b;
}
//向服务端获取在线用户的列表
public void onlineFreindList() throws Exception{
Message mes = new Message();
//设置请求类型
mes.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);
//说明发送者
mes.setSender(user1.getUserId());
try {
//客户端也可以开启多个线程,所有不能直接用socket,取得当前线程的读取流
ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServe.getclientContentServerThread(user1.getUserId()).getSocket().getOutputStream());
oos.writeObject(mes);//发送要用户请求
} catch (IOException e) {
e.printStackTrace();
}
}
//通知服务端退出系统
public void logout(){
Message mes = new Message();
mes.setMesType(MessageType.MESSAGE_CLIENT_EXIT);
mes.setSender(user1.getUserId());
try {
ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServe.getclientContentServerThread(user1.getUserId()).getSocket().getOutputStream());
oos.writeObject(mes);
System.out.println(user1.getUserId()+"退出系统了");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
线程的私聊工具类
package com.hwk.qqclient.service;
import com.hwk.qqcommon.Message;
import com.hwk.qqcommon.MessageType;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;
//提供给消息相关的方法
public class messageClient {
public void sendMessagetoOne(String content,String senderId,String getterId){
Message mes = new Message();
mes.setSender(senderId);
mes.setContent(content);
mes.setGetter(getterId);
mes.setSendTime(new Date().toString());
mes.setMesType(MessageType.MESSAGE_COMM_MES);
System.out.println(senderId+"对"+getterId+"说:"+content);
try {
ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServe.getclientContentServerThread(senderId).getSocket().getOutputStream());
oos.writeObject(mes);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
public void sendMessagetoAll(String content,String senderId){
Message message = new Message();
message.setMesType(MessageType.MESSAGE_T0_ALL_MES);
message.setContent(content);
message.setSender(senderId);
message.setSendTime(new Date().toString());
System.out.println(senderId+"对大家说:"+content);
try {
ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServe.getclientContentServerThread(senderId).getSocket().getOutputStream());
oos.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
线程的文件传输工具类
package com.hwk.qqclient.service;
import com.hwk.qqcommon.Message;
import com.hwk.qqcommon.MessageType;
import java.io.*;
public class FileClientService {
public void sendFiletoOne(String src,String dest,String senderId,String getterId) throws IOException {
Message message = new Message();
message.setMesType(MessageType.MESSAGE_FILE_MES);
message.setSender(senderId);
message.setGetter(getterId);
message.setSource(src);
message.setDest(dest);
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(src);
byte[] bytes = new byte[(int) (new File(src).length())];
fileInputStream.read(bytes);
message.setFileByte(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fileInputStream !=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ObjectOutputStream oos =new ObjectOutputStream(ManageClientConnectServe.getclientContentServerThread(senderId).getSocket().getOutputStream());
oos.writeObject(message);
System.out.println(message.getSender()+"发送"+message.getGetter()+"文件"+message.getSource());
}
}
qqcomom包里的类和服务端的一模一样。
之前的功能,韩老师已经讲得很透彻了,不懂的建议去b站上去看,
这个离线本质就是服务端有一个hashmap(就理解成一个特殊的数组,只是这个数组的索引不是数组,变成了别的类型,必须唯一))。
用户发送私聊信息的时候,服务器接收,在转发的时候根据getterId判断,线程的集合里面是否有这个接收者的线程,也就是接收者登录美,如果登录,就转发,如果不登录就存到准备好的消息集合里;
此外用户在登录的时候,服务器需要根据客户端发送的这个user的里面的userid来判断这个消息集合里是否为空,也就是这个用户离线的时候有没有人发私聊信息给他,如果为空,则没有,就不管;如果有,就通过服务器遍历集合里该对象的消息列表,逐条转发过去,并且在转发之后,删除在集合里关于这个客户端的离线消息。
在转发的时候,每循环一次需要建立一个输出流,否则会报错。