设计一个聊天服务器
设计一个聊天服务器
一贱书生 发表于1年前
设计一个聊天服务器
  • 发表于 1年前
  • 阅读 27
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

系统基本功能描述

 

客户端可以实现注册,即时聊天,相互之间收发文件,发送截图,查看历史聊天记录

等功能。收发消息时,可以实现离线接收。

 

服务器端应当建立一个

ServerSocket

并且不断进行侦听是否有客户端连接或者断开

连接

(

包括判断没有响应的连接超时

)

。服务器端应当是一个信息发送中心,所有客户端的

信息都传到服务器端,由服务器端根据要求分发信息。

 

在后台管理系统,可以到对用户进行增删改查,查看在线用户,和踢用户下线

本系统要实现的功能如下:

 

1)

注册

 

服务器收到用户的注册请求,便开始接受客户传递的信息,诸如客户的呢称,性别,

籍贯,个人资料等,接受完毕后,便通过

JDBC

与后台数据库连接,然后向数据库添加记

录,如果成功,便向客户返回其号码。客户收到服务器返回的信息后,便打开窗口登陆。

 

 

2)

登陆

 

在客户端,

用户输入其号码和密码,

然后建立与服务器的连接,

告诉服务器我要登录,

服务器收到后,

开始通过

JDBC

读取数据库,

然后与用户输入的信息进行比对,

如果成功,

便打开主程序窗口。然后客户向服务器请求读取好友名单,服务器收到该请求,从数据库

中取出好友列表,然后向客户端发送这些信息,客户收到后就在主窗口显示好友。

 

3)

聊天

 

客户端首先发送消息到服务器端,服务器端根据发送人信息和接收者信息来转发。例

如服务器接收到一个

A

发给

B

的消息,先判断

B

是否已经在线,如果在线就将信息发送

过去。

如果

B

不在线则把信息储存在服务器,

B

上线了在发送给他。

B

接收到

A

发来的

信息,如果是未建立对话窗口,则自动打开显示。

 

 

4)

发送截图

 

客户端

A

点击截图按钮,将屏幕锁定,截取图片,将图片储存在

byte[]

数组中发送到

服务器端。服务器端根据发送人信息和接收者信息来转发。

B

接收到

A

发来的截图信息,

会根据

byte[]

构造图片,将图片显示出来。同样,如果是未建立对话窗口,则自动打开显

示。

 

5)

发送文件

 

客户端

A

点击发送文件按键,从本地选择文件,将文件在

byte[]

数组中分批次发送到

服务器端。服务器端根据发送人信息和接收者信息来转发。

B

接收到

A

发来的文件信息,

会根据文件发送的状态和

byte[]

构造文件。

6)

日志

 

客户端发送和接收到的信息都会储存在文本文档中。默认储存在

C

盘下面,会以用户

的登录账号为名。

 

 

7)

对用户的操作

 

 

管理员通过

B/S

的系统对用户进行增加,

修改,

删除,

查找等操作,

对系统进行维护。

 

 

8)

在线用户的操作

 

 

管理员通过

B/S

的系统对在线用户操作,可以踢用户下线。

 

 

界面是用swing写的,还不懒,简约大方。有图有真相:

 

 

 

正如上图所示,这个程序分为 服务器端和客户端,说白了就是 两个main class,用eclipse直接运行之。。。。。

 

           聊天室的设计思想是 :在局域网下, 利用socket进行连接通信,当服务器端启动的时候,利用Thread线程不停的等待客户端的链接;当有客户端开启连

接的时候,服务器端通过IO流反馈“上线用户”信息给客户端,客户端也使用线程不停的接收服务器的信息,从而实现多人在线聊天功能。

 

程序中有三个类,分别 Server.Java(服务器端)、Client(客户端)、User.java(javabean)。代码如下:

 

Server.java(服务器端):

[html] view plain copy

 

  1. import java.awt.BorderLayout;  
  2. import java.awt.Color;  
  3. import java.awt.GridLayout;  
  4. import java.awt.Toolkit;  
  5. import java.awt.event.ActionEvent;  
  6. import java.awt.event.ActionListener;  
  7. import java.awt.event.WindowAdapter;  
  8. import java.awt.event.WindowEvent;  
  9. import java.io.BufferedReader;  
  10. import java.io.IOException;  
  11. import java.io.InputStreamReader;  
  12. import java.io.PrintWriter;  
  13. import java.net.BindException;  
  14. import java.net.ServerSocket;  
  15. import java.net.Socket;  
  16. import java.util.ArrayList;  
  17. import java.util.StringTokenizer;  
  18.   
  19. import javax.swing.DefaultListModel;  
  20. import javax.swing.JButton;  
  21. import javax.swing.JFrame;  
  22. import javax.swing.JLabel;  
  23. import javax.swing.JList;  
  24. import javax.swing.JOptionPane;  
  25. import javax.swing.JPanel;  
  26. import javax.swing.JScrollPane;  
  27. import javax.swing.JSplitPane;  
  28. import javax.swing.JTextArea;  
  29. import javax.swing.JTextField;  
  30. import javax.swing.border.TitledBorder;  
  31.   
  32. public class Server {  
  33.   
  34.     private JFrame frame;  
  35.     private JTextArea contentArea;  
  36.     private JTextField txt_message;  
  37.     private JTextField txt_max;  
  38.     private JTextField txt_port;  
  39.     private JButton btn_start;  
  40.     private JButton btn_stop;  
  41.     private JButton btn_send;  
  42.     private JPanel northPanel;  
  43.     private JPanel southPanel;  
  44.     private JScrollPane rightPanel;  
  45.     private JScrollPane leftPanel;  
  46.     private JSplitPane centerSplit;  
  47.     private JList userList;  
  48.     private DefaultListModel listModel;  
  49.   
  50.     private ServerSocket serverSocket;  
  51.     private ServerThread serverThread;  
  52.     private ArrayList<ClientThread> clients;  
  53.   
  54.     private boolean isStart = false;  
  55.   
  56.     // 主方法,程序执行入口  
  57.     public static void main(String[] args) {  
  58.         new Server();  
  59.     }  
  60.   
  61.     // 执行消息发送  
  62.     public void send() {  
  63.         if (!isStart) {  
  64.             JOptionPane.showMessageDialog(frame, "服务器还未启动,不能发送消息!", "错误",  
  65.                     JOptionPane.ERROR_MESSAGE);  
  66.             return;  
  67.         }  
  68.         if (clients.size() == 0) {  
  69.             JOptionPane.showMessageDialog(frame, "没有用户在线,不能发送消息!", "错误",  
  70.                     JOptionPane.ERROR_MESSAGE);  
  71.             return;  
  72.         }  
  73.         String message = txt_message.getText().trim();  
  74.         if (message == null || message.equals("")) {  
  75.             JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误",  
  76.                     JOptionPane.ERROR_MESSAGE);  
  77.             return;  
  78.         }  
  79.         sendServerMessage(message);// 群发服务器消息  
  80.         contentArea.append("服务器说:" + txt_message.getText() + "\r\n");  
  81.         txt_message.setText(null);  
  82.     }  
  83.   
  84.     // 构造放法  
  85.     public Server() {  
  86.         frame = new JFrame("服务器");  
  87.         // 更改JFrame的图标:  
  88.         //frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png")));  
  89.         frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Server.class.getResource("qq.png")));  
  90.         contentArea = new JTextArea();  
  91.         contentArea.setEditable(false);  
  92.         contentArea.setForeground(Color.blue);  
  93.         txt_message = new JTextField();  
  94.         txt_max = new JTextField("30");  
  95.         txt_port = new JTextField("6666");  
  96.         btn_start = new JButton("启动");  
  97.         btn_stop = new JButton("停止");  
  98.         btn_send = new JButton("发送");  
  99.         btn_stop.setEnabled(false);  
  100.         listModel = new DefaultListModel();  
  101.         userList = new JList(listModel);  
  102.   
  103.         southPanel = new JPanel(new BorderLayout());  
  104.         southPanel.setBorder(new TitledBorder("写消息"));  
  105.         southPanel.add(txt_message, "Center");  
  106.         southPanel.add(btn_send, "East");  
  107.         leftPanel = new JScrollPane(userList);  
  108.         leftPanel.setBorder(new TitledBorder("在线用户"));  
  109.   
  110.         rightPanel = new JScrollPane(contentArea);  
  111.         rightPanel.setBorder(new TitledBorder("消息显示区"));  
  112.   
  113.         centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel,  
  114.                 rightPanel);  
  115.         centerSplit.setDividerLocation(100);  
  116.         northPanel = new JPanel();  
  117.         northPanel.setLayout(new GridLayout(1, 6));  
  118.         northPanel.add(new JLabel("人数上限"));  
  119.         northPanel.add(txt_max);  
  120.         northPanel.add(new JLabel("端口"));  
  121.         northPanel.add(txt_port);  
  122.         northPanel.add(btn_start);  
  123.         northPanel.add(btn_stop);  
  124.         northPanel.setBorder(new TitledBorder("配置信息"));  
  125.   
  126.         frame.setLayout(new BorderLayout());  
  127.         frame.add(northPanel, "North");  
  128.         frame.add(centerSplit, "Center");  
  129.         frame.add(southPanel, "South");  
  130.         frame.setSize(600, 400);  
  131.         //frame.setSize(Toolkit.getDefaultToolkit().getScreenSize());//设置全屏  
  132.         int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;  
  133.         int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;  
  134.         frame.setLocation((screen_width - frame.getWidth()) / 2,  
  135.                 (screen_height - frame.getHeight()) / 2);  
  136.         frame.setVisible(true);  
  137.   
  138.         // 关闭窗口时事件  
  139.         frame.addWindowListener(new WindowAdapter() {  
  140.             public void windowClosing(WindowEvent e) {  
  141.                 if (isStart) {  
  142.                     closeServer();// 关闭服务器  
  143.                 }  
  144.                 System.exit(0);// 退出程序  
  145.             }  
  146.         });  
  147.   
  148.         // 文本框按回车键时事件  
  149.         txt_message.addActionListener(new ActionListener() {  
  150.             public void actionPerformed(ActionEvent e) {  
  151.                 send();  
  152.             }  
  153.         });  
  154.   
  155.         // 单击发送按钮时事件  
  156.         btn_send.addActionListener(new ActionListener() {  
  157.             public void actionPerformed(ActionEvent arg0) {  
  158.                 send();  
  159.             }  
  160.         });  
  161.   
  162.         // 单击启动服务器按钮时事件  
  163.         btn_start.addActionListener(new ActionListener() {  
  164.             public void actionPerformed(ActionEvent e) {  
  165.                 if (isStart) {  
  166.                     JOptionPane.showMessageDialog(frame, "服务器已处于启动状态,不要重复启动!",  
  167.                             "错误", JOptionPane.ERROR_MESSAGE);  
  168.                     return;  
  169.                 }  
  170.                 int max;  
  171.                 int port;  
  172.                 try {  
  173.                     try {  
  174.                         max = Integer.parseInt(txt_max.getText());  
  175.                     } catch (Exception e1) {  
  176.                         throw new Exception("人数上限为正整数!");  
  177.                     }  
  178.                     if (max <= 0) {  
  179.                         throw new Exception("人数上限为正整数!");  
  180.                     }  
  181.                     try {  
  182.                         port = Integer.parseInt(txt_port.getText());  
  183.                     } catch (Exception e1) {  
  184.                         throw new Exception("端口号为正整数!");  
  185.                     }  
  186.                     if (port <= 0) {  
  187.                         throw new Exception("端口号 为正整数!");  
  188.                     }  
  189.                     serverStart(max, port);  
  190.                     contentArea.append("服务器已成功启动!人数上限:" + max + ",端口:" + port  
  191.                             + "\r\n");  
  192.                     JOptionPane.showMessageDialog(frame, "服务器成功启动!");  
  193.                     btn_start.setEnabled(false);  
  194.                     txt_max.setEnabled(false);  
  195.                     txt_port.setEnabled(false);  
  196.                     btn_stop.setEnabled(true);  
  197.                 } catch (Exception exc) {  
  198.                     JOptionPane.showMessageDialog(frame, exc.getMessage(),  
  199.                             "错误", JOptionPane.ERROR_MESSAGE);  
  200.                 }  
  201.             }  
  202.         });  
  203.   
  204.         // 单击停止服务器按钮时事件  
  205.         btn_stop.addActionListener(new ActionListener() {  
  206.             public void actionPerformed(ActionEvent e) {  
  207.                 if (!isStart) {  
  208.                     JOptionPane.showMessageDialog(frame, "服务器还未启动,无需停止!", "错误",  
  209.                             JOptionPane.ERROR_MESSAGE);  
  210.                     return;  
  211.                 }  
  212.                 try {  
  213.                     closeServer();  
  214.                     btn_start.setEnabled(true);  
  215.                     txt_max.setEnabled(true);  
  216.                     txt_port.setEnabled(true);  
  217.                     btn_stop.setEnabled(false);  
  218.                     contentArea.append("服务器成功停止!\r\n");  
  219.                     JOptionPane.showMessageDialog(frame, "服务器成功停止!");  
  220.                 } catch (Exception exc) {  
  221.                     JOptionPane.showMessageDialog(frame, "停止服务器发生异常!", "错误",  
  222.                             JOptionPane.ERROR_MESSAGE);  
  223.                 }  
  224.             }  
  225.         });  
  226.     }  
  227.   
  228.     // 启动服务器  
  229.     public void serverStart(int max, int port) throws java.net.BindException {  
  230.         try {  
  231.             clients = new ArrayList<ClientThread>();  
  232.             serverSocket = new ServerSocket(port);  
  233.             serverThread = new ServerThread(serverSocket, max);  
  234.             serverThread.start();  
  235.             isStart = true;  
  236.         } catch (BindException e) {  
  237.             isStart = false;  
  238.             throw new BindException("端口号已被占用,请换一个!");  
  239.         } catch (Exception e1) {  
  240.             e1.printStackTrace();  
  241.             isStart = false;  
  242.             throw new BindException("启动服务器异常!");  
  243.         }  
  244.     }  
  245.   
  246.     // 关闭服务器  
  247.     @SuppressWarnings("deprecation")  
  248.     public void closeServer() {  
  249.         try {  
  250.             if (serverThread != null)  
  251.                 serverThread.stop();// 停止服务器线程  
  252.   
  253.             for (int i = clients.size() - 1; i >= 0; i--) {  
  254.                 // 给所有在线用户发送关闭命令  
  255.                 clients.get(i).getWriter().println("CLOSE");  
  256.                 clients.get(i).getWriter().flush();  
  257.                 // 释放资源  
  258.                 clients.get(i).stop();// 停止此条为客户端服务的线程  
  259.                 clients.get(i).reader.close();  
  260.                 clients.get(i).writer.close();  
  261.                 clients.get(i).socket.close();  
  262.                 clients.remove(i);  
  263.             }  
  264.             if (serverSocket != null) {  
  265.                 serverSocket.close();// 关闭服务器端连接  
  266.             }  
  267.             listModel.removeAllElements();// 清空用户列表  
  268.             isStart = false;  
  269.         } catch (IOException e) {  
  270.             e.printStackTrace();  
  271.             isStart = true;  
  272.         }  
  273.     }  
  274.   
  275.     // 群发服务器消息  
  276.     public void sendServerMessage(String message) {  
  277.         for (int i = clients.size() - 1; i >= 0; i--) {  
  278.             clients.get(i).getWriter().println("服务器:" + message + "(多人发送)");  
  279.             clients.get(i).getWriter().flush();  
  280.         }  
  281.     }  
  282.   
  283.     // 服务器线程  
  284.     class ServerThread extends Thread {  
  285.         private ServerSocket serverSocket;  
  286.         private int max;// 人数上限  
  287.   
  288.         // 服务器线程的构造方法  
  289.         public ServerThread(ServerSocket serverSocket, int max) {  
  290.             this.serverSocket = serverSocket;  
  291.             this.max = max;  
  292.         }  
  293.   
  294.         public void run() {  
  295.             while (true) {// 不停的等待客户端的链接  
  296.                 try {  
  297.                     Socket socket = serverSocket.accept();  
  298.                     if (clients.size() == max) {// 如果已达人数上限  
  299.                         BufferedReader r = new BufferedReader(  
  300.                                 new InputStreamReader(socket.getInputStream()));  
  301.                         PrintWriter w = new PrintWriter(socket  
  302.                                 .getOutputStream());  
  303.                         // 接收客户端的基本用户信息  
  304.                         String inf = r.readLine();  
  305.                         StringTokenizer st = new StringTokenizer(inf, "@");  
  306.                         User user = new User(st.nextToken(), st.nextToken());  
  307.                         // 反馈连接成功信息  
  308.                         w.println("MAX@服务器:对不起," + user.getName()  
  309.                                 + user.getIp() + ",服务器在线人数已达上限,请稍后尝试连接!");  
  310.                         w.flush();  
  311.                         // 释放资源  
  312.                         r.close();  
  313.                         w.close();  
  314.                         socket.close();  
  315.                         continue;  
  316.                     }  
  317.                     ClientThread client = new ClientThread(socket);  
  318.                     client.start();// 开启对此客户端服务的线程  
  319.                     clients.add(client);  
  320.                     listModel.addElement(client.getUser().getName());// 更新在线列表  
  321.                     contentArea.append(client.getUser().getName()  
  322.                             + client.getUser().getIp() + "上线!\r\n");  
  323.                 } catch (IOException e) {  
  324.                     e.printStackTrace();  
  325.                 }  
  326.             }  
  327.         }  
  328.     }  
  329.   
  330.     // 为一个客户端服务的线程  
  331.     class ClientThread extends Thread {  
  332.         private Socket socket;  
  333.         private BufferedReader reader;  
  334.         private PrintWriter writer;  
  335.         private User user;  
  336.   
  337.         public BufferedReader getReader() {  
  338.             return reader;  
  339.         }  
  340.   
  341.         public PrintWriter getWriter() {  
  342.             return writer;  
  343.         }  
  344.   
  345.         public User getUser() {  
  346.             return user;  
  347.         }  
  348.   
  349.         // 客户端线程的构造方法  
  350.         public ClientThread(Socket socket) {  
  351.             try {  
  352.                 this.socket = socket;  
  353.                 reader = new BufferedReader(new InputStreamReader(socket  
  354.                         .getInputStream()));  
  355.                 writer = new PrintWriter(socket.getOutputStream());  
  356.                 // 接收客户端的基本用户信息  
  357.                 String inf = reader.readLine();  
  358.                 StringTokenizer st = new StringTokenizer(inf, "@");  
  359.                 user = new User(st.nextToken(), st.nextToken());  
  360.                 // 反馈连接成功信息  
  361.                 writer.println(user.getName() + user.getIp() + "与服务器连接成功!");  
  362.                 writer.flush();  
  363.                 // 反馈当前在线用户信息  
  364.                 if (clients.size() > 0) {  
  365.                     String temp = "";  
  366.                     for (int i = clients.size() - 1; i >= 0; i--) {  
  367.                         temp += (clients.get(i).getUser().getName() + "/" + clients  
  368.                                 .get(i).getUser().getIp())  
  369.                                 + "@";  
  370.                     }  
  371.                     writer.println("USERLIST@" + clients.size() + "@" + temp);  
  372.                     writer.flush();  
  373.                 }  
  374.                 // 向所有在线用户发送该用户上线命令  
  375.                 for (int i = clients.size() - 1; i >= 0; i--) {  
  376.                     clients.get(i).getWriter().println(  
  377.                             "ADD@" + user.getName() + user.getIp());  
  378.                     clients.get(i).getWriter().flush();  
  379.                 }  
  380.             } catch (IOException e) {  
  381.                 e.printStackTrace();  
  382.             }  
  383.         }  
  384.   
  385.         @SuppressWarnings("deprecation")  
  386.         public void run() {// 不断接收客户端的消息,进行处理。  
  387.             String message = null;  
  388.             while (true) {  
  389.                 try {  
  390.                     message = reader.readLine();// 接收客户端消息  
  391.                     if (message.equals("CLOSE"))// 下线命令  
  392.                     {  
  393.                         contentArea.append(this.getUser().getName()  
  394.                                 + this.getUser().getIp() + "下线!\r\n");  
  395.                         // 断开连接释放资源  
  396.                         reader.close();  
  397.                         writer.close();  
  398.                         socket.close();  
  399.   
  400.                         // 向所有在线用户发送该用户的下线命令  
  401.                         for (int i = clients.size() - 1; i >= 0; i--) {  
  402.                             clients.get(i).getWriter().println(  
  403.                                     "DELETE@" + user.getName());  
  404.                             clients.get(i).getWriter().flush();  
  405.                         }  
  406.   
  407.                         listModel.removeElement(user.getName());// 更新在线列表  
  408.   
  409.                         // 删除此条客户端服务线程  
  410.                         for (int i = clients.size() - 1; i >= 0; i--) {  
  411.                             if (clients.get(i).getUser() == user) {  
  412.                                 ClientThread temp = clients.get(i);  
  413.                                 clients.remove(i);// 删除此用户的服务线程  
  414.                                 temp.stop();// 停止这条服务线程  
  415.                                 return;  
  416.                             }  
  417.                         }  
  418.                     } else {  
  419.                         dispatcherMessage(message);// 转发消息  
  420.                     }  
  421.                 } catch (IOException e) {  
  422.                     e.printStackTrace();  
  423.                 }  
  424.             }  
  425.         }  
  426.   
  427.         // 转发消息  
  428.         public void dispatcherMessage(String message) {  
  429.             StringTokenizer stringTokenizer = new StringTokenizer(message, "@");  
  430.             String source = stringTokenizer.nextToken();  
  431.             String owner = stringTokenizer.nextToken();  
  432.             String content = stringTokenizer.nextToken();  
  433.             message = source + "说:" + content;  
  434.             contentArea.append(message + "\r\n");  
  435.             if (owner.equals("ALL")) {// 群发  
  436.                 for (int i = clients.size() - 1; i >= 0; i--) {  
  437.                     clients.get(i).getWriter().println(message + "(多人发送)");  
  438.                     clients.get(i).getWriter().flush();  
  439.                 }  
  440.             }  
  441.         }  
  442.     }  
  443. }  


 

 

Client(客户端):

[html] view plain copy

 

  1. import java.awt.BorderLayout;  
  2. import java.awt.Color;  
  3. import java.awt.GridLayout;  
  4. import java.awt.Toolkit;  
  5. import java.awt.event.ActionEvent;  
  6. import java.awt.event.ActionListener;  
  7. import java.awt.event.WindowAdapter;  
  8. import java.awt.event.WindowEvent;  
  9. import java.io.BufferedReader;  
  10. import java.io.IOException;  
  11. import java.io.InputStreamReader;  
  12. import java.io.PrintWriter;  
  13. import java.net.Socket;  
  14. import java.util.HashMap;  
  15. import java.util.Map;  
  16. import java.util.StringTokenizer;  
  17.   
  18. import javax.swing.DefaultListModel;  
  19. import javax.swing.JButton;  
  20. import javax.swing.JFrame;  
  21. import javax.swing.JLabel;  
  22. import javax.swing.JList;  
  23. import javax.swing.JOptionPane;  
  24. import javax.swing.JPanel;  
  25. import javax.swing.JScrollPane;  
  26. import javax.swing.JSplitPane;  
  27. import javax.swing.JTextArea;  
  28. import javax.swing.JTextField;  
  29. import javax.swing.border.TitledBorder;  
  30.   
  31. public class Client{  
  32.   
  33.     private JFrame frame;  
  34.     private JList userList;  
  35.     private JTextArea textArea;  
  36.     private JTextField textField;  
  37.     private JTextField txt_port;  
  38.     private JTextField txt_hostIp;  
  39.     private JTextField txt_name;  
  40.     private JButton btn_start;  
  41.     private JButton btn_stop;  
  42.     private JButton btn_send;  
  43.     private JPanel northPanel;  
  44.     private JPanel southPanel;  
  45.     private JScrollPane rightScroll;  
  46.     private JScrollPane leftScroll;  
  47.     private JSplitPane centerSplit;  
  48.   
  49.     private DefaultListModel listModel;  
  50.     private boolean isConnected = false;  
  51.   
  52.     private Socket socket;  
  53.     private PrintWriter writer;  
  54.     private BufferedReader reader;  
  55.     private MessageThread messageThread;// 负责接收消息的线程  
  56.     private Map<String, User> onLineUsers = new HashMap<String, User>();// 所有在线用户  
  57.   
  58.     // 主方法,程序入口  
  59.     public static void main(String[] args) {  
  60.         new Client();  
  61.     }  
  62.   
  63.     // 执行发送  
  64.     public void send() {  
  65.         if (!isConnected) {  
  66.             JOptionPane.showMessageDialog(frame, "还没有连接服务器,无法发送消息!", "错误",  
  67.                     JOptionPane.ERROR_MESSAGE);  
  68.             return;  
  69.         }  
  70.         String message = textField.getText().trim();  
  71.         if (message == null || message.equals("")) {  
  72.             JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误",  
  73.                     JOptionPane.ERROR_MESSAGE);  
  74.             return;  
  75.         }  
  76.         sendMessage(frame.getTitle() + "@" + "ALL" + "@" + message);  
  77.         textField.setText(null);  
  78.     }  
  79.   
  80.     // 构造方法  
  81.     public Client() {  
  82.         textArea = new JTextArea();  
  83.         textArea.setEditable(false);  
  84.         textArea.setForeground(Color.blue);  
  85.         textField = new JTextField();  
  86.         txt_port = new JTextField("6666");  
  87.         txt_hostIp = new JTextField("127.0.0.1");  
  88.         txt_name = new JTextField("xiaoqiang");  
  89.         btn_start = new JButton("连接");  
  90.         btn_stop = new JButton("断开");  
  91.         btn_send = new JButton("发送");  
  92.         listModel = new DefaultListModel();  
  93.         userList = new JList(listModel);  
  94.   
  95.         northPanel = new JPanel();  
  96.         northPanel.setLayout(new GridLayout(1, 7));  
  97.         northPanel.add(new JLabel("端口"));  
  98.         northPanel.add(txt_port);  
  99.         northPanel.add(new JLabel("服务器IP"));  
  100.         northPanel.add(txt_hostIp);  
  101.         northPanel.add(new JLabel("姓名"));  
  102.         northPanel.add(txt_name);  
  103.         northPanel.add(btn_start);  
  104.         northPanel.add(btn_stop);  
  105.         northPanel.setBorder(new TitledBorder("连接信息"));  
  106.   
  107.         rightScroll = new JScrollPane(textArea);  
  108.         rightScroll.setBorder(new TitledBorder("消息显示区"));  
  109.         leftScroll = new JScrollPane(userList);  
  110.         leftScroll.setBorder(new TitledBorder("在线用户"));  
  111.         southPanel = new JPanel(new BorderLayout());  
  112.         southPanel.add(textField, "Center");  
  113.         southPanel.add(btn_send, "East");  
  114.         southPanel.setBorder(new TitledBorder("写消息"));  
  115.   
  116.         centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll,  
  117.                 rightScroll);  
  118.         centerSplit.setDividerLocation(100);  
  119.   
  120.         frame = new JFrame("客户机");  
  121.         // 更改JFrame的图标:  
  122.         frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png")));  
  123.         frame.setLayout(new BorderLayout());  
  124.         frame.add(northPanel, "North");  
  125.         frame.add(centerSplit, "Center");  
  126.         frame.add(southPanel, "South");  
  127.         frame.setSize(600, 400);  
  128.         int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;  
  129.         int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;  
  130.         frame.setLocation((screen_width - frame.getWidth()) / 2,  
  131.                 (screen_height - frame.getHeight()) / 2);  
  132.         frame.setVisible(true);  
  133.   
  134.         // 写消息的文本框中按回车键时事件  
  135.         textField.addActionListener(new ActionListener() {  
  136.             public void actionPerformed(ActionEvent arg0) {  
  137.                 send();  
  138.             }  
  139.         });  
  140.   
  141.         // 单击发送按钮时事件  
  142.         btn_send.addActionListener(new ActionListener() {  
  143.             public void actionPerformed(ActionEvent e) {  
  144.                 send();  
  145.             }  
  146.         });  
  147.   
  148.         // 单击连接按钮时事件  
  149.         btn_start.addActionListener(new ActionListener() {  
  150.             public void actionPerformed(ActionEvent e) {  
  151.                 int port;  
  152.                 if (isConnected) {  
  153.                     JOptionPane.showMessageDialog(frame, "已处于连接上状态,不要重复连接!",  
  154.                             "错误", JOptionPane.ERROR_MESSAGE);  
  155.                     return;  
  156.                 }  
  157.                 try {  
  158.                     try {  
  159.                         port = Integer.parseInt(txt_port.getText().trim());  
  160.                     } catch (NumberFormatException e2) {  
  161.                         throw new Exception("端口号不符合要求!端口为整数!");  
  162.                     }  
  163.                     String hostIp = txt_hostIp.getText().trim();  
  164.                     String name = txt_name.getText().trim();  
  165.                     if (name.equals("") || hostIp.equals("")) {  
  166.                         throw new Exception("姓名、服务器IP不能为空!");  
  167.                     }  
  168.                     boolean flag = connectServer(port, hostIp, name);  
  169.                     if (flag == false) {  
  170.                         throw new Exception("与服务器连接失败!");  
  171.                     }  
  172.                     frame.setTitle(name);  
  173.                     JOptionPane.showMessageDialog(frame, "成功连接!");  
  174.                 } catch (Exception exc) {  
  175.                     JOptionPane.showMessageDialog(frame, exc.getMessage(),  
  176.                             "错误", JOptionPane.ERROR_MESSAGE);  
  177.                 }  
  178.             }  
  179.         });  
  180.   
  181.         // 单击断开按钮时事件  
  182.         btn_stop.addActionListener(new ActionListener() {  
  183.             public void actionPerformed(ActionEvent e) {  
  184.                 if (!isConnected) {  
  185.                     JOptionPane.showMessageDialog(frame, "已处于断开状态,不要重复断开!",  
  186.                             "错误", JOptionPane.ERROR_MESSAGE);  
  187.                     return;  
  188.                 }  
  189.                 try {  
  190.                     boolean flag = closeConnection();// 断开连接  
  191.                     if (flag == false) {  
  192.                         throw new Exception("断开连接发生异常!");  
  193.                     }  
  194.                     JOptionPane.showMessageDialog(frame, "成功断开!");  
  195.                 } catch (Exception exc) {  
  196.                     JOptionPane.showMessageDialog(frame, exc.getMessage(),  
  197.                             "错误", JOptionPane.ERROR_MESSAGE);  
  198.                 }  
  199.             }  
  200.         });  
  201.   
  202.         // 关闭窗口时事件  
  203.         frame.addWindowListener(new WindowAdapter() {  
  204.             public void windowClosing(WindowEvent e) {  
  205.                 if (isConnected) {  
  206.                     closeConnection();// 关闭连接  
  207.                 }  
  208.                 System.exit(0);// 退出程序  
  209.             }  
  210.         });  
  211.     }  
  212.   
  213.     /**  
  214.      * 连接服务器  
  215.      *   
  216.      * @param port  
  217.      * @param hostIp  
  218.      * @param name  
  219.      */  
  220.     public boolean connectServer(int port, String hostIp, String name) {  
  221.         // 连接服务器  
  222.         try {  
  223.             socket = new Socket(hostIp, port);// 根据端口号和服务器ip建立连接  
  224.             writer = new PrintWriter(socket.getOutputStream());  
  225.             reader = new BufferedReader(new InputStreamReader(socket  
  226.                     .getInputStream()));  
  227.             // 发送客户端用户基本信息(用户名和ip地址)  
  228.             sendMessage(name + "@" + socket.getLocalAddress().toString());  
  229.             // 开启接收消息的线程  
  230.             messageThread = new MessageThread(reader, textArea);  
  231.             messageThread.start();  
  232.             isConnected = true;// 已经连接上了  
  233.             return true;  
  234.         } catch (Exception e) {  
  235.             textArea.append("与端口号为:" + port + "    IP地址为:" + hostIp  
  236.                     + "   的服务器连接失败!" + "\r\n");  
  237.             isConnected = false;// 未连接上  
  238.             return false;  
  239.         }  
  240.     }  
  241.   
  242.     /**  
  243.      * 发送消息  
  244.      *   
  245.      * @param message  
  246.      */  
  247.     public void sendMessage(String message) {  
  248.         writer.println(message);  
  249.         writer.flush();  
  250.     }  
  251.   
  252.     /**  
  253.      * 客户端主动关闭连接  
  254.      */  
  255.     @SuppressWarnings("deprecation")  
  256.     public synchronized boolean closeConnection() {  
  257.         try {  
  258.             sendMessage("CLOSE");// 发送断开连接命令给服务器  
  259.             messageThread.stop();// 停止接受消息线程  
  260.             // 释放资源  
  261.             if (reader != null) {  
  262.                 reader.close();  
  263.             }  
  264.             if (writer != null) {  
  265.                 writer.close();  
  266.             }  
  267.             if (socket != null) {  
  268.                 socket.close();  
  269.             }  
  270.             isConnected = false;  
  271.             return true;  
  272.         } catch (IOException e1) {  
  273.             e1.printStackTrace();  
  274.             isConnected = true;  
  275.             return false;  
  276.         }  
  277.     }  
  278.   
  279.     // 不断接收消息的线程  
  280.     class MessageThread extends Thread {  
  281.         private BufferedReader reader;  
  282.         private JTextArea textArea;  
  283.   
  284.         // 接收消息线程的构造方法  
  285.         public MessageThread(BufferedReader reader, JTextArea textArea) {  
  286.             this.reader = reader;  
  287.             this.textArea = textArea;  
  288.         }  
  289.   
  290.         // 被动的关闭连接  
  291.         public synchronized void closeCon() throws Exception {  
  292.             // 清空用户列表  
  293.             listModel.removeAllElements();  
  294.             // 被动的关闭连接释放资源  
  295.             if (reader != null) {  
  296.                 reader.close();  
  297.             }  
  298.             if (writer != null) {  
  299.                 writer.close();  
  300.             }  
  301.             if (socket != null) {  
  302.                 socket.close();  
  303.             }  
  304.             isConnected = false;// 修改状态为断开  
  305.         }  
  306.   
  307.         public void run() {  
  308.             String message = "";  
  309.             while (true) {  
  310.                 try {  
  311.                     message = reader.readLine();  
  312.                     StringTokenizer stringTokenizer = new StringTokenizer(  
  313.                             message, "/@");  
  314.                     String command = stringTokenizer.nextToken();// 命令  
  315.                     if (command.equals("CLOSE"))// 服务器已关闭命令  
  316.                     {  
  317.                         textArea.append("服务器已关闭!\r\n");  
  318.                         closeCon();// 被动的关闭连接  
  319.                         return;// 结束线程  
  320.                     } else if (command.equals("ADD")) {// 有用户上线更新在线列表  
  321.                         String username = "";  
  322.                         String userIp = "";  
  323.                         if ((username = stringTokenizer.nextToken()) != null  
  324.                                 && (userIp = stringTokenizer.nextToken()) != null) {  
  325.                             User user = new User(username, userIp);  
  326.                             onLineUsers.put(username, user);  
  327.                             listModel.addElement(username);  
  328.                         }  
  329.                     } else if (command.equals("DELETE")) {// 有用户下线更新在线列表  
  330.                         String username = stringTokenizer.nextToken();  
  331.                         User user = (User) onLineUsers.get(username);  
  332.                         onLineUsers.remove(user);  
  333.                         listModel.removeElement(username);  
  334.                     } else if (command.equals("USERLIST")) {// 加载在线用户列表  
  335.                         int size = Integer  
  336.                                 .parseInt(stringTokenizer.nextToken());  
  337.                         String username = null;  
  338.                         String userIp = null;  
  339.                         for (int i = 0; i < size; i++) {  
  340.                             username = stringTokenizer.nextToken();  
  341.                             userIp = stringTokenizer.nextToken();  
  342.                             User user = new User(username, userIp);  
  343.                             onLineUsers.put(username, user);  
  344.                             listModel.addElement(username);  
  345.                         }  
  346.                     } else if (command.equals("MAX")) {// 人数已达上限  
  347.                         textArea.append(stringTokenizer.nextToken()  
  348.                                 + stringTokenizer.nextToken() + "\r\n");  
  349.                         closeCon();// 被动的关闭连接  
  350.                         JOptionPane.showMessageDialog(frame, "服务器缓冲区已满!", "错误",  
  351.                                 JOptionPane.ERROR_MESSAGE);  
  352.                         return;// 结束线程  
  353.                     } else {// 普通消息  
  354.                         textArea.append(message + "\r\n");  
  355.                     }  
  356.                 } catch (IOException e) {  
  357.                     e.printStackTrace();  
  358.                 } catch (Exception e) {  
  359.                     e.printStackTrace();  
  360.                 }  
  361.             }  
  362.         }  
  363.     }  
  364. }  


 

 

User.java

[html] view plain copy

 

  1. //用户信息类  
  2. public class User{  
  3.     private String name;  
  4.     private String ip;  
  5.   
  6.     public User(String name, String ip) {  
  7.         this.name = name;  
  8.         this.ip = ip;  
  9.     }  
  10.   
  11.     public String getName() {  
  12.         return name;  
  13.     }  
  14.   
  15.     public void setName(String name) {  
  16.         this.name = name;  
  17.     }  
  18.   
  19.     public String getIp() {  
  20.         return ip;  
  21.     }  
  22.   
  23.     public void setIp(String ip) {  
  24.         this.ip = ip;  
  25.     }  
  26. }  


 

注: 在eclipse下运行无需另外添加jar文件,其中服务器端和客户端均需要一张图片做icon,大家可以随便找张图片补上。 接把图片放在所建的包下,和三个类文件并列放在一起就行

 

Java Socket   多人聊天室 swing 局域网

共有 人打赏支持
粉丝 15
博文 722
码字总数 600072
×
一贱书生
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: