欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java GUI编程实现在线聊天室

程序员文章站 2024-02-22 09:11:41
引言 综合应用java的gui编程和网络编程,实现一个能够支持多组用户同时使用的聊天室软件。该聊天室具有比较友好的gui界面,并使用c/s模式,支持多个用户同时使用,用户...

引言

综合应用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+"#]<socket:"+socket+">";
 }
}

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<user> 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<user> 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<long, room> 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<max_rooms){
   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<entry<long, room>> set = map.entryset();
  iterator<entry<long, room>> iterator = set.iterator();
  while(iterator.hasnext()){
   map.entry<long, room> 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<user> allusers;
 private roomlist rooms;
 private int port;
 private serversocket ss;
 private long 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<user> userlist;/*保存用户列表*/
 private roomlist map;   /*保存房间列表*/
 private long roomid;
 private printwriter pw;
 /**
  * 通过用户的对象实例、全局的用户列表、房间列表进行构造
  * @param user
  * @param userlist
  * @param map
  */
 public serverthread(user user, 
   arraylist<user> 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("<code>(.*)</code>");
   matcher matcher = pattern.matcher(msg);
   if(matcher.find()){
    code = matcher.group(1);
   }
   /*匹配消息部分的字符串*/
   pattern = pattern.compile("<msg>(.*)</msg>");
   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("<name>"+user.getname()+"</name><id>"+user.getid()+"</id>", 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("<name>"+user.getname()+"</name><id>"+user.getid()+"</id>", 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("<from>"+user.getid()+"</from><smsg>"+message+"</smsg>", 4));
    break;
   case "create":
    // create a room 
    // code=5,提示用户进入了房间
    // code=15,需要在其他所有用户的room列表中更新
    roomid = map.createroom(message);
    map.join(user, roomid);
    sendmsg(buildcodewithmsg("<rid>"+roomid+"</rid><rname>"+message+"</rname>", 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("<id>"+user.getid()+"</id><name>"+message+"</name>", 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<user> users = room.getusers();
   for(user each: users){
    stringbuffer.append("<member><name>"+each.getname()+
        "</name><id>"+each.getid()+"</id></member>");
   }
  }
  return stringbuffer.tostring();
 }

 /**
  * 获得所有房间的列表,并构造成一定的格式
  * @return
  */
 private string getroomslist(){
  string[][] strings = map.listrooms();
  stringbuffer sb = new stringbuffer();
  for(int i=0; i<strings.length; i++){
   sb.append("<room><rname>"+strings[i][1]+
      "</rname><rid>"+strings[i][0]+"</rid></room>");
  }
  return sb.tostring();
 }

 /**
  * 构造成一个统一的消息格式
  * @param msg
  * @param code
  * @return
  */
 private string buildcodewithmsg(string msg, int code){
  return "<code>"+code+"</code><msg>"+msg+"</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<user> 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<user> 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;
import 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<string, integer> rooms_map;
 private hashmap<string, integer> 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<string> roomlist;
 private jlist<string> userlist;
 private jtextpane msgarea;
 private jscrollpane textscrollpane;
 private jscrollbar vertical;
 defaultlistmodel<string> rooms_model;
 defaultlistmodel<string> 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.");
//  }
// }

 /**
  * 发给服务器的消息,先经过一定的格式构造再发送
  * @param msg
  * @param code
  */
 public void sendmsg(string msg, string code){
  try {
   pw.println("<code>"+code+"</code><msg>"+msg+"</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("<name>(.*)</name><id>(.*)</id>");
   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<string> set = users_map.keyset();
   iterator<string> 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("<id>(.*)</id><name>(.*)</name>");
   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("<member>(.*?)</member>");
   rough_matcher = rough_pattern.matcher(content);
   while(rough_matcher.find()){
    string detail = rough_matcher.group(1);
    detail_pattern = pattern.compile("<name>(.*)</name><id>(.*)</id>");
    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("<from>(.*)</from><smsg>(.*)</smsg>");
   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("<emoji>")){
     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("<rid>(.*)</rid><rname>(.*)</rname>");
   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<string> set = rooms_map.keyset();
   iterator<string> 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("<room>(.*?)</room>");
   rough_matcher = rough_pattern.matcher(content);
   while(rough_matcher.find()){
    string detail = rough_matcher.group(1);
    detail_pattern = pattern.compile("<rname>(.*)</rname><rid>(.*)</rid>");
    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<string> set = users_map.keyset();
  iterator<string> 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<string> set = rooms_map.keyset();
  iterator<string> 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("<code>(.*)</code>");
   matcher matcher = pattern.matcher(message);
   if(matcher.find()){
    code = matcher.group(1);
   }
   pattern = pattern.compile("<msg>(.*)</msg>");
   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("<emoji>"+cmd+"</emoji>", "message");
 }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。