Java GUI编程实现在线聊天室

网友投稿 359 2023-01-24


Java GUI编程实现在线聊天室

引言

综合应用java的GUI编程和网络编程,实现一个能够支持多组用户同时使用的聊天室软件。该聊天室具有比较友好的GUI界面,并使用C/S模式,支持多个用户同时使用,用户可以自己选择加入或者创建房间,和房间内的其他用户互发信息(文字和图片)

主要功能

客户端的功能主要包括如下的功能:

选择连上服务端

显示当前房间列表(包括房间号和房间名称)

选择房间进入

多个用户在线群聊

可以发送表情(用本地的,实际上发送只发送表情的代码)

退出房间

选择创建房间

房间里没人(房主退出),导致房间解散

显示系统提示消息

显示用户消息

构造标准的消息结构发送

维护GUI所需的数据模型

服务端的功能主要包括:

维护用户信息和房间信息

处理用户发送来的消息选择转发或者回复处理结果

构造标准的消息结构发送

架构

整个程序采用C/S设计架构,分为一个服务端和多个客户端。服务端开放一个端口给所有开客户端,客户端连接该端口并收发信息,服务端在内部维护客户端的组,并对每一个客户端都用一个子线程来收发信息

基本类的设计

User类

package User;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.Socket;

/**

*

* @author lannooo

*

*/

public class User {

private String name;

private long id;

private long roomId;

private Socket socket;

private BufferedReader br;

private PrintWriter pw;

/**

*

* @param name: 设置user的姓名

* @param id:设置user的id

* @param socket:保存用户连接的socket

* @throws IOException

*/

public User(String name, long id, final Socket socket) throws IOException {

this.name=name;

this.id=id;

this.socket=socket;

this.br=new BufferedReader(new InputStreamReader(

socket.getInputStream()));

this.pw=new PrintWriter(socket.getOutputStream());

}

/**

* 获得该用户的id

* @return id

*/

public long getId() {

return id;

}

/**

* 设置该用户的id

* @param id 新的id

*/

public void setId(long id) {

this.id = id;

}

/**

* 获得用户当前所在的房间号

* @return roomId

*/

public long getRoomId() {

return roomId;

}

/**

* 设置当前用户的所在的房间号

* @param roomId

*/

public void setRoomId(long roomId) {

this.roomId = roomId;

}

/**

* 设置当前用户在聊天室中的昵称

* @param name

*/

public void setName(String name) {

this.name = name;

}

/**

* 返回当前用户在房间中的昵称

* @return

*/

public String getName() {

return name;

}

/**

* 返回当前用户连接的socket实例

* @return

*/

public Socket getSocket() {

return socket;

}

/**

* 设置当前用户连接的socket

* @param socket

*/

public void setSocket(Socket socket) {

this.socket = socket;

}

/**

* 获得该用户的消息读取辅助类BufferedReader实例

* @return

*/

public BufferedReader getBr() {

return br;

}

/**

* 设置 用户的消息读取辅助类

* @param br

*/

public void setBr(BufferedReader br) {

this.br = br;

}

/**

* 获得消息写入类实例

* @return

*/

public PrintWriter getPw() {

return pw;

}

/**

* 设置消息写入类实例

* @param pw

*/

public void setPw(PrintWriter pw) {

this.pw = pw;

}

/**

* 重写了用户类打印的函数

*/

@Override

public String toString() {

return "#User"+id+"#"+name+"[#Room"+roomId+"#]";

}

}

Room类

package Room;

import java.util.ArrayList;

import java.util.List;

import User.User;

/**

*

* @author lannooo

*

*/

public class Room {

private String name;

private long roomId;

private ArrayList list;

private int totalUsers;

/**

* 获得房间的名字

* @return name

*/

public String getName() {

return name;

}

/**

* 设置房间的新名字

* @param name

*/

public void setName(String name) {

this.name = name;

}

/**

* 获得房间的id号

* @return

*/

public long getRoomId() {

return roomId;

}

/**

* 设置房间的id

* @param roomId

*/

public void setRoomId(long roomId) {

this.roomId = roomId;

}

/**

* 向房间中加入一个新用户

* @param user

*/

public void addUser(User user) {

if(!list.contains(user)){

list.add(user);

totalUsers++;

}else{

System.out.println("User is already in Room<"+name+">:"+user);

}

}

/**

* 从房间中删除一个用户

* @param user

* @return 目前该房间中的总用户数目

*/

public int delUser(User user){

if(list.contains(user)){

list.remove(user);

return --totalUsers;

}else{

System.out.println("User is not in Room<"+name+">:"+user);

return totalUsers;

}

}

/**

* 获得当前房间的用户列表

* @return

*/

public ArrayList getUsers(){

return list;

}

/**

* 获得当前房间的用户昵称的列表

* @return

*/

public String[] getUserNames(){

String[] userList = new String[list.size()];

int i=0;

for(User each: list){

userList[i++]=each.getName();

}

return userList;

}

/**

* 使用房间的名称和id来new一个房间

* @param name

* @param roomId

*/

public Room(String name, long roomId) {

this.name=name;

this.roomId=roomId;

this.totalUsers=0;

list = new ArrayList<>();

}

}

RoomList类

package Room;

import java.awt.image.DirectColorModel;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import User.User;

/**

*

* @author lannooo

*

*/

public class RoomList {

private HashMap map;

private long unusedRoomId;

public static long MAX_ROOMS = 9999;

private int totalRooms;

/**

* 未使用的roomid从1算起,起始的房间总数为0

*/

public RoomList(){

map = new HashMap<>();

unusedRoomId = 1;

totalRooms = 0;

}

/**

* 创建一个新的房间,使用未使用的房间号进行创建,如果没有可以使用的则就创建失败

* @param name: 房间的名字

* @return 创建的房间的id

*/

public long createRoom(String name){

if(totalRooms

if(name.length()==0){

name = ""+unusedRoomId;

}

Room room = new Room(name, unusedRoomId);

map.put(unusedRoomId, room);

totalRooms++;

return unusedRoomId++;

}else{

return -1;

}

}

/**

* 用户加入一个房间

* @param user

* @param roomID

* @return

*/

public boolean join(User user, long roomID){

if(map.containsKey(roomID)){

map.get(roomID).addUser(user);

return true;

}else{

return false;

}

}

/**

* 用户退出他的房间

* @param user

* @param roomID

* @return

*/

public int esc(User user, long roomID){

if(map.containsKey(roomID)){

int number = map.get(roomID).delUser(user);

/*如果这个房间剩下的人数为0,那么删除该房间*/

if(number==0){

map.remove(roomID);

totalRooms--;

return 0;

}

return 1;

}else{

return -1;

}

}

/**

* 列出所有房间的列表,返回一个二维数组,strings[i][0]放房间的id,string[i][1]放房间的name

* @return

*/

public String[][] listRooms(){

String[][] strings = new String[totalRooms][2];

int i=0;

/*将map转化为set并使用迭代器来遍历*/

Set> set = map.entrySet();

Iterator> iterator = set.iterator();

while(iterator.hasNext()){

Map.Entry entry = iterator.next();

long key = entry.getKey();

Room value = entry.getValue();

strings[i][0]=""+key;

strings[i][1]=value.getName();

}

return strings;

}

/**

* 通过roomID来获得房间

* @param roomID

* @return

*/

public Room getRoom(long roomID){

if(map.containsKey(roomID)){

return map.get(roomID);

}

else

return null;

}

}

服务端

Server

package Server;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Map;

import org.json.*;

import Room.Room;

import Room.RoomList;

import User.User;

/**

*

* @author lannooo

*

*/

public class Server {

private ArrayList allUsers;

private RoomList rooms;

private int port;

private ServerSocket ss;

private longaJYNtQUjh unusedUserID;

public final long MAX_USERS = 999999;

/**

* 通过port号来构造服务器端对象

* 维护一个总的用户列表和一个房间列表

* @param port

* @throws Exception

*/

public Server(int port) throws Exception {

allUsers = new ArrayList<>();

rooms = new RoomList();

this.port=port;

unusedUserID=1;

ss = new ServerSocket(port);

System.out.println("Server is builded!");

}

/**

* 获得下一个可用的用户id

* @return

*/

private long getNextUserID(){

if(unusedUserID < MAX_USERS)

return unusedUserID++;

else

return -1;

}

/**

* 开始监听,当接受到新的用户连接,就创建一个新的用户,并添加到用户列表中

* 然后创建一个新的服务线程用于收发该用户的消息

* @throws Exception

*/

public void startListen() throws Exception{

while(true){

Socket socket = ss.accept();

long id = getNextUserID();

if(id != -1){

User user = new User("User"+id, id, socket);

System.out.println(user.getName() + " is login...");

allUsers.add(user);

ServerThread thread = new ServerThread(user, allUsers, rooms);

thread.start();

}else{

System.out.println("Server is full!");

socket.close();

}

}

}

/**

* 测试用main方法,设置侦听端口为9999,并开始监听

* @param args

*/

public static void main(String[] args) {

try {

Server server = new Server(9999);

server.startListen();

} catch (Exception e) {

e.printStackTrace();

}

}

}

ServerThread

package Server;

import java.io.IOException;

import java.io.PrintWriter;

import java.net.SocketException;

import java.util.ArrayList;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import Room.Room;

import Room.RoomList;

import User.User;

/**

*

* @author lannooo

*

*/

public class ServerThread extends Thread {

private User user;

private ArrayList userList;/*保存用户列表*/

private RoomList map; /*保存房间列表*/

private long roomId;

private PrintWriter pw;

/**

* 通过用户的对象实例、全局的用户列表、房间列表进行构造

* @param user

* @param userList

* @param map

*/

public ServerThread(User user,

ArrayList userList, RoomList map){

this.user=user;

this.userList=userList;

this.map=map;

pw=null;

roomId = -1;

}

/**

* 线程运行部分,持续读取用户socket发送来的数据,并解析

*/

public void run(){

try{

while (true) {

String msg=user.getBr().readLine();

System.out.println(msg); /*解析用户的数据格式*/

parseMsg(msg);

}

}catch (SocketException se) { /*处理用户断开的异常*/

System.out.println("user "+user.getName()+" logout.");

}catch (Exception e) { /*处理其他异常*/

e.printStackTrace();

}finally {

try {

/*

* 用户断开或者退出,需要把该用户移除

* 并关闭socket

*/

remove(user);

user.getBr().close();

user.getSocket().close();

} catch (IOException ioe) {

ioe.printStackTrace();

}

}

}

/**

* 用正则表达式匹配数据的格式,根据不同的指令类型,来调用相应的方法处理

* @param msg

*/

private void parseMsg(String msg){

String code = null;

String message=null;

if(msg.length()>0){

/*匹配指令类型部分的字符串*/

Pattern pattern = Pattern.compile("(.*)");

Matcher matcher = pattern.matcher(msg);

if(matcher.find()){

code = matcher.group(1);

}

/*匹配消息部分的字符串*/

pattern = Pattern.compile("(.*)");

matcher = pattern.matcher(msg);

if(matcher.find()){

message = matcher.group(1);

}

switch (code) {

case "join":

// add to the room

// code = 1, 直接显示在textArea中

// code = 11, 在list中加入

// code = 21, 把当前房间里的所有用户返回给client

if(roomId == -1){

roomId = Long.parseLong(message);

map.join(user, roomId);

sendRoomMsgExceptSelf(buildCodeWithMsg(""+user.getName()+""+user.getId()+"", 11));

// 这个消息需要加入房间里已有用户的列表

returnMsg(buildCodeWithMsg("你加入了房间:" + map.getRoom(roomId).getName(), 1));

returnMsg(buildCodeWithMsg(getMembersInRoom(), 21));

}else{

map.esc(user, roomId);

sendRoomMsg(buildCodeWithMsg(""+user.getId(), 12));

long oldRoomId = roomId;

roomId = Long.parseLong(message);

map.join(user, roomId);

sendRoomMsgExceptSelf(buildCodeWithMsg(""+user.getName()+""+user.getId()+"", 11));

returnMsg(buildCodeWithMsg("你退出房间:" + map.getRoom(oldRoomId).getName() + ",并加入了房间:" + roomId,1));

returnMsg(buildCodeWithMsg(getMembersInRoom(), 21));

}

break;

case "esc":

// delete from room list

// code = 2, 弹窗提示

// code = 12, 对所有该房间的其他用户发送该用户退出房间的信息,从list中删除

if(roomId!=-1){

int flag=map.esc(user, roomId);

sendRoomMsgExceptSelf(buildCodeWithMsg(""+user.getId(), 12));

long oldRoomId=roomId;

roomId = -1;

returnMsg(buildCodeWithMsg("你已经成功退出房间,不会收到消息", 2));

if(flag==0){

sendMsg(buildCodeWithMsg(""+oldRoomId, 13));

}

}else{

returnMsg(buildCodeWithMsg("你尚未加入任何房间", 2));

}

break;

case "list":

// list all the rooms

// code = 3, 在客户端解析rooms,并填充roomlist

returnMsg(buildCodeWithMsg(getRoomsList(), 3));

break;

case "message":

// send message

// code = 4, 自己收到的话,打印的是‘你说:....'否则打印user id对应的name

sendRoomMsg(buildCodeWithMsg(""+user.getId()+""+message+"", 4));

break;

case "create":

// create a room

// code=5,提示用户进入了房间

// code=15,需要在其他所有用户的room列表中更新

roomId = map.createRoom(message);

map.join(user, roomId);

sendMsg(buildCodeWithMsg(""+roomId+""+message+"", 15));

returnMsg(buildCodeWithMsg("你进入了创建的房间:"+map.getRoom(roomId).getName(), 5));

returnMsg(buildCodeWithMsg(getMembersInRoom(), 21));

break;

case "setname":

// set name for user

// code=16,告诉房间里的其他人,你改了昵称

user.setName(message);

sendRoomMsg(buildCodeWithMsg(""+user.getId()+""+message+"", 16));

break;

default:

// returnMsg("something unknown");

System.out.println("not valid message from user"+user.getId());

break;

}

}

}

/**

* 获得该用户房间中的所有用户列表,并构造成一定格式的消息返回

* @return

*/

private String getMembersInRoom(){

/*先从room列表获得该用户的room*/

Room room = map.getRoom(roomId);

StringBuffer stringBuffer = new StringBuffer();

if(room != null){

/*获得房间中所有的用户的列表,然后构造成一定的格式发送回去*/

ArrayList users = room.getUsers();

for(User each: users){

stringBuffer.append(""+each.getName()+

""+each.getId()+"");

}

}

return stringBuffer.toString();

}

/**

* 获得所有房间的列表,并构造成一定的格式

* @return

*/

private String getRoomsList(){

String[][] strings = map.listRooms();

StringBuffer sb = new StringBuffer();

for(int i=0; i

sb.append(""+strings[i][1]+

""+strings[i][0]+"");

}

return sb.toString();

}

/**

* 构造成一个统一的消息格式

* @param msg

* @param code

* @return

*/

private String buildCodeWithMsg(String msg, int code){

return ""+code+""+msg+"\n";

}

/**

* 这个是群发消息:全体用户,code>10

* @param msg

*/

private void sendMsg(String msg) {

// System.out.println("In sendMsg()");

/*取出用户列表中的每一个用户来发送消息*/

for(User each:userList){

try {

pw=each.getPw();

pw.println(msg);

pw.flush();

System.out.println(msg);

} catch (Exception e) {

System.out.println("exception in sendMsg()");

}

}

}

/**

* 只对同一房间的用户发:code>10

* @param msg

*/

private void sendRoomMsg(String msg){

/*先获得该用户的房间号,然后往该房间发送消息*/

Room room = map.getRoom(roomId);

if(room != null){

ArrayList users = room.getUsers();

for(User each: users){

pw = each.getPw();

pw.println(msg);

pw.flush();

}

}

}

/**

* 向房间中除了该用户自己,发送消息

* @param msg

*/

private void sendRoomMsgExceptSelf(String msg){

Room room = map.getRoom(roomId);

if(room != null){

ArrayList users = room.getUsers();

for(User each: users){

if(each.getId()!=user.getId()){

pw = each.getPw();

pw.println(msg);

pw.flush();

}

}

}

}

/**

* 对于client的来信,返回一个结果,code<10

* @param msg

*/

private void returnMsg(String msg){

try{

pw = user.getPw();

pw.println(msg);

pw.flush();

}catch (Exception e) {

System.out.println("exception in returnMsg()");

}

}

/**

* 移除该用户,并向房间中其他用户发送该用户已经退出的消息

* 如果房间中没人了,那么就更新房间列表给所有用户

* @param user

*/

private void remove(User user){

if(roomId!=-1){

int flag=map.esc(user, roomId);

sendRoomMsgExceptSelf(buildCodeWithMsg(""+user.getId(), 12));

long oldRoomId=roomId;

roomId = -1;

if(flag==0){

sendMsg(buildCodeWithMsg(""+oldRoomId, 13));

}

}

userList.remove(user);

}

}

客户端

Client

package Client;

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.Dimension;

import java.awt.FlowLayout;

import java.awt.Font;

import java.awt.GridBagConstraints;

import java.awt.GridBagLayout;

import java.awt.Insets;

imporhttp://t java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.Socket;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Set;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import javax.swing.DefaultListModel;

import javax.swing.Icon;

import javax.swing.ImageIcon;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JList;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

import javax.swing.JScrollBar;

import javax.swing.JScrollPane;

import javax.swing.JTextField;

import javax.swing.JTextPane;

import javax.swing.UIManager;

import javax.swing.UnsupportedLookAndFeelException;

import javax.swing.text.BadLocationException;

import javax.swing.text.SimpleAttributeSet;

import javax.swing.text.Style;

import javax.swing.text.StyleConstants;

import javax.swing.text.StyledDocument;

/**

*

* @author lannooo

*

*/

public class Client implements ActionListener{

private JFrame frame;

private Socket socket;

private BufferedReader br;

private PrintWriter pw;

private String name;

private HashMap rooms_map;

private HashMap users_map;

private JTextField host_textfield;

private JTextField port_textfield;

private JTextField text_field;

private JTextField name_textfiled;

private JLabel rooms_label;

private JLabel users_label;

private JList roomlist;

private JList userlist;

private JTextPane msgArea;

private JScrollPane textScrollPane;

private JScrollBar vertical;

DefaultListModel rooms_model;

DefaultListModel users_model;

/*

* 构造函数

* 该客户端对象维护两个map,房间的hashmap和房间中用户的hashmap

* 作为列表组件的数据模型

*/

public Client(){

rooms_map = new HashMap<>();

users_map = new HashMap<>();

initialize();

}

/**

* 连接服务端,指定host和port

* @param host

* @param port

* @return

*/

public boolean connect(String host, int port){

try {

socket = new Socket(host, port);

System.out.println("Connected to server!"+socket.getRemoteSocketAddress());

br=new BufferedReader(new InputStreamReader(System.in));

pw=new PrintWriter(socket.getOutputStream());

/*

* 创建一个接受和解析服务器消息的线程

* 传入当前客户端对象的指针,作为句柄调用相应的处理函数

*/

ClientThread thread = new ClientThread(socket, this);

thread.start();

return true;

} catch (IOException e) {

System.out.println("Server error");

JOptionPane.showMessageDialog(frame, "服务器无法连接!");

return false;

}

}

/*当前进程作为只发送消息的线程,从命令行中获取输入*/

// public void sendMsg(){

// String msg;

// try {

// while(true){

// msg = br.readLine();

// pw.println(msg);

// pw.flush();

// }

// } catch (IOException e) {

// System.out.println("error when read msg and to send.");

// aJYNtQUjh}

// }

/**

* 发给服务器的消息,先经过一定的格式构造再发送

* @param msg

* @param code

*/

public void sendMsg(String msg, String code){

try {

pw.println(""+code+""+msg+"");

pw.flush();

} catch (Exception e) {

//一般是没有连接的问题

System.out.println("error in sendMsg()");

JOptionPane.showMessageDialog(frame, "请先连接服务器!");

}

}

/**

* 窗口初始化

*/

private void initialize() {

/*设置窗口的UI风格和字体*/

setUIStyle();

setUIFont();

JFrame frame = new JFrame("ChatOnline");

JPanel panel = new JPanel(); /*主要的panel,上层放置连接区,下层放置消息区,

中间是消息面板,左边是room列表,右边是当前room的用户列表*/

JPanel headpanel = new JPanel(); /*上层panel,用于放置连接区域相关的组件*/

JPanel footpanel = new JPanel(); /*下层panel,用于放置发送信息区域的组件*/

JPanel leftpanel = new JPanel(); /*左边panel,用于放置房间列表和加入按钮*/

JPanel rightpanel = new JPanel(); /*右边panel,用于放置房间内人的列表*/

/*最上层的布局,分中间,东南西北五个部分*/

BorderLayout layout = new BorderLayout();

/*格子布局,主要用来设置西、东、南三个部分的布局*/

GridBagLayout gridBagLayout = new GridBagLayout();

/*主要设置北部的布局*/

FlowLayout flowLayout = new FlowLayout();

/*设置初始窗口的一些性质*/

frame.setBounds(100, 100, 800, 600);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setContentPane(panel);

frame.setLayout(layout);

/*设置各个部分的panel的布局和大小*/

headpanel.setLayout(flowLayout);

footpanel.setLayout(gridBagLayout);

leftpanel.setLayout(gridBagLayout);

rightpanel.setLayout(gridBagLayout);

leftpanel.setPreferredSize(new Dimension(130, 0));

rightpanel.setPreferredSize(new Dimension(130, 0));

/*以下均是headpanel中的组件*/

host_textfield = new JTextField("127.0.0.1");

port_textfield = new JTextField("9999");

name_textfiled = new JTextField("匿名");

host_textfield.setPreferredSize(new Dimension(100, 25));

port_textfield.setPreferredSize(new Dimension(70, 25));

name_textfiled.setPreferredSize(new Dimension(150, 25));

JLabel host_label = new JLabel("服务器IP");

JLabel port_label = new JLabel("端口");

JLabel name_label = new JLabel("昵称");

JButton head_connect = new JButton("连接");

// JButton head_change = new JButton("确认更改");

JButton head_create = new JButton("创建房间");

headpanel.add(host_label);

headpanel.add(host_textfield);

headpanel.add(port_label);

headpanel.add(port_textfield);

headpanel.add(head_connect);

headpanel.add(name_label);

headpanel.add(name_textfiled);

// headpanel.add(head_change);

headpanel.add(head_create);

/*以下均是footpanel中的组件*/

JButton foot_emoji = new JButton("表情");

JButton foot_send = new JButton("发送");

text_field = new JTextField();

footpanel.add(text_field, new GridBagConstraints(0, 0, 1, 1, 100, 100,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

footpanel.add(foot_emoji, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

footpanel.add(foot_send, new GridBagConstraints(2, 0, 1, 1, 1.0, 1.0,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

/*两边的格子中的组件*/

rooms_label = new JLabel("当前房间数:0");

users_label = new JLabel("房间内人数:0");

JButton join_button = new JButton("加入房间");

JButton esc_button = new JButton("退出房间");

rooms_model = new DefaultListModel<>();

users_model = new DefaultListModel<>();

// rooms_model.addElement("房间1");

// rooms_model.addElement("房间2");

// rooms_model.addElement("房间3");

// String fangjian = "房间1";

// rooms_map.put(fangjian, 1);

roomlist = new JList<>(rooms_model);

userlist = new JList<>(users_model);

JScrollPane roomListPane = new JScrollPane(roomlist);

JScrollPane userListPane = new JScrollPane(userlist);

leftpanel.add(rooms_label, new GridBagConstraints(0, 0, 1, 1, 1, 1,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

leftpanel.add(join_button, new GridBagConstraints(0, 1, 1, 1, 1, 1,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

leftpanel.add(esc_button, new GridBagConstraints(0, 2, 1, 1, 1, 1,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

leftpanel.add(roomListPane, new GridBagConstraints(0, 3, 1, 1, 100, 100,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

rightpanel.add(users_label, new GridBagConstraints(0, 0, 1, 1, 1, 1,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

rightpanel.add(userListPane,new GridBagConstraints(0, 1, 1, 1, 100, 100,

GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));

/*中间的文本区组件*/

msgArea = new JTextPane();

msgArea.setEditable(false);

textScrollPane = new JScrollPane();

textScrollPane.setViewportView(msgArea);

vertical = new JScrollBar(JScrollBar.VERTICAL);

vertical.setAutoscrolls(true);

textScrollPane.setVerticalScrollBar(vertical);

/*设置顶层布局*/

panel.add(headpanel, "North");

panel.add(footpanel, "South");

panel.add(leftpanel, "West");

panel.add(rightpanel, "East");

panel.add(textScrollPane, "Center");

/*注册各种事件*/

/*连接服务器*/

head_connect.addActionListener(this);

/*发送消息,如果没有连接则会弹窗提示*/

foot_send.addActionListener(this);

/*改名字*/

// head_change.addActionListener(this);

/*创建房间*/

head_create.addActionListener(this);

/*发送表情*/

foot_emoji.addActionListener(this);

/*加入room*/

join_button.addActionListener(this);

/*退出房间*/

esc_button.addActionListener(this);

/*最终显示*/

frame.setVisible(true);

}

/**

* 事件监听处理

*/

@Override

public void actionPerformed(ActionEvent e) {

String cmd = e.getActionCommand();

switch (cmd) {

case "连接": /*点击连接按钮*/

String strhost = host_textfield.getText();

String strport = port_textfield.getText();

connect(strhost, Integer.parseInt(strport));

String nameSeted = JOptionPane.showInputDialog("请输入你的昵称:"); /*提示输入昵称*/

name_textfiled.setText(nameSeted);

name_textfiled.setEditable(false);

port_textfield.setEditable(false);

host_textfield.setEditable(false);

/*发送设置姓名的消息和列出用户列表的消息*/

sendMsg(nameSeted, "setname");

sendMsg("", "list");

break;

// case "确认更改":

// String strname = name_textfiled.getText();

// name = strname;

// sendMsg(strname, "setname");

// break;

case "加入房间": /*选择房间后,点击加入房间按钮*/

String selected = roomlist.getSelectedValue();

if(rooms_map.containsKey(selected)){

sendMsg(""+rooms_map.get(selected), "join");

}

break;

case "退出房间": /*点击退出房间的按钮*/

sendMsg("", "esc");

break;

case "发送": /*点击发送消息的按钮*/

String text = text_field.getText();

text_field.setText("");

sendMsg(text, "message");

break;

case "表情": /*发送表情,新建一个表情窗口,并直接在表情窗口中处理消息发送*/

IconDialog dialog = new IconDialog(frame, this);

break;

case "创建房间": /*点击创建房间的按钮,弹出提示框数据房间名称*/

String string = JOptionPane.showInputDialog("请输入你的房间名称");

if(string==null || string.equals("")){

string = name+(int)(Math.random()*10000)+"的房间";

}

sendMsg(string, "create");

break;

default:

break;

}

}

/*很多辅助和clientThread互动的*/

/**

* 加入用户,通过正则表达式,匹配消息内容中的用户信息

* @param content

*/

public void addUser(String content){

if(content.length()>0){

Pattern pattern = Pattern.compile("(.*)(.*)");

Matcher matcher = pattern.matcher(content);

if(matcher.find()){

/*

* 获得用户的name和id

* 加入用户列表

* 在消息区显示系统提示

*/

String name = matcher.group(1);

String id = matcher.group(2);

insertUser(Integer.parseInt(id), name);

insertMessage(textScrollPane, msgArea, null, "系统:", name+"加入了聊天室");

}

}

users_label.setText("房间内人数:"+users_map.size()); /*更新房间内的人数*/

}

/**

* 删除用户

* @param content

*/

public void delUser(String content){

if(content.length()>0){

int id = Integer.parseInt(content);

/*

* 从维护的用户map中取得所有的用户名字,然后去遍历匹配的用户

* 匹配到的用户名字从相应的数据模型中移除

* 并从map中移除,并在消息框中提示系统消息

*/

Set set = users_map.keySet();

Iterator iter = set.iterator();

String name=null;

while(iter.hasNext()){

name = iter.next();

if(users_map.get(name)==id){

users_model.removeElement(name);

break;

}

}

users_map.remove(name);

insertMessage(textScrollPane, msgArea, null, "系统:", name+"退出了聊天室");

}

users_label.setText("房间内人数:"+users_map.size());

}

/**

* 更新用户信息

* @param content

*/

public void updateUser(String content){

if(content.length()>0){

Pattern pattern = Pattern.compile("(.*)(.*)");

Matcher matcher = pattern.matcher(content);

if(matcher.find()){

String id = matcher.group(1);

String name = matcher.group(2);

insertUser(Integer.parseInt(id), name);

}

}

}

/**

* 列出所有用户

* @param content

*/

public void listUsers(String content){

String name = null;

String id=null;

Pattern rough_pattern=null;

Matcher rough_matcher=null;

Pattern detail_pattern=null;

/*

* 先用正则表达式匹配用户信息

* 然后插入数据模型中

* 并更新用户数据模型中的条目

*/

if(content.length()>0){

rough_pattern = Pattern.compile("(.*?)");

rough_matcher = rough_pattern.matcher(content);

while(rough_matcher.find()){

String detail = rough_matcher.group(1);

detail_pattern = Pattern.compile("(.*)(.*)");

Matcher detail_matcher = detail_pattern.matcher(detail);

if(detail_matcher.find()){

name = detail_matcher.group(1);

id = detail_matcher.group(2);

insertUser(Integer.parseInt(id), name);

}

}

}

users_label.setText("房间内人数:"+users_map.size());

}

/**

* 直接在textarea中显示消息

* @param content

*/

public void updateTextArea(String content){

insertMessage(textScrollPane, msgArea, null, "系统:", content);

}

/**

* 在textarea中显示其他用户的消息

* 先用正则匹配,再显示消息

* 其中还需要匹配emoji表情的编号

* @param content

*/

public void updateTextAreaFromUser(String content){

if(content.length()>0){

Pattern pattern = Pattern.compile("(.*)(.*)");

Matcher matcher = pattern.matcher(content);

if(matcher.find()){

String from = matcher.group(1);

String smsg = matcher.group(2);

String fromName = getUserName(from);

if(fromName.equals(name))

fromName = "你";

if(smsg.startsWith("")){

String emojiCode = smsg.substring(7, smsg.length()-8);

// System.out.println(emojiCode);

insertMessage(textScrollPane, msgArea, emojiCode, fromName+"说:", null);

return ;

}

insertMessage(textScrollPane, msgArea, null, fromName+"说:", smsg);

}

}

}

/**

* 显示退出的结果

* @param content

*/

public void showEscDialog(String content){

JOptionPane.showMessageDialog(frame, content);

/*清除消息区内容,清除用户数据模型内容和用户map内容,更新房间内人数*/

msgArea.setText("");

users_model.clear();

users_map.clear();

users_label.setText("房间内人数:0");

}

/**

* 新增一个room

* @param content

*/

public void addRoom(String content){

if(content.length()>0){

Pattern pattern = Pattern.compile("(.*)(.*)");

Matcher matcher = pattern.matcher(content);

if(matcher.find()){

String rid = matcher.group(1);

String rname = matcher.group(2);

insertRoom(Integer.parseInt(rid), rname);

}

}

rooms_label.setText("当前房间数:"+rooms_map.size());

}

/**

* 删除一个room

* @param content

*/

public void delRoom(String content){

if(content.length()>0){

int delRoomId = Integer.parseInt(content);

Set set = rooms_map.keySet();

Iterator iter = set.iterator();

String rname=null;

while(iter.hasNext()){

rname = iter.next();

if(rooms_map.get(rname)==delRoomId){

rooms_model.removeElement(rname);

break;

}

}

rooms_map.remove(rname);

}

rooms_label.setText("当前房间数:"+rooms_map.size());

}

/**

* 列出目前所有的rooms

* @param content

*/

public void listRooms(String content){

String rname = null;

String rid=null;

Pattern rough_pattern=null;

Matcher rough_matcher=null;

Pattern detail_pattern=null;

if(content.length()>0){

rough_pattern = Pattern.compile("(.*?)");

rough_matcher = rough_pattern.matcher(content);

while(rough_matcher.find()){

String detail = rough_matcher.group(1);

detail_pattern = Pattern.compile("(.*)(.*)");

Matcher detail_matcher = detail_pattern.matcher(detail);

if(detail_matcher.find()){

rname = detail_matcher.group(1);

rid = detail_matcher.group(2);

insertRoom(Integer.parseInt(rid), rname);

}

}

}

rooms_label.setText("当前房间数:"+rooms_map.size());

}

/**

* 插入一个room

* @param rid

* @param rname

*/

private void insertRoom(Integer rid, String rname){

if(!rooms_map.containsKey(rname)){

rooms_map.put(rname, rid);

rooms_model.addElement(rname);

}else{

rooms_map.remove(rname);

rooms_model.removeElement(rname);

rooms_map.put(rname, rid);

rooms_model.addElement(rname);

}

rooms_label.setText("当前房间数:"+rooms_map.size());

}

/**

* 插入一个user

* @param id

* @param name

*/

private void insertUser(Integer id, String name){

if(!users_map.containsKey(name)){

users_map.put(name, id);

users_model.addElement(name);

}else{

users_map.remove(name);

users_model.removeElement(name);

users_map.put(name, id);

users_model.addElement(name);

}

users_label.setText("房间内人数:"+users_map.size());

}

/**

* 获得用户的姓名

* @param strId

* @return

*/

private String getUserName(String strId){

int uid = Integer.parseInt(strId);

Set set = users_map.keySet();

Iterator iterator = set.iterator();

String cur=null;

while(iterator.hasNext()){

cur = iterator.next();

if(users_map.get(cur)==uid){

return cur;

}

}

return "";

}

/**

* 获得用户所在房间的名称

* @param strId

* @return

*/

private String getRoomName(String strId){

int rid = Integer.parseInt(strId);

Set set = rooms_map.keySet();

Iterator iterator = set.iterator();

String cur = null;

while(iterator.hasNext()){

cur = iterator.next();

if(rooms_map.get(cur)==rid){

return cur;

}

}

return "";

}

/**

* 打印一条消息,如果有图片就打印图片,否则打印content

* @param scrollPane

* @param textPane

* @param icon_code

* @param title

* @param content

*/

private void insertMessage(JScrollPane scrollPane, JTextPane textPane,

String icon_code, String title, String content){

StyledDocument document = textPane.getStyledDocument(); /*获取textpane中的文本*/

/*设置标题的属性*/

SimpleAttributeSet title_attr = new SimpleAttributeSet();

StyleConstants.setBold(title_attr, true);

StyleConstants.setForeground(title_attr, Color.BLUE);

/*设置正文的属性*/

SimpleAttributeSet content_attr = new SimpleAttributeSet();

StyleConstants.setBold(content_attr, false);

StyleConstants.setForeground(content_attr, Color.BLACK);

Style style = null;

if(icon_code!=null){

Icon icon = new ImageIcon("icon/"+icon_code+".png");

style = document.addStyle("icon", null);

StyleConstants.setIcon(style, icon);

}

try {

document.insertString(document.getLength(), title+"\n", title_attr);

if(style!=null)

document.insertString(document.getLength(), "\n", style);

else

document.insertString(document.getLength(), " "+content+"\n", content_attr);

} catch (BadLocationException ex) {

System.out.println("Bad location exception");

}

/*设置滑动条到最后*/

vertical.setValue(vertical.getMaximum());

}

/**

* 设置需要美化字体的组件

*/

public static void setUIFont()

{

Font f = new Font("微软雅黑", Font.PLAIN, 14);

String names[]={ "Label", "CheckBox", "PopupMenu","MenuItem", "CheckBoxMenuItem",

"JRadioButtonMenuItem","ComboBox", "Button", "Tree", "ScrollPane",

"TabbedPane", "EditorPane", "TitledBorder", "Menu", "TextArea","TextPane",

"OptionPane", "MenuBar", "ToolBar", "ToggleButton", "ToolTip",

"ProgressBar", "TableHeader", "Panel", "List", "ColorChooser",

"PasswordField","TextField", "Table", "Label", "Viewport",

"RadioButtonMenuItem","RadioButton", "DesktopPane", "InternalFrame"

};

for (String item : names) {

UIManager.put(item+ ".font",f);

}

}

/**

* 设置UI风格为当前系统的风格

*/

public static void setUIStyle(){

String lookAndFeel =UIManager.getSystemLookAndFeelClassName();

try {

UIManager.setLookAndFeel(lookAndFeel);

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (InstantiationException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (UnsupportedLookAndFeelException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 测试用的main函数

* @param args

*/

public static void main(String[] args) {

Client client = new Client();

}

}

ClientThread

package Client;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.Socket;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

/**

*

* @author lannooo

*

*/

public class ClientThread extends Thread{

private Socket socket;

private Client client;

private BufferedReader br;

private PrintWriter pw;

/**

* 从过主线程传入的socket和client对象来构造

* @param socket

* @param client

*/

public ClientThread(Socket socket, Client client){

this.client = client;

this.socket = socket;

try {

br=new BufferedReader(new InputStreamReader(socket.getInputStream()));

} catch (IOException e) {

System.out.println("cannot get inputstream from socket.");

}

}

/**

* 不断的读数据并处理

* 调用主线程的方法来处理:client.method();

*/

public void run() {

try{

br=new BufferedReader(new InputStreamReader(socket.getInputStream()));

while(true){

String msg = br.readLine();

parseMessage(msg);

}

}catch (Exception e) {

e.printStackTrace();

}

}

/**

* 处理从服务器收到的消息

* @param message

*/

public void parseMessage(String message){

String code = null;

String msg=null;

/*

* 先用正则表达式匹配code码和msg内容

*/

if(message.length()>0){

Pattern pattern = Pattern.compile("(.*)");

Matcher matcher = pattern.matcher(message);

if(matcher.find()){

code = matcher.group(1);

}

pattern = Pattern.compile("(.*)");

matcher = pattern.matcher(message);

if(matcher.find()){

msg = matcher.group(1);

}

System.out.println(code+":"+msg);

switch(code){

case "1": /*一个普通消息处理*/

client.updateTextArea(msg);

break;

case "2": /*退出消息*/

client.showEscDialog(msg);

break;

case "3": /*列出房间*/

client.listRooms(msg);

break;

case "4": /*其他用户的消息*/

client.updateTextAreaFromUser(msg);

break;

case "5": /*普通消息处理*/

client.updateTextArea(msg);

break;

case "11": /*添加用户*/

client.addUser(msg);

break;

case "12": /*删除用户*/

client.delUser(msg);

break;

case "13": /*删除房间*/

client.delRoom(msg);

break;

case "15": /*添加房间*/

client.addRoom(msg);

break;

case "16": /*更新用户名称*/

client.updateUser(msg);

break;

case "21": /*列出用户列表*/

client.listUsers(msg);

break;

}

}

}

}

IconDialog(选择表情界面)

package Client;

import java.awt.Container;

import java.awt.Dialog;

import java.awt.FlowLayout;

import java.awt.GridLayout;

import java.awt.Image;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.ImageIcon;

import javax.swing.JButton;

import javax.swing.JDialog;

import javax.swing.JFrame;

/**

*

* @author lannooo

*

*/

public class IconDialog implements ActionListener {

private JDialog dialog;

private Client client;

/**

* 通过frame和客户端对象来构造

* @param frame

* @param client

*/

public IconDialog(JFrame frame, Client client) {

this.client = client;

dialog = new JDialog(frame, "请选择表情", true);

/*16个表情*/

JButton[] icon_button = new JButton[16];

ImageIcon[] icons = new ImageIcon[16];

/*获得弹出窗口的容器,设置布局*/

Container dialogPane = dialog.getContentPane();

dialogPane.setLayout(new GridLayout(0, 4));

/*加入表情*/

for(int i=1; i<=15; i++){

icons[i] = new ImageIcon("icon/"+i+".png");

icons[i].setImage(icons[i].getImage().getScaledInstance(50, 50, Image.SCALE_DEFAULT));

icon_button[i] = new JButton(""+i, icons[i]);

icon_button[i].addActionListener(this);

dialogPane.add(icon_button[i]);

}

dialog.setBounds(200,266,266,280);

dialog.show();

}

@Override

public void actionPerformed(ActionEvent e) {

/*构造emoji结构的消息发送*/

String cmd = e.getActionCommand();

System.out.println(cmd);

dialog.dispose();

client.sendMsg(""+cmd+"", "message");

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:全局共享文件系统(文件管理共享系统)
下一篇:java 键盘输入一个数,输出数组中指定元素的示例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~