初学java之模拟QQ聊天软件(简单实现)

引言:这个程序是自从学习java以来写的第一个比较大的图形界面程序,花费了大约一周的时间,作为期末的课程设计,也算是基本上完成了任务,当然由于作者的编程能力有限,代码中难免存在BUG,不太简练,今天搬到博客上来,也算是对自己学习历程的一段记录吧,当然,如果我的这篇博客有幸被诸君看到,欢迎提出意见和建议,这篇博客仅仅作为我的一个记录吧,如果可能(估计也没时间了),将在后续版本中不断完善。所有源代码移步:http://download.csdn.net/detail/chengshijian2015/9724315

1设计要求
设计并实现一个基于TCP协议或者UDP协议的文字聊天软件,具体实现可以实现成类似于腾讯QQ式的,用户端通过登录服务器找到彼此,用户端通信时采用点对点,不用通过服务器转发,通信双方都可以主动发起连接或主动通信。
要求:
1、采用Windows应用程序风格,功能完整,显示直观,色彩协调,界面友好,操作简单。
2、设置在线用户列表,显示在线用户,并可以选择在线列表中的用户进行聊天。
3、用户端与服务器通信采用TCP协议;用户端与用户端通信采用UDP协议。
4、 一个用户端可以同时和多个其他用户端聊天。

2设计原理和技术
2.1 多线程
很多应用程序都存在对并发性的需求。例如,一个图形界面的应用程序希望在处理长时间任务的同时能快速响应用户的鼠标和键盘事件,一个在因特网上运行的服务程序希望能同时处理大量的客户请求。多线程(multithreading)为处理程序中的并发性提供了强有力的工具。
本程序主要通过两种方法创建线程:实现runnable接口,并重写里面的run()方法和直接继承Thread类,并重写里面的run(),实现多线程,其中实现Runnable接口时,亦主要是通过两种方法创建线程:直接创建类实现Runnable接口和直接在程序中创建一个继承式的匿名内部类,创建一个线程。
值得注意的是,在JVM环境中,用户程序以线程的形式来运行。当所有的非守护线程结束时,程序也就停止了。
2.2 图形用户界面
Java语言图形用户界面(Graphics User Interface,GUI)是实现用户与程序之间交互的一个控制面板,包含菜单、组件(或控件)。通过这种直观的图形界面接收用户的输入,显示程序运行结果。Java GUI从最初的抽象窗口工具包AWT(Abstract Window ToolKit),到后来的Swing组件,现在还有MyEclipse的SWT(Standard widget Toolkit)及构件基于SWT的JFace,满足不同时期、不同应用市场的需求。
2.3 网络编程
网络应用是Java语言取得成功的领域之一。Java提供了java.net包,其中包括一系列网络类,支持Java程序直接发送与接收TCP或UDP数据报。程序员使用这些类可轻松实现基于TCP和UDP的编程。
UDP的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的特点,所以通常音频、视频和普通数据在传输中使用UDP较多,因为它们偶尔丢失一两个数据包,也不会对接收结果产生太大影响,所以本程序客户端与客户端的之间使用UDP传输。
TCP协议能为应用程序提供可靠的通信连接使一台计算机发出的字节流无差错的发送到其他电脑,对于可靠性要求高的数据通信往往使用TCP通信,所以本程序客户端与服务器端之间使用TCP传输。
3总体设计
本程序是一个模仿当前最流行的聊天软件QQ而设计的简单聊天软件。本程序采用面向对象程序分析设计方法,采用Java语言实现,设计了一个服务器端,和一个客户端,其中服务器与客户端之间采用TCP协议通信,客户端与客户端之间采用UDP协议通信,利用了Java的多线程技术编写而成。
程序主要采用14个Java类实现,共放在6个不同的包中。
程序中包和类的组织结构如图3-1所示:
初学java之模拟QQ聊天软件(简单实现)_第1张图片
图3-1 包和类的组织结构

————————————–所有的源代码————————————–
3详细设计及实现
3.1服务器端
3.1.1服务器主类ChatRoomServerMain

package server;

public class ChatRoomServerMain
{

    /**
     * @author CSJ
     */
    public static void main(String[] args)
    {
        new ChatRoomServerFrame();
    }
}

3.1.2服务器框架类ChatRoomServerFrame

package server;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import stream.ServerClientConnectionStream;
import user.UserInformation;

public class ChatRoomServerFrame extends JFrame
{

    /**
     * @author CSJ
     */
    private static final long serialVersionUID = 1L;
    private Map userMap;
    private Set userSet;
    private ServerSocket serverSocket;
    private JButton start;
    private JLabel welcome;
    private JTextArea hintInfo;
    private JTextArea onlineUserInfo;
    private JTextArea startInfoTextArea;

    public ChatRoomServerFrame()
    {
        super();
        userMap = new HashMap();
        userSet = new HashSet();
        welcome = new JLabel("服务器端");
        hintInfo = new JTextArea("当前状态:服务器未启动!\n");
        startInfoTextArea = new JTextArea();
        startInfoTextArea.setEditable(false);
        hintInfo.setEditable(false);
        onlineUserInfo = new JTextArea();
        onlineUserInfo.setEditable(false);
        createFrame();
        addEventHandler();
    }

    private void createFrame()
    {
        start = new JButton("启动服务器");
        JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.TOP);
        JScrollPane hintPanel = new JScrollPane(hintInfo);
        JScrollPane onlineUserInfoPanel = new JScrollPane(onlineUserInfo);
        JScrollPane startInfoScrollPanel = new JScrollPane(startInfoTextArea);
        JPanel welcomePanel = new JPanel();
        welcomePanel.add(welcome);
        JPanel southPanel = new JPanel();
        southPanel.add(start);
        jTabbedPane.add("状态", hintPanel);
        jTabbedPane.add("运行详情", startInfoScrollPanel);
        jTabbedPane.add("在线用户详情", onlineUserInfoPanel);
        setSize(400, 600);
        setVisible(true);
        add(welcomePanel, BorderLayout.NORTH);
        add(jTabbedPane, BorderLayout.CENTER);
        add(southPanel, BorderLayout.SOUTH);
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension screenSize = toolkit.getScreenSize();
        int height = getHeight();
        int width = getWidth();
        int screenWidth = screenSize.width / 2;
        int screenHeight = screenSize.height / 2;
        setLocation(screenWidth - width / 2, screenHeight - height / 2);
    }

    public void addEventHandler()
    {
        start.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                try
                {
                    serverSocket = new ServerSocket(9000);
                    new Thread(new Runnable()
                    {

                        @Override
                        public void run()
                        {
                            startServer();
                        }
                    }).start();
                    JOptionPane.showMessageDialog(null, "服务器已启动!");
                } catch (IOException e)
                {
                    JOptionPane.showMessageDialog(null, "服务器已启动!");
                    e.printStackTrace();
                }
            }
        });

        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                int t = JOptionPane.showConfirmDialog(null, "确认要退出服务器吗?", "确认退出", JOptionPane.OK_CANCEL_OPTION);
                if (t == JOptionPane.OK_OPTION)
                {
                    System.exit(0);
                }
            }
        });
    }

    private void startServer()
    {
        while (true)
        {
            try
            {
                System.out.println("开始监听客户端连接...");
                startInfoTextArea.append("开始监听客户端连接...\n");
                printServerInfo();
                Socket socket = serverSocket.accept();
                startInfoTextArea.append("已从客户端IP:" + socket.getInetAddress().getHostAddress() + ",端口号(TCP):"
                        + socket.getPort() + "接收到数据...\n");
                new Thread(new ChatRoomServerThread(new ServerClientConnectionStream(socket), userSet, userMap,
                        hintInfo, startInfoTextArea, onlineUserInfo)).start();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        } // while
    }

    private void printServerInfo()
    {
        hintInfo.setText("当前状态:开始监听客户端连接...");
        try
        {
            hintInfo.append("\n服务器名称:" + InetAddress.getLocalHost().getHostName());
            hintInfo.append("\n服务器IP地址:" + InetAddress.getLocalHost().getHostAddress());
            hintInfo.append("\n监听端口:9000");
            hintInfo.append("\n当前在线人数:" + userMap.size());
        } catch (UnknownHostException e)
        {
            e.printStackTrace();
        }
    }
}

3.1.3服务器线程类ChatRoomServerThread

package server;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Set;
import javax.swing.JTextArea;
import stream.ServerClientConnectionStream;
import user.UserInformation;

public class ChatRoomServerThread implements Runnable
{

    /**
     * @author CSJ
     */
    private ServerClientConnectionStream userCS;
    private UserInformation userInfo;
    private Set userSet;
    private Map userMap;
    private StringBuffer userList;
    private JTextArea hintInfo;
    private JTextArea startInfoTextArea;
    private JTextArea onlineUserInfo;

    public ChatRoomServerThread(ServerClientConnectionStream userCS, Set userSet,
            Map userMap, JTextArea hintInfo, JTextArea startInfoTextArea,
            JTextArea onlineUserInfo)
    {
        super();
        this.userCS = userCS;
        this.userSet = userSet;
        this.userMap = userMap;
        this.hintInfo = hintInfo;
        this.startInfoTextArea = startInfoTextArea;
        this.onlineUserInfo = onlineUserInfo;
        userList = new StringBuffer();
        userInfo = new UserInformation();
    }

    @Override
    public void run()
    {
        while (true)
        {
            String message = userCS.read();
            if (message.indexOf("%LOGIN%") == 0)
            {
                userInfo.setInfo(message);
                // 标志 IP 端口号 姓名 账号 密码
                if (userMap.containsKey(userInfo.getAccount()))
                {
                    userCS.send("LOGIN_FIAL");
                } else if (userInfo.getName().equals(""))
                {
                    userCS.send("NAME_IS_NULL");
                } else
                {
                    userMap.put(userInfo.getAccount(), userCS);
                    userSet.add(userInfo);
                    userCS.send("LOGIN_SUCESSFULLY");
                    startInfoTextArea.append("客户端账号:" + userInfo.getAccount() + "登陆服务器登陆成功!\n");
                    userList.append(userInfo.getAccount() + "&LOGIN:");
                    sendCurrentUserList();
                }
            } else if (message.indexOf("%EXIT%") == 0)
            {
                startInfoTextArea.append("服务器收到客户端账号:" + userInfo.getAccount() + "的退出请求...\n");
                userMap.remove(userInfo.getAccount());
                userSet.remove(userInfo);
                userList.append(userInfo.getAccount() + "&EXIT:");
                sendCurrentUserList();
                startInfoTextArea.append("已向客户端账号:" + userInfo.getAccount() + "发送了用户列表消息!\n");
                break;
            }
        }
        if (userCS != null)
        {
            userCS.close();
        }
    }

    public void sendCurrentUserList()
    {
        System.out.println("正在向客户端发送用户列表消息...");
        onlineUserInfo.setText("");
        for (UserInformation userInfo : userSet)
        {
            onlineUserInfo.append("账号:" + userInfo.getAccount() + ",密码:" + userInfo.getPassword() + ",昵称:"
                    + userInfo.getName() + ",IP地址:" + userInfo.getIP() + ",端口号(UDP):" + userInfo.getPort() + "\n");
            userList.append(userInfo.getIP() + "-" + userInfo.getPort() + "-" + userInfo.getAccount() + "-"
                    + userInfo.getName() + "-" + userInfo.getUserPortraitNum() + ",");
        }

        for (String string : userMap.keySet())
        {
            userMap.get(string).send("%USER_LIST%:" + userList);
            startInfoTextArea.append("服务器向" + string + "发送的用户列表为:%USER_LIST%:" + userList + "\n");
        }
        startInfoTextArea.append("已向客户端账号:" + userInfo.getAccount() + "发送了用户列表消息!\n");
        userList.delete(0, userList.length());
        printServerInfo();
        System.out.println("已更新了服务器状态信息!");
        startInfoTextArea.append("已更新了服务器状态信息!\n");
    }

    private void printServerInfo()
    {
        hintInfo.setText("当前状态:开始监听客户端连接...");
        try
        {
            hintInfo.append("\n服务器名称:" + InetAddress.getLocalHost().getHostName());
            hintInfo.append("\n服务器IP地址:" + InetAddress.getLocalHost().getHostAddress());
            hintInfo.append("\n监听端口:9000");
            hintInfo.append("\n当前在线人数:" + userMap.size());
        } catch (UnknownHostException e)
        {
            e.printStackTrace();
        }
    }
}

3.1.4服务器到客户端连接流类ServerClientConnectionStream

package stream;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ServerClientConnectionStream
{

    /**
     * @author CSJ
     */
    private Socket socket;
    private BufferedReader reader;
    private PrintWriter writer;

    public ServerClientConnectionStream(Socket socket)
    {
        super();
        this.socket = socket;
        getStream();
    }

    private void getStream()
    {
        try
        {
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            writer = new PrintWriter(socket.getOutputStream());

        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public void close()
    {
        if (socket != null)
        {
            try
            {
                socket.close();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    public void send(String message)
    {
        writer.println(message);
        writer.flush();
    }

    public String read()
    {
        try
        {
            return reader.readLine();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }
}

3.2客户端
3.2.1客户端框架类ChatRoomClientFrame

package client;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import readThread.ReadMessageFromClientThread;
import user.UserInformation;
import stream.ClientToClientConnectionStream;
import tools.MyMap;

public class ChatRoomClientFrame extends JFrame implements ActionListener
{

    /**
     * @author CSJ
     */
    private static final long serialVersionUID = 1L;
    private JTextArea chatTextArea;
    private JButton send;
    private JTextField inputField;
    private JLabel currentUserNameTitleLabel;
    private JTextArea userInfoListArea;
    private SimpleDateFormat simpleDateFormat;
    private UserInformation myUserInfo;
    private UserInformation toUserInfo;
    private JLabel userSignatureLable;
    private BufferedReader reader;
    private ClientToClientConnectionStream userDataCS;
    private ReadMessageFromClientThread readMessageFromClientThread;
    private Thread readMessageThread;
    private MyMap isOpenMap;
    private JPanel userInfoPanel;

    // 姓名 账户 IP
    public ChatRoomClientFrame(UserInformation toUserInfo, UserInformation myUserInfo, MyMap isOpenMap)
            throws HeadlessException
    {
        super("正在与" + toUserInfo.getName() + "(" + toUserInfo.getAccount() + ")" + "聊天中...");
        this.toUserInfo = toUserInfo;
        this.myUserInfo = myUserInfo;
        this.isOpenMap = isOpenMap;
        DatagramSocket dataSocket = null;
        try
        {
            dataSocket = new DatagramSocket();

        } catch (SocketException e)
        {
            e.printStackTrace();
        }
        try
        {
            userDataCS = new ClientToClientConnectionStream(dataSocket, InetAddress.getByName(toUserInfo.getIP()),
                    toUserInfo.getPort());
        } catch (UnknownHostException e)
        {
            e.printStackTrace();
        }
        userDataCS.send("%TEST%");
        simpleDateFormat = (SimpleDateFormat) DateFormat.getInstance();
        simpleDateFormat.applyPattern("yyyy年MM月dd日HH时mm分ss秒");
        createFrame();
        addEventHandler();
    }

    private void createFrame()
    {
        chatTextArea = new JTextArea(25, 60);
        chatTextArea.setEditable(false);
        userInfoListArea = new JTextArea();
        userInfoListArea.setEditable(false);
        JScrollPane centerScrollPane = new JScrollPane(chatTextArea);
        send = new JButton("发送");
        inputField = new JTextField(35);
        currentUserNameTitleLabel = new JLabel(toUserInfo.getName() + "(" + toUserInfo.getAccount() + ")");
        currentUserNameTitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 25));
        Random random = new Random();
        userInfoPanel = new JPanel(new GridLayout(2, 2, 5, 6));
        userInfoPanel.add(currentUserNameTitleLabel);
        try
        {
            reader = new BufferedReader(
                    new FileReader(new File(new File("").getAbsolutePath() + File.separator + "res" + File.separator
                            + "signatures" + File.separator + "signature-" + (random.nextInt(8) + 1) + ".txt")));
            userSignatureLable = new JLabel(reader.readLine());
            userInfoPanel.add(userSignatureLable);
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        } finally
        {
            try
            {
                reader.close();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        userInfoListArea.setText("IP地址:" + toUserInfo.getIP() + "\n端口号(UDP):" + toUserInfo.getPort() + "\n昵称:"
                + toUserInfo.getName() + "\n账户:" + toUserInfo.getAccount());
        ImageIcon portraItImage = new ImageIcon(
                getClass().getResource("\\pictures\\portrait-" + toUserInfo.getUserPortraitNum() + ".jpg"));

        portraItImage.setImage(portraItImage.getImage().getScaledInstance(80, 80, Image.SCALE_DEFAULT));
        JLabel portraItImageLable = new JLabel(portraItImage);
        JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
        northPanel.add(portraItImageLable, BorderLayout.NORTH);
        JPanel southPanel = new JPanel();
        northPanel.add(userInfoPanel);
        southPanel.add(inputField);
        southPanel.add(send);
        JScrollPane listScrollPane = new JScrollPane(userInfoListArea);
        JPanel eastPanel = new JPanel(new BorderLayout());
        eastPanel.add(new JLabel("对方信息列表:"), BorderLayout.NORTH);
        eastPanel.add(listScrollPane, BorderLayout.CENTER);
        add(eastPanel, BorderLayout.EAST);
        add(northPanel, BorderLayout.NORTH);
        add(centerScrollPane, BorderLayout.CENTER);
        add(southPanel, BorderLayout.SOUTH);
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        String message = inputField.getText();
        if (message == null || message.trim().equals(""))
        {
            JOptionPane.showMessageDialog(this, "不能发送空消息!");
        } else
        {
            String time = simpleDateFormat.format(new Date());
            if (userDataCS != null)
            {
                userDataCS.send(myUserInfo.getName() + "-" + myUserInfo.getAccount() + "-"
                        + myUserInfo.getUserPortraitNum() + "-" + myUserInfo.getRecenIP() + "-"
                        + myUserInfo.getRecentPort() + "-" + "(" + time + ")\n" + message);
                chatTextArea.append("我" + "(" + time + ")\n" + message + "\n");
                inputField.setText("");
            }
        }
    }

    private void addEventHandler()
    {
        inputField.addActionListener(this);
        send.addActionListener(this);
        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                int t = JOptionPane.showConfirmDialog(null, "确认要退出聊天室吗?", "确认退出", JOptionPane.OK_CANCEL_OPTION);
                if (t == JOptionPane.OK_OPTION)
                {
                    userDataCS.send("I_HAVE_EXIT_THE_WINDOW");
                    try
                    {
                        Thread.sleep(100);
                    } catch (InterruptedException e1)
                    {
                        e1.printStackTrace();
                    }
                    readMessageThread.interrupt();
                    readMessageFromClientThread.stopMe();
                    isOpenMap.replace(toUserInfo.getAccount(), false);

                    System.out.println("isOpenMap.replace(toUserInfo.getAccount()?" + toUserInfo.getAccount() + "--》"
                            + isOpenMap.getValue(toUserInfo.getAccount()));
                    System.out.println("窗口已经关闭!");
                    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
                }
            }
        });
    }

    public ChatRoomClientFrame showMe()
    {
        pack();
        setVisible(true);
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        readMessageFromClientThread = new ReadMessageFromClientThread(userDataCS, chatTextArea, toUserInfo);
        readMessageThread = new Thread(readMessageFromClientThread);
        readMessageThread.start();
        return this;
    }

    public JTextArea getChatTextArea()
    {
        return chatTextArea;
    }

    public void setChatTextArea(JTextArea chatTextArea)
    {
        this.chatTextArea = chatTextArea;
    }

    public ClientToClientConnectionStream getUserDataCS()
    {
        return userDataCS;
    }

    public void setUserDataCS(ClientToClientConnectionStream userDataCS)
    {
        this.userDataCS = userDataCS;
    }
}

3.2.2客户端列表框架类ChatRoomUserListFrame

package client;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import readThread.ReadMessageFromServerThread;
import stream.ClientToClientConnectionStream;
import stream.ServerClientConnectionStream;
import user.UserInformation;
import tools.MyMap;

public class ChatRoomUserListFrame extends JFrame
{

    /**
     * @author CSJ
     */
    private static final long serialVersionUID = 2L;
    private MyMap isOnlineMap;
    private MyMap isOpenMap;
    private ServerClientConnectionStream userCS;
    private JList currentOnlineUserList;
    private Map userInfoMap;
    private DatagramSocket dataSocket;
    private UserInformation userInfo;
    private ReadMessageFromServerThread readMessageFromServerThread;
    private Thread readMessagehread;
    private ChatRoomClientFrame chatRoomClientFrame;
    private Map chatRoomMap = new HashMap();

    public ChatRoomUserListFrame(ServerClientConnectionStream userCS, UserInformation userInfo) throws HeadlessException
    {
        super("MYQQ精简版V1.0");
        this.userCS = userCS;
        this.userInfo = userInfo;
        initData();
        createFrame();
        addEventHandler();
        autoListening();
    }

    private void initData()
    {
        isOnlineMap = new MyMap();
        isOpenMap = new MyMap();
        userInfoMap=new HashMap();
        try
        {
            dataSocket = new DatagramSocket(userInfo.getPort());
        } catch (SocketException e)
        {
            e.printStackTrace();
        }
    }

    private void createFrame()
    {
        currentOnlineUserList = new JList();
        currentOnlineUserList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        ImageIcon portraItImage = new ImageIcon(
                getClass().getResource("\\pictures\\portrait-" + userInfo.getUserPortraitNum() + ".jpg"));
        portraItImage.setImage(portraItImage.getImage().getScaledInstance(80, 80, Image.SCALE_DEFAULT));
        JLabel portraItImageLable = new JLabel(portraItImage);
        JScrollPane listScrollPane = new JScrollPane(currentOnlineUserList);
        JPanel centerPanel = new JPanel(new BorderLayout());
        JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
        northPanel.add(portraItImageLable, BorderLayout.NORTH);
        JPanel userInfoPanel = new JPanel(new GridLayout(2, 2, 5, 10));
        Calendar calendar = Calendar.getInstance();
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        JLabel userInfoLable = new JLabel(hour < 6 ? "凌晨好," + userInfo.getName()
                : (hour < 12 ? "上午好," : (hour < 18 ? "下午好," : "晚上好,")) + userInfo.getName());
        userInfoPanel.add(userInfoLable);
        SimpleDateFormat simpleDateFormat = (SimpleDateFormat) DateFormat.getInstance();
        simpleDateFormat.applyPattern("yyyy年MM月dd日    E");
        JLabel userTimeLable = new JLabel(simpleDateFormat.format(new Date()));
        userInfoPanel.add(userTimeLable);
        northPanel.add(userInfoPanel);
        centerPanel.add(new JLabel("在线用户列表:"), BorderLayout.CENTER);
        centerPanel.add(listScrollPane, BorderLayout.CENTER);
        add(northPanel, BorderLayout.NORTH);
        add(centerPanel, BorderLayout.CENTER);
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension screenSize = toolkit.getScreenSize();
        int width = getWidth();
        int screenWidth = screenSize.width / 2;
        setLocation(screenWidth - width / 2, 0);
        setResizable(false);
    }

    public void autoListening()
    {
        final ClientToClientConnectionStream userDataSoketCS = new ClientToClientConnectionStream(dataSocket);
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                while (true)
                {
                    String string = userDataSoketCS.read();
                    System.out.println("自动监听读取到:" + string + "端口号:" + userDataSoketCS.getUserReceivePacket().getPort());
                    if (!"%TEST%".equals(string) && !"I_HAVE_EXIT_THE_WINDOW".equals(string))
                    {
                        String tString[] = string.split("-");
                        if (isOpenMap.getValue(tString[1]) == true)
                        {
                            chatRoomMap.get(tString[1]).getUserDataCS()
                                    .setHostAddress(userDataSoketCS.getUserReceivePacket().getAddress());
                            chatRoomMap.get(tString[1]).getUserDataCS()
                                    .setPort(userDataSoketCS.getUserReceivePacket().getPort());
                            chatRoomMap.get(tString[1]).getUserDataCS().send("%TEST%");
                            chatRoomMap.get(tString[1]).getChatTextArea().append(string + "\n");

                        } else
                        {
                            int t = JOptionPane.showConfirmDialog(null, tString[0] + "给您发来一条消息,是否查收?", "确认",
                                    JOptionPane.YES_NO_OPTION);
                            if (t == JOptionPane.YES_OPTION)
                            {
                                UserInformation toUserInfo = new UserInformation(tString[0], tString[1],
                                        userDataSoketCS.getUserReceivePacket().getAddress().getHostAddress(),
                                        userDataSoketCS.getUserReceivePacket().getPort(), Integer.parseInt(tString[2]),
                                        tString[3], Integer.parseInt(tString[4]));
                                chatRoomClientFrame = new ChatRoomClientFrame(toUserInfo, userInfo, isOpenMap).showMe();
                                StringBuffer tMessage = new StringBuffer();
                                tMessage.append(tString[0]);
                                tMessage.append(tString[5]);
                                chatRoomClientFrame.getChatTextArea().append(tMessage + "\n");
                                isOpenMap.replace(tString[1], true);
                                chatRoomMap.put(tString[1], chatRoomClientFrame);
                            }
                        }
                    }

                }
            }

        }).start();
    }

    private void addEventHandler()
    {
        currentOnlineUserList.addListSelectionListener(new ListSelectionListener()
        {
            @Override
            public void valueChanged(ListSelectionEvent e)
            {
                if (!e.getValueIsAdjusting())
                {
                    String tString = (String) currentOnlineUserList.getSelectedValue();
                    if (tString != null)
                    {
                        String t[] = tString.split("(");
                        String[] string = t[1].split(")");
                        // 姓名-账号-IP-端口
                        if (string[0].equals(userInfo.getAccount()))
                        {
                            JOptionPane.showMessageDialog(null, "不能选择自己!");
                        } else
                        {
                            String tempUserInfo[]= userInfoMap.get(string[0]).split("-");
                            UserInformation toUserInfo = new UserInformation(tempUserInfo[3], tempUserInfo[2], tempUserInfo[0],
                                    Integer.parseInt(tempUserInfo[1]), Integer.parseInt(tempUserInfo[4]), tempUserInfo[0],
                                    Integer.parseInt(tempUserInfo[1]));
                            chatRoomClientFrame = new ChatRoomClientFrame(toUserInfo, userInfo, isOpenMap);
                            chatRoomClientFrame.showMe();
                            chatRoomMap.put(tempUserInfo[1], chatRoomClientFrame);
                            isOpenMap.replace(tempUserInfo[1], true);
                        }
                    }
                }

            }
        });

        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                int t = JOptionPane.showConfirmDialog(null, "确认要退出客户端吗?\n温馨提示:退出会同时关闭所有聊天窗口", "确认退出",
                        JOptionPane.OK_CANCEL_OPTION);
                if (t == JOptionPane.OK_OPTION)
                {
                    readMessagehread.interrupt();
                    readMessageFromServerThread.stopMe();
                    userCS.send("%EXIT%:" + userInfo.getAccount());
                    try
                    {
                        Thread.sleep(100);
                    } catch (InterruptedException e1)
                    {
                        e1.printStackTrace();
                    }
                    userCS.send("%EXIT%:" + userInfo.getAccount());
                    try
                    {
                        Thread.sleep(100);
                    } catch (InterruptedException e1)
                    {
                        e1.printStackTrace();
                    }
                    System.exit(0);
                }
            }
        });
    }

    public void showMe()
    {
        setSize(340, 700);
        setVisible(true);
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        readMessageFromServerThread = new ReadMessageFromServerThread(userCS,  userInfoMap, currentOnlineUserList,
                isOnlineMap, isOpenMap);
        readMessagehread = new Thread(readMessageFromServerThread);
        readMessagehread.start();
    }
}

3.2.3客户端登陆界面类LoginFrame

package client;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import stream.ServerClientConnectionStream;
import user.UserInformation;

public class LoginFrame extends JFrame
{

    /**
     * @author CSJ
     */
    private static final long serialVersionUID = 1L;
    private final int SERVER_PORT = 9000;
    private ServerClientConnectionStream userCS;
    private String userName;
    private int userPort;
    private JLabel register;
    private JLabel findPassword;
    private JCheckBox remPassword;
    private JCheckBox autoLogin;
    private String message;
    private JTextField accountField;
    private JPasswordField passwordField;
    private JPanel jPanel_1;
    private JPanel jPanel_2;
    private JPanel jPanel_3;
    private JButton login;
    private Socket socket;
    private InetAddress SERVER_INETADRESS;
    private InetAddress LOCAL_INETADRESS;
    private UserInformation userInfo;

    public LoginFrame() throws HeadlessException
    {
        initData();
        createFrame();
        addHandlerEvent();
    }

    @SuppressWarnings("resource")
    private void initData()
    {
        userInfo = new UserInformation();
        try
        {
            DatagramSocket d = new DatagramSocket();
            userPort = d.getLocalPort();
            d.close();
            SERVER_INETADRESS = InetAddress.getLocalHost();
            LOCAL_INETADRESS = InetAddress.getLocalHost();
            socket = new Socket(SERVER_INETADRESS, SERVER_PORT, LOCAL_INETADRESS, new DatagramSocket().getLocalPort());
            userCS = new ServerClientConnectionStream(socket);
        } catch (UnknownHostException e)
        {
            System.out.println("找不到 host 的任何 IP 地址!");
            e.printStackTrace();
        } catch (IOException e)
        {
            System.out.println("创建套接字时发生 I/O 错误!");
            e.printStackTrace();
            System.exit(1);
        }
    }

    private void createFrame()
    {
        setTitle("登陆");
        Random random = new Random();
        ImageIcon image1 = new ImageIcon(getClass().getResource("\\pictures\\qq.png"));
        JLabel imageLable = new JLabel(image1);
        int num = (random.nextInt(38) + 1);
        userInfo.setUserPortraitNum(num);
        ImageIcon portraItImage = new ImageIcon(getClass().getResource("\\pictures\\portrait-" + num + ".jpg"));
        portraItImage.setImage(portraItImage.getImage().getScaledInstance(80, 80, Image.SCALE_DEFAULT));
        JLabel portraItImageLable = new JLabel(portraItImage);
        JPanel mainLayout = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
        accountField = new JTextField(15);
        login = new JButton("安全登录");
        login.setBackground(new Color(30, 144, 255));
        login.setForeground(Color.white);
        login.setPreferredSize(new Dimension(170, 30));
        passwordField = new JPasswordField(15);
        JPanel jPanel_0 = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        JPanel accRes = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
        JPanel passRes = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
        JPanel accPassHintPanel = new JPanel(new GridLayout(3, 15, 0, 0));
        register = new JLabel("注册账号");
        register.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        register.setForeground(Color.BLUE);
        findPassword = new JLabel("找回密码");
        findPassword.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        findPassword.setForeground(Color.BLUE);
        remPassword = new JCheckBox("记住密码");
        autoLogin = new JCheckBox("自动登录");
        accRes.add(accountField);
        accRes.add(register);
        passRes.add(passwordField);
        passRes.add(findPassword);
        accPassHintPanel.add(accRes);
        accPassHintPanel.add(passRes);
        jPanel_1 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 10));
        jPanel_2 = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 1));
        jPanel_3 = new JPanel();
        jPanel_0.add(imageLable);
        jPanel_3.add(login);
        jPanel_2.add(remPassword);
        jPanel_2.add(autoLogin);
        accPassHintPanel.add(jPanel_2);
        mainLayout.add(jPanel_0);
        jPanel_1.add(portraItImageLable);
        jPanel_1.add(accPassHintPanel);
        mainLayout.add(jPanel_1);
        mainLayout.add(jPanel_3);
        add(mainLayout);
        setVisible(true);
        setSize(445, 350);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension screenSize = toolkit.getScreenSize();
        int height = getHeight();
        int width = getWidth();
        int screenWidth = screenSize.width / 2;
        int screenHeight = screenSize.height / 2;
        setLocation(screenWidth - width / 2, screenHeight - height / 2);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    }

    private void addHandlerEvent()
    {
        login.addActionListener(new ActionListener()
        {
            // 标志 IP 端口号 姓名 账号 密码
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                System.out.println("已开始向服务器发送登录数据...");
                try
                {
                    userCS.send("%LOGIN%:" + InetAddress.getLocalHost().getHostAddress() + ":" + userPort + ":" + ""
                            + ":" + accountField.getText() + ":" + String.valueOf(passwordField.getPassword()).trim()
                            + ":" + userInfo.getUserPortraitNum());
                } catch (UnknownHostException e)
                {
                    e.printStackTrace();
                }

                System.out.println("等待服务器返回登录结果...");
                new Thread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        while (true)
                        {
                            message = userCS.read();
                            if ("LOGIN_FIAL".equals(message))
                            {
                                JOptionPane.showMessageDialog(null, "已有相同的账号,请重新输入");
                                accountField.setText("");
                                passwordField.setText("");
                            } else if ("NAME_IS_NULL".equals(message))
                            {
                                // setVisible(false);
                                dispose();
                                userName = JOptionPane.showInputDialog(null, "来给自己起个名字吧");
                                try
                                {
                                    // 标志 IP 端口号 姓名 账号 密码
                                    userCS.send("%LOGIN%:" + InetAddress.getLocalHost().getHostAddress() + ":"
                                            + userPort + ":" + userName + ":" + accountField.getText() + ":"
                                            + String.valueOf(passwordField.getPassword()).trim() + ":"
                                            + userInfo.getUserPortraitNum());
                                    userInfo.setIP(InetAddress.getLocalHost().getHostAddress());
                                    userInfo.setRecenIP(InetAddress.getLocalHost().getHostAddress());
                                    userInfo.setRecentPort(userPort);
                                    System.out.println("已向服务器重新发送了信息...");
                                } catch (UnknownHostException e)
                                {
                                    e.printStackTrace();
                                }
                            } else if ("LOGIN_SUCESSFULLY".equals(message))
                            {
                                setDefaultCloseOperation(DISPOSE_ON_CLOSE);
                                userInfo.setAccount(accountField.getText());
                                userInfo.setName(userName);
                                userInfo.setPort(userPort);
                                LoginProcess loginProcess = new LoginProcess(userInfo.getUserPortraitNum());
                                try
                                {
                                    Thread.sleep(2000);
                                } catch (InterruptedException e)
                                {
                                    e.printStackTrace();
                                }
                                loginProcess.dispose();
                                new ChatRoomUserListFrame(userCS, userInfo).showMe();
                                JOptionPane.showMessageDialog(null, "登录成功!");
                            }
                        }
                    }
                }).start();
            }
        });

        register.addMouseListener(new MouseListener()
        {

            @Override
            public void mouseClicked(MouseEvent e)
            {
                JOptionPane.showMessageDialog(null, "本功能暂未开放!");
            }

            @Override
            public void mouseEntered(MouseEvent e)
            {
            }

            @Override
            public void mouseExited(MouseEvent e)
            {
            }

            @Override
            public void mousePressed(MouseEvent e)
            {
            }

            @Override
            public void mouseReleased(MouseEvent e)
            {
            }
        });
        findPassword.addMouseListener(new MouseListener()
        {

            @Override
            public void mouseReleased(MouseEvent e)
            {

            }

            @Override
            public void mousePressed(MouseEvent e)
            {

            }

            @Override
            public void mouseExited(MouseEvent e)
            {

            }

            @Override
            public void mouseEntered(MouseEvent e)
            {

            }

            @Override
            public void mouseClicked(MouseEvent e)
            {
                JOptionPane.showMessageDialog(null, "本功能暂未开放!");

            }
        });
        remPassword.addActionListener(new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                JOptionPane.showMessageDialog(null, "本功能暂未开放!");

            }
        });
        autoLogin.addActionListener(new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                JOptionPane.showMessageDialog(null, "本功能暂未开放!");
            }
        });

        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }
}

3.2.4客户端登陆主类LoginMain

package client;

public class LoginMain
{

    /**
     * @author CSJ
     */
    public static void main(String[] args)
    {

        new LoginFrame();
    }
}

3.2.5客户端登陆过程类LoginProcess

package client;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class LoginProcess extends JFrame
{

    /**
     * @author CSJ
     */
    private static final long serialVersionUID = 1L;

    public LoginProcess(int number)
    {
        super();
        setTitle("登陆");
        ImageIcon image1 = new ImageIcon(getClass().getResource("\\pictures\\qq.png"));
        JLabel imageLable = new JLabel(image1);
        ImageIcon portraItImage = new ImageIcon(getClass().getResource("\\pictures\\portrait-" + number + ".jpg"));
        portraItImage.setImage(portraItImage.getImage().getScaledInstance(80, 80, Image.SCALE_DEFAULT));
        JLabel portraItImageLable = new JLabel(portraItImage);
        JPanel mainLayout = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
        JPanel jPanel_0 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
        JPanel jPanel_1 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 10));
        JPanel jPanel_2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
        JButton login = new JButton("正在登录...");
        login.setPreferredSize(new Dimension(170, 30));
        login.setForeground(Color.white);
        login.setBackground(new Color(30, 144, 255));
        jPanel_2.add(login);
        jPanel_0.add(imageLable);
        mainLayout.add(jPanel_0);
        jPanel_1.add(portraItImageLable);
        JPanel jPanel_3 = new JPanel(new GridLayout(2, 1, 5, 5));
        jPanel_3.add(jPanel_1);
        jPanel_3.add(jPanel_2);
        mainLayout.add(jPanel_3);
        add(mainLayout);
        setSize(445, 350);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension screenSize = toolkit.getScreenSize();
        int height = getHeight();
        int width = getWidth();
        int screenWidth = screenSize.width / 2;
        int screenHeight = screenSize.height / 2;
        setLocation(screenWidth - width / 2, screenHeight - height / 2);
        setVisible(true);
    }
}

3.2.6客户端读取客户端消息线程类ReadMessageFromClientThread

package readThread;

import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.swing.JTextArea;
import stream.ClientToClientConnectionStream;
import user.UserInformation;

public class ReadMessageFromClientThread implements Runnable
{

    /**
     * @author CSJ
     */
    private ClientToClientConnectionStream userDataCS;
    private JTextArea chatTextArea;
    private UserInformation toUserInfo;
    private volatile boolean stop = false;

    public ReadMessageFromClientThread(ClientToClientConnectionStream userDataCS, JTextArea chatTextArea,
            UserInformation toUserInfo)
    {
        super();
        this.userDataCS = userDataCS;
        this.chatTextArea = chatTextArea;
        this.toUserInfo = toUserInfo;
    }

    @Override
    public void run()
    {
        while (!stop)
        {
            synchronized (this)
            {
                System.out.println("客户端--客户端读取消息线程已启动!");
                if (userDataCS != null)
                {
                    String message = userDataCS.read();
                    System.out.println(message);
                    if ("%TEST%".equals(message))
                    {
                        System.out.println("客户端中原IP:" + userDataCS.getHostAddress() + "原端口:" + userDataCS.getPort());
                        userDataCS.setHostAddress(userDataCS.getUserReceivePacket().getAddress());
                        userDataCS.setPort(userDataCS.getUserReceivePacket().getPort());
                        System.out.println("客户端中现IP:" + userDataCS.getHostAddress() + "现在端口:" + userDataCS.getPort());
                    } else if ("I_HAVE_EXIT_THE_WINDOW".equals(message))
                    {
                        try
                        {
                            userDataCS.setHostAddress(InetAddress.getByName(toUserInfo.getRecenIP()));
                            userDataCS.setPort(toUserInfo.getRecentPort());
                        } catch (UnknownHostException e)
                        {
                            e.printStackTrace();
                        }
                    } else
                    {
                        String tString[] = message.split("-");
                        System.out.println("客户端--客户端(IP:" + userDataCS.getHostAddress() + "端口(UDP):"
                                + userDataCS.getPort() + ")读取到消息:" + message);
                        StringBuffer tMessage = new StringBuffer();
                        tMessage.append(tString[0]);
                        tMessage.append(tString[5]);
                        chatTextArea.append(tMessage + "\n");
                    }
                }
            }
        }
    }

    public void stopMe()
    {
        stop = true;
    }
}

3.2.7客户端读取服务器消息线程类ReadMessageFromServerThread

package readThread;

import java.util.Map;

import javax.swing.JList;
import javax.swing.JOptionPane;

import stream.ServerClientConnectionStream;
import tools.MyMap;

public class ReadMessageFromServerThread implements Runnable {

    /**
     * @author CSJ
     */
    private ServerClientConnectionStream userCS;
    private String onlineUserList[];
    private MyMap isOnlineMap;
    private JList currentOnlineUserList;
    private volatile boolean stop = false;
    private MyMap isOpenMap;
    private Map userInfoMap;
    private String loginName;

    public ReadMessageFromServerThread(ServerClientConnectionStream userCS,
            Map userInfoMap, JList currentOnlineUserList,
            MyMap isOnlineMap, MyMap isOpenMap) {
        super();
        this.userCS = userCS;
        this.userInfoMap=userInfoMap;
        this.isOnlineMap = isOnlineMap;
        this.isOpenMap = isOpenMap;
        this.currentOnlineUserList = currentOnlineUserList;
    }

    public void refreshGUIUserList() {
        StringBuffer t = new StringBuffer();
        System.out.println("在服务器--客户端线程中刷新用户界面!");
        for (String string : onlineUserList)
        {
            // 姓名 账号 IP-端口 头像序号--
            String per[] = string.split("-");
            if (!per[2].equals(loginName))
            {
                System.out.println(per[2]);
                isOnlineMap.put(per[2], false);
                isOpenMap.put(per[2], false);
            }
            t.append(per[3] + "(" + per[2]+ ")"+":");
            userInfoMap.put(per[2], string);
        }
        String tt[] = t.toString().split(":");
        currentOnlineUserList.setListData(tt);
    }

    @Override
    public void run()
    {
        while (!stop)
        {

                System.out.println("服务器--客户端读取消息线程已启动!");
                String message = userCS.read();
                if (message != null)
                {
                    System.out.println("服务器--客户端读取消息线程已启动!" + message);
                    String perMessage[] = message.split(":");
                    loginName = perMessage[1];
                    if ("%USER_LIST%".equals(perMessage[0]))
                    {
                        onlineUserList = perMessage[2].split(",");
                        if ("LOGIN".equals(perMessage[1].split("&")[1]))
                        {
                            isOnlineMap.put(perMessage[1].split("&")[0], true);
                            isOpenMap.put(perMessage[1].split("&")[0], false);
                        } else if ("EXIT".equals(perMessage[1].split("&")[1]))
                        {
                            if (isOpenMap.getValue(perMessage[1].split("&")[0]) == true)
                            {
                                JOptionPane.showMessageDialog(null, "对方已离线!");
                            }
                            isOnlineMap.replace(perMessage[1].split("&")[0], false);
                            isOpenMap.replace(perMessage[1].split("&")[0], false);
                        }
                        refreshGUIUserList();
                    }
                }

        }
    }

    public void stopMe() {
        stop = true;
    }
}

3.2.8客户端到客户端连接流类ClientToClientConnectionStream

package stream;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

public class ClientToClientConnectionStream
{

    /**
     * @author CSJ
     */
    private DatagramPacket userReceivePacket;
    private DatagramSocket userDataSocket;
    private byte buffer[];
    private InetAddress hostAddress;
    private int port;

    public ClientToClientConnectionStream(DatagramSocket userDataSocket)
    {
        super();
        this.userDataSocket = userDataSocket;
        buffer = new byte[1024];
        userReceivePacket = new DatagramPacket(buffer, buffer.length);
    }

    public ClientToClientConnectionStream(DatagramSocket userDataSocket, InetAddress hostAddress, int port)
    {
        super();
        this.hostAddress = hostAddress;
        this.port = port;
        this.userDataSocket = userDataSocket;
        buffer = new byte[1024];
        userReceivePacket = new DatagramPacket(buffer, buffer.length);
    }

    public void send(String message)
    {
        try
        {
            userDataSocket.send(new DatagramPacket(message.getBytes(), message.getBytes().length, hostAddress, port));
        } catch (IOException e)
        {
            System.out.println("UDP端口被占用!");
            e.printStackTrace();
        }
    }

    public String read()
    {
        try
        {
            if (!userDataSocket.isClosed())
            {
                userDataSocket.receive(userReceivePacket);
                System.out.print("liu现在端口号:" + userReceivePacket.getPort());
                String string = new String(userReceivePacket.getData(), 0, userReceivePacket.getLength(), "GBK");
                return string;
            }
        } catch (UnsupportedEncodingException e)
        {
            System.out.println("不支持的编码类型!");
            e.printStackTrace();
        } catch (IOException e)
        {
            System.out.println(userDataSocket == null);
            e.printStackTrace();
        }
        return null;
    }

    public void close()
    {
        if (userDataSocket != null)
        {
            userDataSocket.close();
        }
    }

    public void setPort(int port)
    {
        this.port = port;
    }

    public String getHostAddress()
    {
        return hostAddress.getHostAddress();
    }

    public void setHostAddress(InetAddress hostAddress)
    {
        this.hostAddress = hostAddress;
    }

    public int getPort()
    {
        return port;
    }

    public DatagramPacket getUserReceivePacket()
    {
        return userReceivePacket;
    }

    public InetAddress getInetAddress()
    {
        return userReceivePacket.getAddress();
    }

    public DatagramSocket getDatagramSocket()
    {
        return userDataSocket;
    }

}

3.2.9客户端自定义的工具类MyMap

package tools;

import java.util.HashMap;
import java.util.Map;
import java.util.Observable;

public class MyMap extends Observable
{

    /**
     * @author CSJ
     */
    private Map myMap;

    public MyMap()
    {
        super();
        myMap = new HashMap();
    }

    public MyMap(String key, boolean value)
    {
        super();
        myMap = new HashMap();
        myMap.put(key, value);  
    }

    public void put(String key, boolean value)
    {
        myMap.put(key, value);
        setChanged();
        notifyObservers();
    }

    public void replace(String key, boolean value)
    {
        myMap.remove(key);
        myMap.put(key, value);
        setChanged();
        notifyObservers();
    }

    public Map getIsOnlineMap()
    {
        return myMap;
    }

    public void setIsOnlineMap(Map isOnlineMap)
    {
        this.myMap = isOnlineMap;
    }

    public boolean getValue(String key)
    {
        return myMap.get(key);
    }

    public void remove(String key)
    {
        myMap.remove(key);
    }

    public boolean containsValue(String key)
    {
        return myMap.containsValue(key);
    }
}

3.2.10客户端用户信息类UserInformation

package user;

public class UserInformation
{

    /**
     * @author CSJ
     */
    private String name;
    private String account;
    private String password;
    private String IP;
    private int port;
    private int recentPort;
    private String recenIP;
    private int userPortraitNum;

    public UserInformation()
    {
        super();
    }

    public UserInformation(String name, String account, String password, String IP, int port, int userPortraitNum)
    {
        super();
        this.name = name;
        this.account = account;
        this.password = password;
        this.IP = IP;
        this.port = port;
        this.userPortraitNum = userPortraitNum;
    }

    public UserInformation(String name, String account, String IP, int port, int userPortraitNum, String recentIP,
            int recentPort)
    {
        super();
        this.name = name;
        this.account = account;
        this.IP = IP;
        this.port = port;
        this.recenIP = recentIP;
        this.recentPort = recentPort;
        this.userPortraitNum = userPortraitNum;
    }

    public int getRecentPort()
    {
        return recentPort;
    }

    public void setRecentPort(int recentPort)
    {
        this.recentPort = recentPort;
    }

    public String getRecenIP()
    {
        return recenIP;
    }

    public void setRecenIP(String recenIP)
    {
        this.recenIP = recenIP;
    }

    public UserInformation(String name, int port)
    {
        super();
        this.name = name;
        this.port = port;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getAccount()
    {
        return account;
    }

    public void setAccount(String acocount)
    {
        this.account = acocount;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

    public String getIP()
    {
        return IP;
    }

    public void setIP(String iP)
    {
        IP = iP;
    }

    public int getPort()
    {
        return port;
    }

    public void setPort(int port)
    {
        this.port = port;
    }

    public int getUserPortraitNum()
    {
        return userPortraitNum;
    }

    public void setUserPortraitNum(int userPortraitNum)
    {
        this.userPortraitNum = userPortraitNum;
    }

    public UserInformation setInfo(String message)
    {
        // 标志 IP 端口号 姓名 账号 密码
        this.IP = message.split(":")[1];
        this.port = Integer.parseInt(message.split(":")[2]);
        this.name = message.split(":")[3];
        this.account = message.split(":")[4];
        this.password = message.split(":")[5];
        this.userPortraitNum = Integer.parseInt(message.split(":")[6]);
        return this;
    }
}

5测试
初学java之模拟QQ聊天软件(简单实现)_第2张图片
图5-1 服务器界面
初学java之模拟QQ聊天软件(简单实现)_第3张图片
图5-2服务器启动界面
初学java之模拟QQ聊天软件(简单实现)_第4张图片
图5-3用户登录界面(用户:巴黎夜雨)
初学java之模拟QQ聊天软件(简单实现)_第5张图片
图5-4 创建昵称界面(用户:巴黎夜雨)
初学java之模拟QQ聊天软件(简单实现)_第6张图片
图5-5 登录过程界面(用户:巴黎夜雨)
初学java之模拟QQ聊天软件(简单实现)_第7张图片
图5-6 登录成功界面(用户:巴黎夜雨)
初学java之模拟QQ聊天软件(简单实现)_第8张图片
图5-7用户登录界面(用户:甜心百分百)
初学java之模拟QQ聊天软件(简单实现)_第9张图片
图5-8 创建昵称界面(用户:甜心百分百)
初学java之模拟QQ聊天软件(简单实现)_第10张图片
图5-9 登录过程界面(用户:甜心百分百)
初学java之模拟QQ聊天软件(简单实现)_第11张图片
图5-10 登录成功界面(用户:甜心百分百)
初学java之模拟QQ聊天软件(简单实现)_第12张图片
图5-11 用户登录界面(用户:执手不忆殇)
初学java之模拟QQ聊天软件(简单实现)_第13张图片
图5-12 创建昵称界面(用户:执手不忆殇)
初学java之模拟QQ聊天软件(简单实现)_第14张图片
图5-13 登录过程界面(用户:执手不忆殇)
初学java之模拟QQ聊天软件(简单实现)_第15张图片
图5-14 登录成功界面(用户:执手不忆殇)
初学java之模拟QQ聊天软件(简单实现)_第16张图片
图5-15当前的三个用户
初学java之模拟QQ聊天软件(简单实现)_第17张图片
图5-16 执手不忆殇找巴黎夜雨聊天
初学java之模拟QQ聊天软件(简单实现)_第18张图片
图5-17 巴黎夜雨接弹出消息提示
初学java之模拟QQ聊天软件(简单实现)_第19张图片
图5-18 巴黎夜雨选择是,弹出聊天窗口
初学java之模拟QQ聊天软件(简单实现)_第20张图片
图5-19 聊天截图
初学java之模拟QQ聊天软件(简单实现)_第21张图片
图5-20 执手不忆殇关闭了与巴黎夜雨的聊天窗口,巴黎夜雨弹出提示消息
初学java之模拟QQ聊天软件(简单实现)_第22张图片
图5-21巴黎夜雨关闭了聊天窗口,又选择和甜心百分百聊天
初学java之模拟QQ聊天软件(简单实现)_第23张图片
图5-22 甜心百分百弹出消息提示后,选择是,两人开始聊天
初学java之模拟QQ聊天软件(简单实现)_第24张图片
图5-23 聊天截图
初学java之模拟QQ聊天软件(简单实现)_第25张图片
图5-24三人下线后,服务器的运行情况
初学java之模拟QQ聊天软件(简单实现)_第26张图片
初学java之模拟QQ聊天软件(简单实现)_第27张图片
图5-25 服务器运行详情截图

6结束语
本课程设计采用Java编程语言实现,在Eclipse Java Neon上编译通过。通过本次课程设计,我对Java语言有了更深入的理解,对后续课程的学习及实际工作中一定会带来很大的帮助。同时,本程序还存在一定的不足之处,如果可能,将在后续版本中不断完善。

参考文献
 赵新慧 等. Java程序设计教程,清华大学出版社,2014
 网络编程相关资料
电子文档:
第11章 网络编程.ppt
JDK_API_1_6_zh_CN.CHM

你可能感兴趣的:(java,聊天,大二上,java)