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"); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。