当前位置: 移动技术网 > IT编程>开发语言>Java > Java GUI编程实现在线聊天室

Java GUI编程实现在线聊天室

2019年07月19日  | 移动技术网IT编程  | 我要评论

引言

综合应用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");
 }

}

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网