Java GUI编程——在线聊天室

来源:互联网 发布:mac双系统修复windows 编辑:程序博客网 时间:2024/06/05 09:21

引言

综合应用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");    }}
0 0
原创粉丝点击