综合项目实训 —— 聊天室
本篇博文是本人《Java SE》专栏的最后一篇博文了!
虽然还有些不舍,但是,在将近一学期的学习中,SE的讲解终究是迎来了尾声
那么,作为收尾博文,本人将在本篇博文中带着同学们来重温一下我们在SE学习阶段所学到的常用知识点。
那么,话不多说,现在,本人就开始本篇博文的主题的讲解吧:
首先,我们要做聊天室,就要清楚一个问题:
无论是服务器与客户端的聊天,还是客户端与客户端的聊天,它们的本质都是客户端与服务器之间的聊天
基本功能:
那么,本人再来介绍下此次所要制作的聊天室项目所具备的功能吧:
功能:
- 私聊
- 公聊
- 在线列表
- 下线
- 发送文件
- 隐身/活跃状态 切换
- 查询聊天记录
工具类:
那么,为了方便我们之后的代码编写,本人先来给出三个工具类:
1.处理用户输入信息的 InputUtil 工具:
package edu.youzg.chatroom.utils;
import java.util.Scanner;
public class InputUtil {
public static int inputNeededIntType(int start, int end){
Scanner sc = new Scanner(System.in);
int choose = 0;
while(true){
try {
choose = sc.nextInt();
if (choose >= start && choose <= end) {
break;
}
} catch (Exception e) {
sc = new Scanner(System.in);
}
System.out.println("输入的类型不正确请重新输入整数:");
}
return choose;
}
public static String inputUserName() {
Scanner sc = new Scanner(System.in);
String res = "";
String pattern = "[a-zA-Z][a-zA-Z_]\\w{4,14}";
while (true) {
System.out.println("请输入6~16位用户名:(只能以 数字/字母/下划线 组成,且只能以字母开头)");
res = sc.nextLine();
if (res.matches(pattern)) {
break;
}
sc = new Scanner(System.in);
System.out.println("输入的类型不正确,请重新输入:");
}
return res;
}
public static String inputUserPassword() {
Scanner sc = new Scanner(System.in);
String res = "";
String pattern = ".{6,20}";
while (true) {
System.out.println("请输入6~20位密码:");
res = sc.nextLine();
if (res.matches(pattern)) {
break;
}
sc = new Scanner(System.in);
System.out.println("输入的类型不正确,请重新输入:");
}
return res;
}
public static String inputChoice() {
Scanner sc = new Scanner(System.in);
String res = "";
String pattern = "[y,n]";
while (true) {
System.out.println("是否接收?y/n");
res = sc.nextLine();
if (res.matches(pattern)) {
break;
}
sc = new Scanner(System.in);
System.out.println("输入的类型不正确,请重新输入:");
}
return res;
}
}
2.将 毫秒值式 的日期,转换为指定格式 的 TimeUtil 工具:
package edu.youzg.chatroom.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TimeUtil {
//将dateStr转换为formatStr格式的日期字符串
public static String changeDate2Week(String dateStr,String formatStr){
SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
Date date = null;
try {
date = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
SimpleDateFormat sdf1 = new SimpleDateFormat("E");
return sdf1.format(date);
}
//把毫秒值转换成日器字符串
public static String changeMils2Date(long mils,String formatStr){
SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
Date date = new Date(mils);
return sdf.format(date);
}
}
3.用于传输文件的 InputAndOutputUtil工具:
package edu.youzg.chatroom.utils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class InputAndOutputUtil {
public static byte[] readFile(String path){
File file = new File(path);
byte datas[] = null;
if(!file.exists()){
datas = null;
} else {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileInputStream fis = new FileInputStream(file);
byte data[] = new byte[1024*1024];
int len = 0;
while((len = fis.read(data))>0){
baos.write(data, 0, len);
}
datas = baos.toByteArray();
baos.flush();
baos.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return datas;
}
public static boolean writeFile(String path,byte datas[]){
try {
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
fos.write(datas);
fos.flush();
fos.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
思路:
那么,我们还要明确两个问题 :
- 客户端和服务器之间传送的信息是何种类型?
- 对端接收到本端信息后,如何识别?
这就牵扯到一个非常高大上、但是很容易理解的名词 —— 协议:
说简单的,就是双方所指定的规则。
也就是说,我们规定好消息的类型,以及消息的识别方式。
那么,现在,本人来给出本人此篇博文所需要的消息类型:
首先是用于表名消息的请求类型的枚举:
package edu.youzg.chatroom.config;
public enum MsgType {
ONLINE, //上线提醒
PRIVATE, //私聊
PUBLIC, //公聊
CLIENT_INEXISTENCE, //好友不在线或不存在
FRIEND_LIST, //在线列表
PASS_FILE, //文件传输
ACCEPT, //对端接收文件
REFUSE, //对端拒绝接收文件
EXIT, //下线
SWITCH, //切换 隐身/在线 状态
SWITCH_ACTIVE, //切换至在线状态
SWITCH_HIDDEN, //切换至隐身状态
QUERY_PUBLIC, //查询公聊内容
QUERY_PRIVATE, //查询私聊内容
USER_CONTAINED, //用户名已存在
ILLEAGAL_USER, //非法用户名
REGISTER, //请求注册
REGISTER_SUCCESSFUL, //注册成功
REGISTER_FAILURE, //注册失败
LOGIN, //请求登录
LOGIN_OK, //允许登录
LOGIN_NO, //不允许登录
}
然后是存储消息的类:
package edu.youzg.chatroom.bean;
import edu.youzg.chatroom.server.config.MsgType;
import java.io.Serializable;
public class ChatMsgBean implements Serializable { //实现序列化接口
private static final long serialVersionUID = 1L;
private String reciver;//接收者
private String sender;//发送者
private String content;//消息内容
private long time;//时间
private MsgType msgType;//消息类型
private String fileName;//文件名
private long fileLength;//文件大小
private byte fileData[];//文件的字节数据
//构造方法 封装普通消息的数据
public ChatMsgBean(String reciver, String sender, String content,
long time, MsgType msgType) {
super();
this.reciver = reciver;
this.sender = sender;
this.content = content;
this.time = time;
this.msgType = msgType;
}
//封装发送文件所需数据
public ChatMsgBean(String reciver, String sender, String content,
long time, MsgType msgType, String fileName, long fileLength,
byte[] fileData) {
super();
this.reciver = reciver;
this.sender = sender;
this.content = content;
this.time = time;
this.msgType = msgType;
this.fileName = fileName;
this.fileLength = fileLength;
this.fileData = fileData;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public String getReciver() {
return reciver;
}
public void setReciver(String reciver) {
this.reciver = reciver;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public MsgType getMsgType() {
return msgType;
}
public void setMsgType(MsgType msgType) {
this.msgType = msgType;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getFileLength() {
return fileLength;
}
public void setFileLength(long fileLength) {
this.fileLength = fileLength;
}
public byte[] getFileData() {
return fileData;
}
public void setFileData(byte[] fileData) {
this.fileData = fileData;
}
}
有了上述 小工具 以及 协议,本人就将博文分为两个部分 —— 服务器端 和 客户端 来讲解吧:
服务器端
在本人 《详解 网络编程》 这篇博文中就讲过,我们若是通过TCP协议进行网络编程,就要先通过服务器去侦听客户端,在侦听到客户端之后,我们就开启处理该客户端 登录/注册 请求的线程。
那么,依照上述原理,本人来编写下代码:
package edu.youzg.chatroom.server.ui;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class MyServer {
public static void main(String[] args) {
HashMap<String, ObjectOutputStream> hm = new HashMap<String, ObjectOutputStream>();
List<String> activeClients = new ArrayList<String>();
try {
ServerSocket ss = new ServerSocket(6666);
System.out.println("服务器已经开启,等待连接...");
//循环侦听连接上来的客户端
int i = 1;
while (true) {
Socket sk = ss.accept(); //侦听客户端
System.out.println("第" + (i++) + "个客户端已连接!");
new SaveUserThread(hm, activeClients, sk).start(); //侦听到客户端后,开启处理该客户端 登录/注册 请求的线程
}
} catch (IOException e) {
}
}
}
在侦听到客户端后,我们就要询问客户端要登录还是进行账号注册,所以,本人还是通过一个线程来处理这个需求:
package edu.youzg.chatroom.server.ui;
import edu.youzg.chatroom.bean.ChatMsgBean;
import edu.youzg.chatroom.config.MsgType;
import edu.youzg.chatroom.utils.TimeUtil;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.Set;
//这个线程,专门用来处理 注册/登录
public class SaveUserThread extends Thread {
HashMap<String, ObjectOutputStream> hm;
List<String> activeClients;
Socket sk;
ObjectOutputStream out;
ObjectInputStream in;
private String userName;
public SaveUserThread(HashMap<String, ObjectOutputStream> hm, List<String> activeClients, Socket sk) throws FileNotFoundException {
this.hm = hm;
this.activeClients = activeClients;
this.sk = sk;
}
@Override
public void run() {
try {
//注册好了之后
//1.读取客户端,发来的用户名进行注册。
//包装通道中的字节流
out = new ObjectOutputStream(sk.getOutputStream());
in = new ObjectInputStream(sk.getInputStream());
boolean flag = true;
while (flag) {
ChatMsgBean bean = (ChatMsgBean) in.readObject();
userName = bean.getSender();
MsgType type = bean.getMsgType();
if (type == MsgType.REGISTER) { //处理注册
dealRegister(bean);
} else { //处理登录
flag = dealLogin(bean);
}
}
//2.上线功能也可以做
//遍历集合,取出每个人的管道,给他发一个谁谁谁上线了的提醒
Set<String> keySet = hm.keySet();
for (String key : keySet) {
//排除自己,不要给自己发上线提醒
if (userName.equals(key)) {
continue;
}
ChatMsgBean bean = new ChatMsgBean(key, userName, null, System.currentTimeMillis(), MsgType.ONLINE);
hm.get(key).writeObject(bean);
}
//最后开启 聊天线程,把集合 发送者,还有sk 传到读取消息的线程
new ServerTransmitThread(hm, activeClients, userName, in).start();
} catch (SocketException e) {
hm.remove(userName);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private boolean dealLogin(ChatMsgBean bean) throws IOException {
Properties properties = new Properties();
properties.load(new FileReader("userInfo.properties"));
String password = (String) properties.get(userName);
if (bean.getContent().equals(password)) { //用户名存在,检验密码
if (hm.containsKey(userName)) {
out.writeObject(new ChatMsgBean(userName, null, null, System.currentTimeMillis(), MsgType.USER_CONTAINED));
return true;
}
out.writeObject(new ChatMsgBean(userName, null, null, System.currentTimeMillis(), MsgType.LOGIN_OK));
hm.put(userName, out);
activeClients.add(userName);
System.out.println(TimeUtil.changeMils2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss:"));
System.out.println("客户端[" + userName + "]上线!");
return false;
} else { //用户名不存在
out.writeObject(new ChatMsgBean(userName, null, null, System.currentTimeMillis(), MsgType.LOGIN_NO));
return true;
}
}
private void dealRegister(ChatMsgBean bean) throws IOException {
if (userName == "-q") {
out.writeObject(new ChatMsgBean(userName, null, null, System.currentTimeMillis(), MsgType.ILLEAGAL_USER));
return;
}
Properties properties = new Properties();
properties.load(new FileReader("userInfo.properties"));
if (!properties.stringPropertyNames().contains(bean.getSender())) {
synchronized (SaveUserThread.class) {
properties.clear(); //防止我们向文件中录入之前文件中就存在的值
properties.setProperty(userName, bean.getContent());
properties.store(new FileWriter("userInfo.properties", true), null);
}
out.writeObject(new ChatMsgBean(userName, null, null, System.currentTimeMillis(), MsgType.REGISTER_SUCCESSFUL));
} else {
out.writeObject(new ChatMsgBean(userName, null, null, System.currentTimeMillis(), MsgType.REGISTER_FAILURE));
}
}
}
最后是 用于 处理客户端操作请求 的线程类:
package edu.youzg.chatroom.server.ui;
import edu.youzg.chatroom.bean.ChatMsgBean;
import edu.youzg.chatroom.config.MsgType;
import edu.youzg.chatroom.utils.TimeUtil;
import java.io.*;
import java.net.SocketException;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
//服务端的子线程,用来读取客户端发来的消息(对象)
public class ServerTransmitThread extends Thread {
ObjectInputStream in;
BufferedWriter bw;
BufferedReader br;
HashMap<String,ObjectOutputStream> hm;
List<String> activeClients;
String username;
public ServerTransmitThread(HashMap<String, ObjectOutputStream> hm, List<String> activeClients, String username, ObjectInputStream in) {
this.in = in;
this.username = username;
this.hm = hm;
this.activeClients = activeClients;
}
@Override
public void run() {
try {
while (true) {
//读取客户端,发来的对象
ChatMsgBean bean = (ChatMsgBean) in.readObject();
String reciver = bean.getReciver();
String msgContent = bean.getContent();
MsgType msgType = bean.getMsgType();
//把对象中缺的数据补上
bean.setSender(username);
bean.setTime(System.currentTimeMillis());
//根据消息类型做出不同的处理
if (msgType == MsgType.PRIVATE) {
//私聊
if (!activeClients.contains(reciver)) {
hm.get(username).writeObject(new ChatMsgBean(null, reciver, null, System.currentTimeMillis(), MsgType.CLIENT_INEXISTENCE));
continue;
}
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("private.txt", true)));
synchronized (ServerTransmitThread.class) { //向文件中写入聊天信息
String time = TimeUtil.changeMils2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss ");
bw.write(time + "[" + bean.getReciver() + "]对" + "[" + username + "]说:" + bean.getContent());
bw.newLine();
bw.flush();
}
//取出这个人的管道,写给他
hm.get(reciver).writeObject(bean);
} else if (msgType == MsgType.PUBLIC) {
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("public.txt", true)));
synchronized (ServerTransmitThread.class) { //向文件中写入聊天信息
String time = TimeUtil.changeMils2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss ");
bw.write(time + "[" + username + "]说:" + bean.getContent());
bw.newLine();
bw.flush();
}
//公聊:遍历所有人,取出每个人的管道,写给每个人
Set<String> keySet = hm.keySet();
for (String key : keySet) {
//排除自己,不要给自己发
if (username.equals(key)) {
continue;
}
hm.get(key).writeObject(bean);
}
} else if (msgType == MsgType.FRIEND_LIST) {
//获取在线列表:把集合中的人,拼接好,发送给请求者
StringBuffer sb = new StringBuffer();
int i = 1;
for (String key : activeClients) {
if (username.equals(key)) {
continue;
}
//拼接每个人
sb.append((i++)).append(".").append(key).append("\n");
}
bean.setContent(sb.toString());
hm.get(username).writeObject(bean);
} else if (msgType == MsgType.EXIT) {
//下线:服务端通知其他人
Set<String> keySet = hm.keySet();
for (String key : keySet) {
//排除自己,不要给自己发
if (username.equals(key)) {
continue;
}
hm.get(key).writeObject(bean);
}
hm.remove(username);
activeClients.remove(username);
break;
} else if (msgType == MsgType.SWITCH) {
if (activeClients.contains(username)) {
activeClients.remove(username);
hm.get(username).writeObject(new ChatMsgBean(null, null, null, System.currentTimeMillis(), MsgType.SWITCH_HIDDEN));
} else {
activeClients.add(username);
hm.get(username).writeObject(new ChatMsgBean(null, null, null, System.currentTimeMillis(), MsgType.SWITCH_ACTIVE));
}
} else if (msgType == MsgType.PASS_FILE) {
//处理客户端发来的文件
//1.从消息内容中,取出文件名和文件大小
if (activeClients.contains(reciver)) {
hm.get(reciver).writeObject(bean);
} else {
hm.get(username).writeObject(new ChatMsgBean(null, null, null, System.currentTimeMillis(), MsgType.CLIENT_INEXISTENCE));
}
} else if (MsgType.ACCEPT == msgType) {
hm.get(username).writeObject(new ChatMsgBean(null, username, null, System.currentTimeMillis(), MsgType.ACCEPT));
} else if (MsgType.REFUSE == msgType) {
hm.get(username).writeObject(new ChatMsgBean(null, username, null, System.currentTimeMillis(), MsgType.REFUSE));
} else if (MsgType.QUERY_PUBLIC == msgType){ //查询公聊聊天记录
StringBuffer str = new StringBuffer();
String line = null;
br = new BufferedReader(new InputStreamReader(new FileInputStream("public.txt")));
while ((line=br.readLine()) != null) {
str.append(line).append("\n");
}
ChatMsgBean msgBean = new ChatMsgBean(null, null, str.toString(), System.currentTimeMillis(), MsgType.QUERY_PUBLIC);
hm.get(username).writeObject(msgBean);
} else { //查询私聊聊天记录
StringBuffer str = new StringBuffer();
String line = null;
br = new BufferedReader(new InputStreamReader(new FileInputStream("private.txt")));
while ((line=br.readLine()) != null) {
str.append(line).append("\n");
}
ChatMsgBean msgBean = new ChatMsgBean(null, null, str.toString(), System.currentTimeMillis(), MsgType.QUERY_PRIVATE);
hm.get(username).writeObject(msgBean);
}
}
} catch (SocketException e){
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
//1.关闭下线者的管道
hm.get(username).close();
String time = TimeUtil.changeMils2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss:");
System.out.println("\r\n" + time);
System.out.println("客户端" + "[" + username + "]下线!");
//2.从集合中移除这个人
hm.remove(username);
} catch (Exception e) {
}
}
}
}
客户端
首先,我们要先连接服务器端,
在连接到服务器端之后,我们再进行身份验证(或 注册),
身份验证成功后,我们就可以开启一个线程来侦听服务器端发来的消息,
并且向服务器端发送各种操作的请求。
在本篇博文中,本人简化了上述的步骤,将 身份验证 和 向服务器端发送操作请求的功能写在了主线程中:
package edu.youzg.chatroom.client.ui;
import edu.youzg.chatroom.bean.ChatMsgBean;
import edu.youzg.chatroom.config.MsgType;
import edu.youzg.chatroom.utils.InputAndOutputUtil;
import edu.youzg.chatroom.utils.InputUtil;
import edu.youzg.chatroom.utils.TimeUtil;
import javax.management.Query;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;
public class MyClient {
private static ObjectInputStream in;
private static ObjectOutputStream out;
private static Scanner sc;
private static Socket sk;
private static ClientChatThread th;
public static void main(String[] args) {
//TCP协议的Socket
try {
sk = new Socket("localhost", 6666);
//1.包装通道中的字节流
in = new ObjectInputStream(sk.getInputStream());
out = new ObjectOutputStream(sk.getOutputStream());
sc = new Scanner(System.in);
beforeChat();
//2.开启子线程读取服务端转发回来的消息
th = new ClientChatThread(in);
th.start();
//3.提供功能菜单选项
boolean flag=true;
while (flag) {
System.out.println("\r\n请选择:1.私聊 2.公聊 3.在线列表 4.下线 5.发送文件 6.隐身/上线 7.查询聊天记录");
int num = InputUtil.inputNeededIntType(1, 7);
switch (num) {
case 1:
//私聊
privateTalk();
break;
case 2:
//公聊
publicTalk();
break;
case 3:
//在线列表
getOnLineList();
break;
case 4:
//下线的逻辑
//客户端:1.你给服务端发下线指令,2.关闭客户端的Socket 3.关闭客户端读取消息的线程
//服务端:0.通知其他人,谁谁下线了。1.关闭下线的这个人的Socket 2.把下线的这个人名字从集合中移除掉
exitTalk();
flag = false; //修改标记,结束死循环
break;
case 5:
//发送文件
sendFile();
break;
case 6:
switchState();
break;
case 7:
queryChatRecords();
break;
}
}
}catch (SocketException e){
//客户端下线,有能会出现SocketException这个异常,我们在这里做个空处理
}catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
//释放资源
th.stop(); //强制关闭掉客户端读取消息线程
sk.close();//关闭客户端的socket
} catch (Exception e) {
}
}
}
private static void queryChatRecords() throws IOException {
System.out.println("---请选择查询的类别---");
System.out.println("1.公聊内容 2.私聊内容");
int i = InputUtil.inputNeededIntType(1, 2);
MsgType type = null;
String content = null;
if (1 == i) {
type = MsgType.QUERY_PUBLIC;
} else {
content = sc.nextLine();
type = MsgType.QUERY_PRIVATE;
}
ChatMsgBean chatMsgBean = new ChatMsgBean(null, null, content, 0, type);
out.writeObject(chatMsgBean);
}
private static void switchState() throws IOException {
ChatMsgBean chatMsgBean = new ChatMsgBean(null, null, null, 0, MsgType.SWITCH);
out.writeObject(chatMsgBean);
}
private static void beforeChat() throws IOException, ClassNotFoundException {
//1.注册用户名
boolean flag = true;
while (flag) {
System.out.println(TimeUtil.changeMils2Date(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss") + ":");
System.out.println("---------------------------欢迎使用右转聊天室---------------------------");
System.out.println("-------------------------------请输入命令-------------------------------");
System.out.println(" 1.注册 2.登录 ");
int choose = InputUtil.inputNeededIntType(1, 2);
String username = InputUtil.inputUserName(); //对用户进行正则校验
String password = InputUtil.inputUserPassword();
if (choose == 1) {
dealRegister(username, password);
} else {
flag = dealLogin(username, password);
}
}
}
private static boolean dealLogin(String username, String password) throws IOException, ClassNotFoundException {
//创建对象
ChatMsgBean chatMsgBean = new ChatMsgBean(null, username, password, 0, MsgType.LOGIN);
out.writeObject(chatMsgBean);
//读取服务端,对这个用户名的是否注册成功的反馈
//读取服务器返回的对象
ChatMsgBean bean = (ChatMsgBean) in.readObject();
MsgType fk = bean.getMsgType();
if (fk.equals(MsgType.LOGIN_OK)) {
System.out.println("\r\n" + TimeUtil.changeMils2Date(bean.getTime(), "yyyy-MM-dd HH:mm:ss") + ":");
System.out.println("登陆成功!");
return false;
} else if (fk.equals(MsgType.USER_CONTAINED)) {
System.out.println("\r\n" + TimeUtil.changeMils2Date(bean.getTime(), "yyyy-MM-dd HH:mm:ss") + ":");
System.out.println("该账号已登陆,请稍后重试!");
} else {
System.out.println("\r\n" + TimeUtil.changeMils2Date(bean.getTime(), "yyyy-MM-dd HH:mm:ss") + ":");
System.out.println("用户信息错误,登录失败!");
}
return true;
}
private static void dealRegister(String username, String password) throws IOException, ClassNotFoundException {
//创建对象
ChatMsgBean chatMsgBean = new ChatMsgBean(null, username, password, 0, MsgType.REGISTER);
out.writeObject(chatMsgBean);
//读取服务端,对这个用户名的是否注册成功的反馈
//读取服务器返回的对象
ChatMsgBean bean = (ChatMsgBean) in.readObject();
MsgType fk = bean.getMsgType();
if (fk.equals(MsgType.REGISTER_SUCCESSFUL)) {
System.out.println("\r\n" + TimeUtil.changeMils2Date(bean.getTime(), "yyyy-MM-dd HH:mm:ss") + ":");
System.out.println("用户注册成功!");
} else if (fk.equals(MsgType.ILLEAGAL_USER)) {
System.out.println("\r\n" + TimeUtil.changeMils2Date(bean.getTime(), "yyyy-MM-dd HH:mm:ss") + ":");
System.out.println("非法用户名,注册失败!");
} else {
System.out.println("\r\n" + TimeUtil.changeMils2Date(bean.getTime(), "yyyy-MM-dd HH:mm:ss") + ":");
System.out.println("用户名已经存在,注册失败!");
}
}
private static void sendFile() throws IOException {
//发送文件
Scanner sc = new Scanner(System.in);
System.out.println("请输入接收者");
String reciver = sc.nextLine();
System.out.println("请输入文件的路径");
String filePath = sc.nextLine();
//封装文件
File file = new File(filePath);
if(file.exists()){
//获取文件的字节数组
byte[] fileBytes = InputAndOutputUtil.readFile(filePath);
ChatMsgBean bean = new ChatMsgBean(reciver, null, null, 0, MsgType.PASS_FILE, file.getName(), file.length(), fileBytes);
out.writeObject(bean);
}else{
System.out.println("文件不存在,请检查文件路径");
}
}
private static void exitTalk() throws IOException {
//退出聊天室
ChatMsgBean chatMsgBean = new ChatMsgBean(null, null, null, 0,MsgType.EXIT);
out.writeObject(chatMsgBean);
}
private static void getOnLineList() throws IOException{
//获取在线列表
ChatMsgBean chatMsgBean = new ChatMsgBean(null, null,null, 0, MsgType.FRIEND_LIST);
out.writeObject(chatMsgBean);
}
private static void publicTalk() throws IOException {
while (true) {
System.out.println("\r\n" + "(您当前模式-公聊模式) 接收者输入\"-q\"退出当前模式");
Scanner sc = new Scanner(System.in);
String msg = sc.nextLine();
if ("-q".equals(msg)) {
break;
}
ChatMsgBean chatMsgBean = new ChatMsgBean(null, null, msg, 0, MsgType.PUBLIC);
out.writeObject(chatMsgBean);
}
}
private static void privateTalk() throws IOException {
while (true) {
Scanner sc = new Scanner(System.in);
System.out.println("\r\n" + "(您当前模式-私聊模式) 接收者输入\"-q\"退出当前模式");
System.out.println("请输入接收者姓名:");
String reciver = sc.nextLine();
if ("-q".equals(reciver)) {
break;
}
System.out.println("请输入消息内容:");
String msg = sc.nextLine();
//把要发的数据封装进对象,发给服务器
ChatMsgBean chatMsgBean = new ChatMsgBean(reciver, null, msg, 0, MsgType.PRIVATE);
out.writeObject(chatMsgBean);
}
}
}
那么,至于侦听服务器端发来的信息,就交由身份验证成功后,开启的ClientChatThread类来处理:
package edu.youzg.chatroom.client.ui;
import edu.youzg.chatroom.bean.ChatMsgBean;
import edu.youzg.chatroom.config.MsgType;
import edu.youzg.chatroom.utils.InputAndOutputUtil;
import edu.youzg.chatroom.utils.InputUtil;
import edu.youzg.chatroom.utils.TimeUtil;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.SocketException;
//客户端的子线程用来读取,服务端转发过来的消息
public class ClientChatThread extends Thread {
ObjectInputStream in;
public ClientChatThread(ObjectInputStream in) {
this.in = in;
}
@Override
public void run() {
try {
while (true) {
//读取服务端,转发回来的对象
ChatMsgBean bean = (ChatMsgBean) in.readObject();
String sender = bean.getSender();
String msgContent = bean.getContent();
MsgType msgType = bean.getMsgType();
long timeLong = bean.getTime();
String dateStr = TimeUtil.changeMils2Date(timeLong, "yyyy-MM-dd HH:mm:ss");
//根据消息类型,判断展示
if (msgType == MsgType.PRIVATE) {
System.out.println("\r\n" + dateStr);
System.out.println("好友[" + sender + "]对你说:" + msgContent);
} else if (msgType == MsgType.CLIENT_INEXISTENCE) {
System.out.println("\r\n" + dateStr);
System.out.println("好友[" + bean.getReciver() + "]不存在 或 未上线!");
} else if (msgType == MsgType.ONLINE) {
System.out.println("\r\n" + dateStr);
System.out.println("您的好友[" + sender + "]已上线!");
} else if (msgType == MsgType.PUBLIC) {
System.out.println("\r\n" + dateStr);
System.out.println("好友[" + sender + "]对大家说:" + msgContent);
} else if (msgType == MsgType.FRIEND_LIST) {
System.out.println(dateStr + "-当前在线的人:");
System.out.println(msgContent);
} else if (msgType == MsgType.EXIT) {
System.out.println("\r\n" + dateStr);
System.out.println("好友[" + sender + "]已下线!");
} else if (msgType == MsgType.QUERY_PUBLIC || msgType == MsgType.QUERY_PRIVATE) {
System.out.println("\n当前时间为:" + dateStr);
System.out.println(msgContent);
} else if (msgType == MsgType.PASS_FILE) {
//1.从消息内容中,取出文件名和文件大小
System.out.println("\r\n" + dateStr);
System.out.println("好友[" + sender + "]给你发来一个文件:" + bean.getFileName() + " 文件大小;" + (bean.getFileLength() / 1024 / 1024.0) + " MB");
String choose = InputUtil.inputChoice();
if ("y".equals(choose)) {
byte[] fileBytes = bean.getFileData();
//此处本人设置默认保存在D盘中
boolean b = InputAndOutputUtil.writeFile("D:\\" + bean.getFileName(), fileBytes);
if (b) {
System.out.println("文件保存成功在" + "D:\\" + bean.getFileName());
} else {
System.out.println("文件保存失败");
}
} else {
byte[] fileBytes = bean.getFileData();
}
} else if (MsgType.ACCEPT == msgType) {
System.out.println(TimeUtil.changeMils2Date(timeLong, "yyyy-MM-dd HH:mm:ss :"));
System.out.println("好友[" + sender +"]接收文件成功!");
} else if (MsgType.REFUSE == msgType) {
System.out.println("好友[" + sender +"]拒绝接收该文件!");
} else {
String status = null;
if (msgType == MsgType.SWITCH_ACTIVE) {
status = "在线";
} else {
status = "隐身";
}
System.out.println("\n" + TimeUtil.changeMils2Date(bean.getTime(), "yyyy-MM-dd HH:mm:ss"));
System.out.println("已切换至" + status + "状态");
}
}
} catch (SocketException e) {
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
那么,在这里,本人《Java SE》专栏的所有知识点,就讲解完成了!
希望看到这里的同学,能够记得本人在总集篇说过的一句话:
Java是一门面向对象的语言
相信学到这里的同学,大概能明白为何 本人特别强调了这个点了
😀😀😀
那么,让我们《Web》专栏右转见喽!(ง •_•)ง