不说废话,先放一波效果图,展示部分给功能效果,想了解所有代码功能可以自己运行代码体验。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/fffd70a1a28e61879f93c380d17f3bdb.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e771e7db2d8542ddfc439225485202d2.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/ce097fba253ed66fa7bc8ad482534bd9.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/25003e3a9148b2be3ccaa00b95da532b.png)
感觉不错,就看下去吧!
一、前置知识
-
Socket是什么呢?
Socket俗称套接字,是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。Scoket根据IP+端口确定连接。 -
Socket怎么使用呢?
-
为什么要是使用多线程?
有时候我们就是需要去同时做一件事情,所以为了达到这个目的,多线程也就应运而生了。就本项目而言当服务器调用accept()方法后线程就会阻塞等待连接,但我们的程序不能卡住不动,所以我们就需要使用到多线程。
二、设计实现
项目结构
项目精华——工具类
文件工具类
public class MyFileUtils {
/**
* 导出文件
* @param msg 导出内容
* @param file 目标文件
*/
public static void writeFile(String msg, File file){
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream);
byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
outputStream.write(bytes,0,bytes.length);
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
面板信息展示工具类
public class MsgShowUtils {
/**
*
* @param jTextPane 显示信息的板面
* @param msg 需要显示的信息
* @param color 字体颜色
* @param size 字体大小
* @param alignment 对齐方式 0:左对齐; 1:居中; 2:右对齐
* @throws BadLocationException
*/
public static void setMsgStyle(JTextPane jTextPane, String msg, Color color, Integer size, Integer alignment)
throws BadLocationException {
StyledDocument doc = jTextPane.getStyledDocument();
SimpleAttributeSet set = new SimpleAttributeSet();
// 设置文本颜色
if(color==null){
StyleConstants.setForeground(set, Color.BLACK);
}else {
StyleConstants.setForeground(set, color);
}
// 设置文本大小
if(size==null){
StyleConstants.setFontSize(set, 14);
}else {
StyleConstants.setFontSize(set, size);
}
// 设置文本对齐方式
if(alignment==null){
StyleConstants.setAlignment(set, 0);
}else {
StyleConstants.setAlignment(set, alignment);
}
doc.setParagraphAttributes(jTextPane.getText().length(), doc.getLength() - jTextPane.getText().length(), set,
false);
doc.insertString(doc.getLength(), msg, set);
}
}
项目灵魂——实现流程
为了更好的实现功能的实现,可以将消息提进行封装,根据消息属性进行不同的处理操作。
public class MsgData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 消息类型
*/
private String msgType;
/**
* 发送者
*/
private String sender;
/**
* 接收者
*/
private String recipient;
/**
* 内容
*/
private String content;
/**
* 消息发送时间
*/
private String sendTime;
}
登录
根据用户名、密码,判断用户类型进行页面跳转
- 用户名密码相同则视为正确
- 用户名密码皆为cnlbc,则默认为管理员跳转至服务器页面
关键代码实现
private void toLogin() {
String name = username.getText().trim();
String pass = new String(password.getPassword());
// 判空,空信息不能提交
if(Objects.equals("",name) || Objects.equals("",pass.trim())){
tip.setText("请填写信息后登录");
return;
}
// 设计简单的登录验证规则,用户名密码相同则视为正确
if (Objects.equals(name, pass.trim())) {
if(Objects.equals("cnlbc", name)){
new ServerView().setVisible(true);
}else {
new ClientView(name).setVisible(true);
}
this.setVisible(false);
} else {
tip.setText("用户名或密码错误");
}
}
服务端
根据接受到的消息类型采取不同的处理方式
if (msgData != null) {
if (Objects.equals(msgData.getMsgType(), StringConstant.LOGIN_TYPE)) {
//登录处理
ServerView.CLIENT_MAP.put(msgData.getSender(), client);
fillList();
StringBuffer userList = new StringBuffer();
for (Map.Entry<String, Socket> entry : ServerView.CLIENT_MAP.entrySet()) {
if (!Objects.equals(entry.getKey(), msgData.getSender())) {
userList.append(entry.getKey()+",");
}
}
msgData.setContent(userList.toString());
objectOutputStream = new ObjectOutputStream(client.getOutputStream());
objectOutputStream.writeObject(msgData);
objectOutputStream.flush();
msgData.setMsgType(StringConstant.SYSTEM_UP);
msgData.setContent(msgData.getSender() + "用户上线了");
msgData.setSendTime(MyDateUtils.getDate());
userSendPublicMsg(msgData);
} else if (Objects.equals(msgData.getMsgType(), StringConstant.PUBLIC_TYPE)) {
//群发消息处理
userSendPublicMsg(msgData);
String s = msgData.getSender() + "对所有人说:" + msgData.getContent() + " " + msgData.getSendTime()
+ StringConstant.END_LINE;
MsgShowUtils.setMsgStyle(msgPane, s, null, null, null);
} else if (Objects.equals(msgData.getMsgType(), StringConstant.PRIVATE_TYPE)) {
//私聊处理
String s = msgData.getSender() + "对" + msgData.getRecipient() + "说:" + msgData.getContent()
+ " " + msgData.getSendTime() + StringConstant.END_LINE;
MsgShowUtils.setMsgStyle(msgPane, s, Color.BLUE, null, null);
userSendPrivateMsg(msgData);
} else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_DOWN)) {
//退出消息处理
msgData.setMsgType(StringConstant.SYSTEM_DOWN);
msgData.setContent("用户:" + msgData.getSender() + "退出聊天室" + StringConstant.END_LINE);
MsgShowUtils.setMsgStyle(msgPane, msgData.getContent(), Color.RED, null, null);
userQuit(msgData);
}
}
客户端
根据接收到的消息类型进行不同展示
if (Objects.nonNull(msgData)) {
if (Objects.equals(msgData.getMsgType(), StringConstant.LOGIN_TYPE)) {
// 登录处理
JOptionPane.showMessageDialog(null, "加入成功", "提示", -1);
} else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_UP)) {
String s = "系统提示:" + msgData.getSender() + "用户上线了 " + msgData.getSendTime()
+ StringConstant.END_LINE;
MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 15, 1);
} else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_DOWN)) {
// 下线提示消息
String s = "";
if(name.equals(msgData.getRecipient())){
s = "系统提示:" + "您已被强制下线 " + msgData.getSendTime()
+ StringConstant.END_LINE;
close();
}else {
s = "系统提示:" + msgData.getSender() + "用户下线了 " + msgData.getSendTime()
+ StringConstant.END_LINE;
}
MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 15, 1);
} else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_TYPE)) {
// 系统消息
String s = "";
System.out.println(msgData.getRecipient());
System.out.println(name);
if(name.equals(msgData.getRecipient())){
s = "系统悄悄告诉你:" + msgData.getContent() + " " + msgData.getSendTime() + StringConstant.END_LINE;
}else {
s = "系统提示:" + msgData.getContent() + " " + msgData.getSendTime() + StringConstant.END_LINE;
}
MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 15, 1);
} else if (Objects.equals(msgData.getMsgType(), StringConstant.QUIT_TYPE)) {
// 系统关闭消息
String s = "系统提示:服务器停止服务 " + msgData.getSendTime() + StringConstant.END_LINE;
MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 18, 1);
} else {
// 其他消息(群聊、私聊)
MsgShowUtils.setMsgStyle(msgPane, msgData.getSendTime() + StringConstant.END_LINE, null, null,
null);
MsgShowUtils.setMsgStyle(msgPane,
msgData.getSender() + ": " + msgData.getContent() + StringConstant.END_LINE, null, null,
null);
}
}
建议:
虽然该项目代码能够完整运行,但不建议大家拿去直接用,可以理理流程,加些功能点,如:文件传输、客户端之间的私聊等。本项目中有封装好的工具类、消息实体类,方便大家根据自己的需求进行整改。希望每个小项目都能有收获。
三、完整源码获取方式
关注微信公众号 菜鸟乐编程,然后发送:源码04Java聊天室