├── .gitignore ├── README.md └── src ├── .DS_Store └── com ├── .DS_Store └── longluo ├── .DS_Store └── webchat ├── .DS_Store ├── ChatClient.java ├── ChatServer.java ├── Constants.java ├── ServerFrame.java └── ServerProcess.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # 4 | .idea/ 5 | *.iml 6 | 7 | bin/ 8 | 9 | .settings/ 10 | .classpath 11 | .project 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | 18 | *_user.txt 19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Chat Room 2 | 3 | --------------------------------------------------------------------------- 4 | 5 | **A Web Chat Room Project writing in Java, you can chat with others whom in the LAN.** 6 | 7 | # How To Use 8 | 9 | 1. 运行ChatServer.java 10 | 2. 运行ChatClient.java 11 | 3. 客户端出现登录界面 12 | 4. 登录成功后进入通信面板 13 | 14 | More details and descriptions you can refer to: 15 | http://blog.csdn.net/tcpipstack/article/details/8610849 16 | or: 17 | http://www.longluo.me/blog/2011/10/15/Implement-A-Chat-Room-By-Java/ 18 | 19 | ## Thank you. 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longluo/WebChat/c23486842df432b2aeb30aa460b802277a489e26/src/.DS_Store -------------------------------------------------------------------------------- /src/com/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longluo/WebChat/c23486842df432b2aeb30aa460b802277a489e26/src/com/.DS_Store -------------------------------------------------------------------------------- /src/com/longluo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longluo/WebChat/c23486842df432b2aeb30aa460b802277a489e26/src/com/longluo/.DS_Store -------------------------------------------------------------------------------- /src/com/longluo/webchat/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longluo/WebChat/c23486842df432b2aeb30aa460b802277a489e26/src/com/longluo/webchat/.DS_Store -------------------------------------------------------------------------------- /src/com/longluo/webchat/ChatClient.java: -------------------------------------------------------------------------------- 1 | package com.longluo.webchat; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.*; 6 | import java.net.*; 7 | import java.io.*; 8 | import java.util.*; 9 | 10 | /** 11 | * 类名:ChatClient 12 | * 描述:创建一个聊天室客户端,连接服务器并实现聊天功能 13 | * 14 | * @author longluo 15 | */ 16 | public class ChatClient extends JFrame implements ActionListener { 17 | JFrame clientFrame = new JFrame(Constants.APP_SERVER_NAME); 18 | 19 | GridBagLayout gl; 20 | BorderLayout bdl; 21 | GridBagConstraints gbc; 22 | 23 | // 聊天界面 24 | JPanel pnlBack, pnlTalk; 25 | JButton btnTalk; 26 | JTextArea txtViewTalk; 27 | JLabel lblTalk, lblTo; 28 | JComboBox listOnline; 29 | 30 | // 登陆界面 31 | JPanel pnlLogin; 32 | 33 | JLabel lblServerIP; 34 | JLabel lblName; 35 | JLabel lblPassword; 36 | 37 | JTextField txtTalk; 38 | JTextField txtServerIP; 39 | JTextField txtName; 40 | 41 | JPasswordField txtPassword; 42 | 43 | JButton btnLogin; 44 | JButton btnReg; 45 | JButton btnExit; 46 | 47 | JDialog dialogLogin = new JDialog(this, Constants.LOGIN, true); 48 | 49 | Socket socket = null; 50 | BufferedReader in = null; 51 | PrintWriter out = null; 52 | 53 | String strSend; 54 | String strReceive; 55 | String strKey; 56 | String strStatus; 57 | private StringTokenizer st; 58 | 59 | public ChatClient() { 60 | // 初始化 61 | gl = new GridBagLayout(); 62 | bdl = new BorderLayout(); 63 | gbc = new GridBagConstraints(); 64 | pnlBack = (JPanel) getContentPane(); 65 | pnlBack.setLayout(bdl); 66 | 67 | // 初始化控件 68 | pnlLogin = new JPanel(); 69 | pnlLogin.setLayout(gl); 70 | 71 | lblServerIP = new JLabel(Constants.SERVER_IP); 72 | lblName = new JLabel(Constants.USER_NAME); 73 | lblPassword = new JLabel(Constants.PASSWORD); 74 | txtServerIP = new JTextField(12); 75 | txtName = new JTextField(12); 76 | txtPassword = new JPasswordField(12); 77 | 78 | txtServerIP.setText("127.0.0.1"); 79 | 80 | btnLogin = new JButton(Constants.LOGIN); 81 | btnReg = new JButton(Constants.REGISTER); 82 | btnExit = new JButton(Constants.EXIT); 83 | 84 | btnTalk = new JButton(Constants.SEND); 85 | lblTalk = new JLabel(Constants.SPEAK); 86 | 87 | lblTo = new JLabel(" To :"); 88 | txtTalk = new JTextField(30); 89 | pnlTalk = new JPanel(); 90 | txtViewTalk = new JTextArea(18, 40); 91 | listOnline = new JComboBox(); 92 | txtViewTalk.setForeground(Color.blue); 93 | btnTalk.addActionListener(this); 94 | 95 | btnLogin.addActionListener(this); 96 | btnReg.addActionListener(this); 97 | btnExit.addActionListener(this); 98 | 99 | listOnline.addItem("All"); 100 | 101 | pnlTalk.add(lblTalk); 102 | pnlTalk.add(txtTalk); 103 | pnlTalk.add(lblTo); 104 | pnlTalk.add(listOnline); 105 | pnlTalk.add(btnTalk); 106 | pnlBack.add("Center", txtViewTalk); 107 | pnlBack.add("South", pnlTalk); 108 | pnlTalk.setBackground(Color.cyan); 109 | btnTalk.setEnabled(false); 110 | 111 | clientFrame.getContentPane().add(pnlBack); 112 | clientFrame.setSize(600, 450); 113 | clientFrame.setVisible(true); 114 | clientFrame.setResizable(false); 115 | clientFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 116 | 117 | // 登陆对话框初始化 118 | dialogLogin.getContentPane().setLayout(new FlowLayout()); 119 | dialogLogin.getContentPane().add(lblServerIP); 120 | dialogLogin.getContentPane().add(txtServerIP); 121 | dialogLogin.getContentPane().add(lblName); 122 | dialogLogin.getContentPane().add(txtName); 123 | dialogLogin.getContentPane().add(lblPassword); 124 | dialogLogin.getContentPane().add(txtPassword); 125 | dialogLogin.getContentPane().add(btnLogin); 126 | dialogLogin.getContentPane().add(btnReg); 127 | dialogLogin.getContentPane().add(btnExit); 128 | 129 | dialogLogin.setBounds(300, 300, 250, 200); 130 | dialogLogin.getContentPane().setBackground(Color.gray); 131 | dialogLogin.show(true); 132 | } 133 | 134 | public static void main(String[] args) { 135 | new ChatClient(); 136 | } 137 | 138 | /** 139 | * 建立与服务端通信的套接字 140 | */ 141 | void connectServer() { 142 | try { 143 | socket = new Socket(txtServerIP.getText(), 8888); 144 | in = new BufferedReader(new InputStreamReader( 145 | socket.getInputStream())); 146 | out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 147 | socket.getOutputStream())), true); 148 | } catch (ConnectException e) { 149 | JOptionPane.showMessageDialog(this, "连接服务器失败!", "ERROE", 150 | JOptionPane.INFORMATION_MESSAGE); 151 | txtServerIP.setText(""); 152 | System.out.println(e); 153 | } catch (Exception e) { 154 | System.out.println(e); 155 | } 156 | } 157 | 158 | /** 159 | * 弹出窗口 160 | * 161 | * @param strWarning 162 | * @param strTitle 163 | */ 164 | public void popWindows(String strWarning, String strTitle) { 165 | JOptionPane.showMessageDialog(this, strWarning, strTitle, 166 | JOptionPane.INFORMATION_MESSAGE); 167 | } 168 | 169 | private void initLogin() throws IOException { 170 | strReceive = in.readLine(); 171 | st = new StringTokenizer(strReceive, "|"); 172 | strKey = st.nextToken(); 173 | if (strKey.equals("login")) { 174 | strStatus = st.nextToken(); 175 | if (strStatus.equals("succeed")) { 176 | btnLogin.setEnabled(false); 177 | btnTalk.setEnabled(true); 178 | pnlLogin.setVisible(false); 179 | dialogLogin.dispose(); 180 | new ClientThread(socket); 181 | out.println("init|online"); 182 | } 183 | popWindows(strKey + " " + strStatus + "!", "Login"); 184 | } 185 | if (strKey.equals("warning")) { 186 | strStatus = st.nextToken(); 187 | popWindows(strStatus, "Register"); 188 | } 189 | } 190 | 191 | public void actionPerformed(ActionEvent evt) { 192 | Object obj = evt.getSource(); 193 | 194 | try { 195 | if (obj.equals(btnLogin)) { 196 | if ((txtServerIP.getText().length() > 0) 197 | && (txtName.getText().length() > 0) 198 | && (txtPassword.getText().length() > 0)) { 199 | connectServer(); 200 | strSend = "login|" + txtName.getText() + "|" 201 | + String.valueOf(txtPassword.getPassword()); 202 | out.println(strSend); 203 | initLogin(); 204 | } else { 205 | popWindows("请输入完整信息", "ERROR"); 206 | } 207 | } else if (obj.equals(btnReg)) { 208 | if ((txtName.getText().length() > 0) 209 | && (txtPassword.getText().length() > 0)) { 210 | connectServer(); 211 | strSend = "reg|" + txtName.getText() + "|" 212 | + String.valueOf(txtPassword.getPassword()); 213 | out.println(strSend); 214 | initLogin(); 215 | } 216 | } else if (obj.equals(btnExit)) { 217 | System.exit(0); 218 | } else if (obj.equals(btnTalk)) { 219 | if (txtTalk.getText().length() > 0) { 220 | out.println("talk|" + txtTalk.getText() + "|" 221 | + txtName.getText() + "|" 222 | + listOnline.getSelectedItem().toString());// 223 | txtTalk.setText(""); 224 | } 225 | } 226 | } catch (Exception e) { 227 | System.out.println(e); 228 | } 229 | } 230 | 231 | class ClientThread implements Runnable { 232 | private Socket socket; 233 | private BufferedReader in; 234 | private PrintWriter out; 235 | private String strReceive, strKey; 236 | private Thread threadTalk; 237 | private StringTokenizer st; 238 | 239 | public ClientThread(Socket s) throws IOException { 240 | this.socket = s; 241 | in = new BufferedReader(new InputStreamReader( 242 | socket.getInputStream())); 243 | threadTalk = new Thread(this); 244 | threadTalk.start(); 245 | } 246 | 247 | public void run() { 248 | while (true) { 249 | synchronized (this) { 250 | try { 251 | strReceive = in.readLine(); 252 | st = new StringTokenizer(strReceive, "|"); 253 | strKey = st.nextToken(); 254 | if (strKey.equals("talk")) { 255 | String strTalk = st.nextToken(); 256 | strTalk = txtViewTalk.getText() + "\r\n " 257 | + strTalk; 258 | txtViewTalk.setText(strTalk); 259 | } else if (strKey.equals("online")) { 260 | String strOnline; 261 | while (st.hasMoreTokens()) { 262 | strOnline = st.nextToken(); 263 | listOnline.addItem(strOnline); 264 | } 265 | } else if (strKey.equals("remove")) { 266 | String strRemove; 267 | while (st.hasMoreTokens()) { 268 | strRemove = st.nextToken(); 269 | listOnline.removeItem(strRemove); 270 | } 271 | } else if (strKey.equals("warning")) { 272 | String strWarning = st.nextToken(); 273 | popWindows(strWarning, "Warning"); 274 | } 275 | Thread.sleep(1000); 276 | } catch (InterruptedException e) { 277 | } catch (IOException e) { 278 | } 279 | } 280 | } 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/com/longluo/webchat/ChatServer.java: -------------------------------------------------------------------------------- 1 | package com.longluo.webchat; 2 | 3 | import java.io.IOException; 4 | import java.net.BindException; 5 | import java.net.InetAddress; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | 9 | /** 10 | * 类名:ChatServer 11 | * 描述:创建一个网络聊天室服务器程序,描绘服务器界面,并与客户端建立联系 12 | * 13 | * @author longluo 14 | */ 15 | public class ChatServer extends Thread { 16 | ServerFrame serverFrame = null; 17 | ServerSocket serverSocket = null; // 创建服务器端套接字 18 | 19 | public boolean bServerIsRunning = false; 20 | 21 | public ChatServer() { 22 | try { 23 | serverSocket = new ServerSocket(Constants.SERVER_PORT); // 启动服务 24 | bServerIsRunning = true; 25 | 26 | serverFrame = new ServerFrame(); 27 | getServerIP(); // 得到并显示服务器端IP 28 | System.out.println("Server Port is:" + Constants.SERVER_PORT); 29 | serverFrame.taLog.setText("服务器已经启动..."); 30 | while (true) { 31 | Socket socket = serverSocket.accept(); // 监听客户端的连接请求,并返回客户端socket 32 | new ServerProcess(socket, serverFrame); // 创建一个新线程来处理与该客户的通讯 33 | } 34 | } catch (BindException e) { 35 | System.out.println("端口使用中...."); 36 | System.out.println("请关掉相关程序并重新运行服务器!"); 37 | System.exit(0); 38 | } catch (IOException e) { 39 | System.out.println("[ERROR] Cound not start server." + e); 40 | } 41 | 42 | this.start(); // 启动线程 43 | } 44 | 45 | /** 46 | * 获取服务器的主机名和IP地址 47 | */ 48 | public void getServerIP() { 49 | try { 50 | InetAddress serverAddress = InetAddress.getLocalHost(); 51 | byte[] ipAddress = serverAddress.getAddress(); 52 | 53 | serverFrame.txtServerName.setText(serverAddress.getHostName()); 54 | serverFrame.txtIP.setText(serverAddress.getHostAddress()); 55 | serverFrame.txtPort.setText(String.valueOf(Constants.SERVER_PORT)); 56 | 57 | System.out.println("Server IP is:" + (ipAddress[0] & 0xff) + "." 58 | + (ipAddress[1] & 0xff) + "." + (ipAddress[2] & 0xff) + "." 59 | + (ipAddress[3] & 0xff)); 60 | } catch (Exception e) { 61 | System.out.println("###Cound not get Server IP." + e); 62 | } 63 | } 64 | 65 | /** 66 | * main方法,实例化服务器端程序 67 | * 68 | * @param args 69 | */ 70 | public static void main(String args[]) { 71 | new ChatServer(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/com/longluo/webchat/Constants.java: -------------------------------------------------------------------------------- 1 | package com.longluo.webchat; 2 | 3 | /** 4 | * The Project's Constants 5 | */ 6 | public class Constants { 7 | // The Strings 8 | public static final String APP_NAME = "WebChat"; 9 | public static final String APP_NAME_STR = "Web ChatRoom 网络聊天室"; 10 | public static final String APP_SERVER_NAME = "Web ChatRoom 聊天服务器"; 11 | 12 | public static final int SERVER_PORT = 8888;// 定义服务器端口号 13 | 14 | public static final String LOGIN = "登陆"; 15 | public static final String REGISTER = "注册"; 16 | public static final String EXIT = "退出"; 17 | 18 | public static final String SEND = "发送"; 19 | public static final String SPEAK = "发言:"; 20 | public static final String SAVE = "保存"; 21 | 22 | public static final String SERVER_IP = "服务器IP:"; 23 | public static final String USER_NAME = " 用户名:"; 24 | public static final String PASSWORD = " 密码: "; 25 | 26 | public static final String USER = "用户"; 27 | 28 | public static final String SERVER = "服务器"; 29 | public static final String LOG = "日志"; 30 | 31 | public static final String SERVER_NAME = "服务器名称:"; 32 | public static final String SERVER_PORT_DESC = "服务器端口:"; 33 | 34 | public static final String CLOSE_SERVER = "关闭服务器(C)"; 35 | 36 | public static final String SERVER_LOG = "[服务器日志]"; 37 | public static final String SAVE_LOG = "保存日志(S)]"; 38 | public static final String FONT_SONG = "宋体"; 39 | 40 | public static final String SERVER_ADMIN = SERVER + "管理"; 41 | public static final String ONLINE_USERS = "在线用户"; 42 | public static final String ONLINE_USERS_NUM = "当前在线人数:"; 43 | public static final String ONLINE_USERS_LIST = "[在线用户列表]"; 44 | public static final String ABOUT_APP = "关于本软件"; 45 | public static final String ZERO_NUM = "0 人"; 46 | 47 | public static final String HAD_EXIT = "已经退出, "; 48 | public static final String EXIT_TIME = "退出时间:"; 49 | public static final String LEAVE_ROOM = " 恋恋不舍的离开了聊天室。"; 50 | 51 | public static final String VERSION_INFO = "Version No. v1.51"; 52 | public static final String APP_ABOUT_INFO = "Long.Luo Created @8th, March, 2012" + "at Shenzhen, China"; 53 | 54 | public static final String SAVE_LOG_FILE = "记录保存在log.txt"; 55 | } 56 | -------------------------------------------------------------------------------- /src/com/longluo/webchat/ServerFrame.java: -------------------------------------------------------------------------------- 1 | package com.longluo.webchat; 2 | 3 | import java.awt.*; 4 | import java.awt.event.*; 5 | import java.io.*; 6 | import javax.swing.*; 7 | 8 | /** 9 | * 类名:ServerFrame 10 | * 描述:The Server UI. 服务器窗口 11 | * 12 | * @author longluo 13 | */ 14 | public class ServerFrame extends JFrame implements ActionListener { 15 | protected JTabbedPane tpServer; 16 | 17 | // 服务器信息面板 18 | protected JPanel pnlServer; 19 | protected JPanel pnlServerInfo; 20 | 21 | protected JLabel lblNumber; 22 | protected JLabel lblServerName; 23 | protected JLabel lblIP; 24 | protected JLabel lblPort; 25 | protected JLabel lblLog; 26 | 27 | protected JTextField txtNumber; 28 | protected JTextField txtServerName; 29 | protected JTextField txtIP; 30 | protected JTextField txtPort; 31 | protected JButton btnStop; 32 | protected JButton btnSaveLog; 33 | protected TextArea taLog; 34 | 35 | // 用户信息面板 36 | protected JPanel pnlUser; 37 | protected JLabel lblUser; 38 | protected JList lstUser; 39 | protected JScrollPane spUser; 40 | 41 | // 关于本软件 42 | protected JPanel pnlAbout; 43 | protected JLabel lblVersionNo; 44 | protected JLabel lblAbout; 45 | 46 | public ServerFrame() { 47 | super(Constants.APP_SERVER_NAME); 48 | setSize(550, 500); 49 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 50 | setResizable(false); 51 | Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();// 在屏幕居中显示 52 | Dimension fra = this.getSize(); 53 | if (fra.width > scr.width) { 54 | fra.width = scr.width; 55 | } 56 | if (fra.height > scr.height) { 57 | fra.height = scr.height; 58 | } 59 | this.setLocation((scr.width - fra.width) / 2, 60 | (scr.height - fra.height) / 2); 61 | 62 | // 服务器信息 63 | pnlServerInfo = new JPanel(new GridLayout(14, 1)); 64 | pnlServerInfo.setBackground(new Color(52, 130, 203)); 65 | pnlServerInfo.setFont(new Font(Constants.FONT_SONG, 0, 12)); 66 | pnlServerInfo.setBorder(BorderFactory.createCompoundBorder( 67 | BorderFactory.createTitledBorder(""), 68 | BorderFactory.createEmptyBorder(1, 1, 1, 1))); 69 | 70 | lblNumber = new JLabel(Constants.ONLINE_USERS_NUM); 71 | lblNumber.setForeground(Color.YELLOW); 72 | lblNumber.setFont(new Font(Constants.FONT_SONG, 0, 12)); 73 | txtNumber = new JTextField(Constants.ZERO_NUM, 10); 74 | txtNumber.setBackground(Color.decode("#d6f4f2")); 75 | txtNumber.setFont(new Font(Constants.FONT_SONG, 0, 12)); 76 | txtNumber.setEditable(false); 77 | 78 | lblServerName = new JLabel(Constants.SERVER_NAME); 79 | lblServerName.setForeground(Color.YELLOW); 80 | lblServerName.setFont(new Font(Constants.FONT_SONG, 0, 12)); 81 | txtServerName = new JTextField(10); 82 | txtServerName.setBackground(Color.decode("#d6f4f2")); 83 | txtServerName.setFont(new Font(Constants.FONT_SONG, 0, 12)); 84 | txtServerName.setEditable(false); 85 | 86 | lblIP = new JLabel(Constants.SERVER_IP); 87 | lblIP.setForeground(Color.YELLOW); 88 | lblIP.setFont(new Font(Constants.FONT_SONG, 0, 12)); 89 | txtIP = new JTextField(10); 90 | txtIP.setBackground(Color.decode("#d6f4f2")); 91 | txtIP.setFont(new Font(Constants.FONT_SONG, 0, 12)); 92 | txtIP.setEditable(false); 93 | 94 | lblPort = new JLabel(Constants.SERVER_PORT_DESC); 95 | lblPort.setForeground(Color.YELLOW); 96 | lblPort.setFont(new Font(Constants.FONT_SONG, 0, 12)); 97 | txtPort = new JTextField(String.valueOf(Constants.SERVER_PORT), 10); 98 | txtPort.setBackground(Color.decode("#d6f4f2")); 99 | txtPort.setFont(new Font(Constants.FONT_SONG, 0, 12)); 100 | txtPort.setEditable(false); 101 | 102 | btnStop = new JButton(Constants.CLOSE_SERVER); 103 | btnStop.addActionListener(new ActionListener() { 104 | public void actionPerformed(ActionEvent arg0) { 105 | closeServer(); 106 | } 107 | }); 108 | btnStop.setBackground(Color.ORANGE); 109 | btnStop.setFont(new Font(Constants.FONT_SONG, 0, 12)); 110 | 111 | pnlServerInfo.setBounds(5, 5, 100, 400); 112 | pnlServerInfo.add(lblNumber); 113 | pnlServerInfo.add(txtNumber); 114 | pnlServerInfo.add(lblServerName); 115 | pnlServerInfo.add(txtServerName); 116 | pnlServerInfo.add(lblIP); 117 | pnlServerInfo.add(txtIP); 118 | pnlServerInfo.add(lblPort); 119 | pnlServerInfo.add(txtPort); 120 | 121 | // 服务器面板 122 | pnlServer = new JPanel(); 123 | pnlServer.setLayout(null); 124 | pnlServer.setBackground(new Color(52, 130, 203)); 125 | 126 | lblLog = new JLabel(Constants.SERVER_LOG); 127 | lblLog.setForeground(Color.YELLOW); 128 | lblLog.setFont(new Font(Constants.FONT_SONG, 0, 12)); 129 | taLog = new TextArea(20, 50); 130 | taLog.setFont(new Font(Constants.FONT_SONG, 0, 12)); 131 | 132 | btnSaveLog = new JButton(Constants.SAVE_LOG); 133 | btnSaveLog.addActionListener(new ActionListener() { 134 | public void actionPerformed(ActionEvent arg0) { 135 | saveLog(); 136 | } 137 | }); 138 | btnSaveLog.setBackground(Color.ORANGE); 139 | btnSaveLog.setFont(new Font(Constants.FONT_SONG, 0, 12)); 140 | 141 | lblLog.setBounds(110, 5, 100, 30); 142 | taLog.setBounds(110, 35, 300, 370); 143 | btnStop.setBounds(200, 410, 120, 30); 144 | btnSaveLog.setBounds(320, 410, 120, 30); 145 | 146 | // 147 | pnlServer.add(pnlServerInfo); 148 | pnlServer.add(lblLog); 149 | pnlServer.add(taLog); 150 | pnlServer.add(btnStop); 151 | pnlServer.add(btnSaveLog); 152 | 153 | // 用户面板 154 | pnlUser = new JPanel(); 155 | pnlUser.setLayout(null); 156 | pnlUser.setBackground(new Color(52, 130, 203)); 157 | pnlUser.setBorder(BorderFactory.createCompoundBorder( 158 | BorderFactory.createTitledBorder(""), 159 | BorderFactory.createEmptyBorder(1, 1, 1, 1))); 160 | 161 | lblUser = new JLabel(Constants.ONLINE_USERS_LIST); 162 | lblUser.setFont(new Font(Constants.FONT_SONG, 0, 12)); 163 | lblUser.setForeground(Color.YELLOW); 164 | 165 | lstUser = new JList(); 166 | lstUser.setFont(new Font(Constants.FONT_SONG, 0, 12)); 167 | lstUser.setVisibleRowCount(17); 168 | lstUser.setFixedCellWidth(180); 169 | lstUser.setFixedCellHeight(18); 170 | 171 | spUser = new JScrollPane(); 172 | spUser.setBackground(Color.cyan); 173 | spUser.setFont(new Font(Constants.FONT_SONG, 0, 12)); 174 | spUser.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 175 | spUser.getViewport().setView(lstUser); 176 | 177 | pnlUser.setBounds(50, 5, 300, 400); 178 | lblUser.setBounds(50, 10, 100, 30); 179 | spUser.setBounds(50, 35, 200, 360); 180 | 181 | pnlUser.add(lblUser); 182 | pnlUser.add(spUser); 183 | 184 | // 软件信息 185 | pnlAbout = new JPanel(); 186 | pnlAbout.setLayout(null); 187 | pnlAbout.setBackground(new Color(52, 130, 203)); 188 | pnlAbout.setFont(new Font(Constants.FONT_SONG, 0, 14)); 189 | 190 | lblVersionNo = new JLabel(Constants.VERSION_INFO); 191 | lblVersionNo.setFont(new Font(Constants.FONT_SONG, 0, 14)); 192 | lblVersionNo.setForeground(Color.YELLOW); 193 | 194 | lblAbout = new JLabel(); 195 | lblAbout.setFont(new Font(Constants.FONT_SONG, 0, 14)); 196 | lblAbout.setText(Constants.APP_ABOUT_INFO); 197 | lblAbout.setForeground(Color.YELLOW); 198 | 199 | lblVersionNo.setBounds(110, 5, 100, 30); 200 | lblAbout.setBounds(110, 35, 400, 50); 201 | 202 | pnlAbout.add(lblVersionNo); 203 | pnlAbout.add(lblAbout); 204 | 205 | // 主标签面板 206 | tpServer = new JTabbedPane(JTabbedPane.TOP); 207 | tpServer.setBackground(Color.CYAN); 208 | tpServer.setFont(new Font(Constants.FONT_SONG, 0, 14)); 209 | 210 | tpServer.add(Constants.SERVER_ADMIN, pnlServer); 211 | tpServer.add(Constants.ONLINE_USERS, pnlUser); 212 | tpServer.add(Constants.ABOUT_APP, pnlAbout); 213 | 214 | this.getContentPane().add(tpServer); 215 | setVisible(true); 216 | } 217 | 218 | protected void closeServer() { 219 | this.dispose(); 220 | } 221 | 222 | protected void saveLog() { 223 | try { 224 | FileOutputStream fileoutput = new FileOutputStream("log.txt", true); 225 | String temp = taLog.getText(); 226 | fileoutput.write(temp.getBytes()); 227 | fileoutput.close(); 228 | JOptionPane.showMessageDialog(null, Constants.SAVE_LOG_FILE); 229 | } catch (Exception e) { 230 | System.out.println(e); 231 | } 232 | } 233 | 234 | public void actionPerformed(ActionEvent evt) { 235 | } 236 | 237 | /** 238 | * 服务器窗口 239 | * 240 | * @param args 241 | */ 242 | public static void main(String[] args) { 243 | new ServerFrame(); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/com/longluo/webchat/ServerProcess.java: -------------------------------------------------------------------------------- 1 | package com.longluo.webchat; 2 | 3 | import java.io.*; 4 | import java.net.*; 5 | import java.util.*; 6 | 7 | /** 8 | * 类名:ServerProcess 9 | * 描述:接收到客户端socket发来的信息后进行解析、处理、转发。 10 | * 11 | * @author longluo 12 | */ 13 | public class ServerProcess extends Thread { 14 | private Socket socket = null;// 定义客户端套接字 15 | 16 | private BufferedReader in;// 定义输入流 17 | private PrintWriter out;// 定义输出流 18 | 19 | private static Vector onlineUser = new Vector(10, 5); 20 | private static Vector socketUser = new Vector(10, 5); 21 | 22 | private String strReceive, strKey; 23 | private StringTokenizer st; 24 | 25 | private final String USERLIST_FILE = System.getProperty("user.dir") + "_user.txt"; // 设定存放用户信息的文件 26 | private ServerFrame sFrame = null; 27 | 28 | public ServerProcess(Socket client, ServerFrame frame) throws IOException { 29 | socket = client; 30 | sFrame = frame; 31 | 32 | in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 客户端接收 33 | out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 34 | socket.getOutputStream())), true);// 客户端输出 35 | this.start(); 36 | } 37 | 38 | public void run() { 39 | try { 40 | while (true) { 41 | strReceive = in.readLine();// 从服务器端接收一条信息后拆分、解析,并执行相应操作 42 | st = new StringTokenizer(strReceive, "|"); 43 | strKey = st.nextToken(); 44 | if (strKey.equals("login")) { 45 | login(); 46 | } else if (strKey.equals("talk")) { 47 | talk(); 48 | } else if (strKey.equals("init")) { 49 | freshClientsOnline(); 50 | } else if (strKey.equals("reg")) { 51 | register(); 52 | } 53 | } 54 | } catch (IOException e) { // 用户关闭客户端造成此异常,关闭该用户套接字。 55 | String leaveUser = closeSocket(); 56 | Date t = new Date(); 57 | log(Constants.USER + leaveUser + Constants.HAD_EXIT + Constants.EXIT_TIME + t.toLocaleString()); 58 | try { 59 | freshClientsOnline(); 60 | } catch (IOException e1) { 61 | e1.printStackTrace(); 62 | } 63 | System.out.println("[SYSTEM] " + leaveUser + " leave chatroom!"); 64 | sendAll("talk|>>>" + leaveUser + Constants.LEAVE_ROOM); 65 | } 66 | } 67 | 68 | /** 69 | * 判断是否有该注册用户 70 | */ 71 | private boolean isExistUser(String name) { 72 | String strRead; 73 | try { 74 | FileInputStream inputfile = new FileInputStream(USERLIST_FILE); 75 | DataInputStream inputdata = new DataInputStream(inputfile); 76 | while ((strRead = inputdata.readLine()) != null) { 77 | StringTokenizer stUser = new StringTokenizer(strRead, "|"); 78 | if (stUser.nextToken().equals(name)) { 79 | return true; 80 | } 81 | } 82 | } catch (FileNotFoundException fn) { 83 | System.out.println("[ERROR] User File has not exist!" + fn); 84 | out.println("warning|读写文件时出错!"); 85 | } catch (IOException ie) { 86 | System.out.println("[ERROR] " + ie); 87 | out.println("warning|读写文件时出错!"); 88 | } 89 | return false; 90 | } 91 | 92 | /** 93 | * 判断用户的用户名密码是否正确 94 | * 95 | * @param name 96 | * @param password 97 | * @return 98 | */ 99 | private boolean isUserLogin(String name, String password) { 100 | String strRead; 101 | try { 102 | FileInputStream inputfile = new FileInputStream(USERLIST_FILE); 103 | DataInputStream inputdata = new DataInputStream(inputfile); 104 | while ((strRead = inputdata.readLine()) != null) { 105 | if (strRead.equals(name + "|" + password)) { 106 | return true; 107 | } 108 | } 109 | } catch (FileNotFoundException fn) { 110 | System.out.println("[ERROR] User File has not exist!" + fn); 111 | out.println("warning|读写文件时出错!"); 112 | } catch (IOException ie) { 113 | System.out.println("[ERROR] " + ie); 114 | out.println("warning|读写文件时出错!"); 115 | } 116 | return false; 117 | } 118 | 119 | /** 120 | * 用户注册 121 | * 122 | * @throws IOException 123 | */ 124 | private void register() throws IOException { 125 | String name = st.nextToken(); // 得到用户名称 126 | String password = st.nextToken().trim();// 得到用户密码 127 | Date t = new Date(); 128 | 129 | if (isExistUser(name)) { 130 | System.out.println("[ERROR] " + name + " Register fail!"); 131 | out.println("warning|该用户已存在,请改名!"); 132 | } else { 133 | RandomAccessFile userFile = new RandomAccessFile(USERLIST_FILE, 134 | "rw"); 135 | userFile.seek(userFile.length()); // 在文件尾部加入新用户信息 136 | userFile.writeBytes(name + "|" + password + "\r\n"); 137 | log(Constants.USER + name + "注册成功, " + "注册时间:" + t.toLocaleString()); 138 | userLoginSuccess(name); // 自动登陆聊天室 139 | } 140 | } 141 | 142 | /** 143 | * 用户登陆(从登陆框直接登陆) 144 | * 145 | * @throws IOException 146 | */ 147 | private void login() throws IOException { 148 | String name = st.nextToken(); // 得到用户名称 149 | String password = st.nextToken().trim();// 得到用户密码 150 | boolean succeed = false; 151 | Date t = new Date(); 152 | 153 | log(Constants.USER + name + "正在登陆..." + "\n" + "密码 :" + password + "\n" + "端口 " 154 | + socket + t.toLocaleString()); 155 | System.out.println("[USER LOGIN] " + name + ":" + password + ":" 156 | + socket); 157 | 158 | for (int i = 0; i < onlineUser.size(); i++) { 159 | if (onlineUser.elementAt(i).equals(name)) { 160 | System.out.println("[ERROR] " + name + " is logined!"); 161 | out.println("warning|" + name + "已经登陆聊天室"); 162 | } 163 | } 164 | if (isUserLogin(name, password)) { // 判断用户名和密码 165 | userLoginSuccess(name); 166 | succeed = true; 167 | } 168 | if (!succeed) { 169 | out.println("warning|" + name + "登陆失败,请检查您的输入!"); 170 | log(Constants.USER + name + "登陆失败!" + t.toLocaleString()); 171 | System.out.println("[SYSTEM] " + name + " login fail!"); 172 | } 173 | } 174 | 175 | /** 176 | * 用户登陆 177 | * 178 | * @param name 179 | * @throws IOException 180 | */ 181 | private void userLoginSuccess(String name) throws IOException { 182 | Date t = new Date(); 183 | out.println("login|succeed"); 184 | sendAll("online|" + name); 185 | 186 | onlineUser.addElement(name); 187 | socketUser.addElement(socket); 188 | 189 | log(Constants.USER + name + "登录成功," + "登录时间:" + t.toLocaleString()); 190 | 191 | freshClientsOnline(); 192 | sendAll("talk|>>>欢迎 " + name + " 进来与我们一起交谈!"); 193 | System.out.println("[SYSTEM] " + name + " login succeed!"); 194 | } 195 | 196 | /** 197 | * 聊天信息处理 198 | * 199 | * @throws IOException 200 | */ 201 | private void talk() throws IOException { 202 | String strTalkInfo = st.nextToken(); // 得到聊天内容; 203 | String strSender = st.nextToken(); // 得到发消息人 204 | String strReceiver = st.nextToken(); // 得到接收人 205 | System.out.println("[TALK_" + strReceiver + "] " + strTalkInfo); 206 | Socket socketSend; 207 | PrintWriter outSend; 208 | Date t = new Date(); 209 | 210 | // 得到当前时间 211 | GregorianCalendar calendar = new GregorianCalendar(); 212 | String strTime = "(" + calendar.get(Calendar.HOUR) + ":" 213 | + calendar.get(Calendar.MINUTE) + ":" 214 | + calendar.get(Calendar.SECOND) + ")"; 215 | strTalkInfo += strTime; 216 | 217 | log(Constants.USER + strSender + "对 " + strReceiver + "说:" + strTalkInfo 218 | + t.toLocaleString()); 219 | 220 | if (strReceiver.equals("All")) { 221 | sendAll("talk|" + strSender + " 对所有人说:" + strTalkInfo); 222 | } else { 223 | if (strSender.equals(strReceiver)) { 224 | out.println("talk|>>>不能自言自语哦!"); 225 | } else { 226 | for (int i = 0; i < onlineUser.size(); i++) { 227 | if (strReceiver.equals(onlineUser.elementAt(i))) { 228 | socketSend = (Socket) socketUser.elementAt(i); 229 | outSend = new PrintWriter(new BufferedWriter( 230 | new OutputStreamWriter( 231 | socketSend.getOutputStream())), true); 232 | outSend.println("talk|" + strSender + " 对你说:" 233 | + strTalkInfo); 234 | } else if (strSender.equals(onlineUser.elementAt(i))) { 235 | socketSend = (Socket) socketUser.elementAt(i); 236 | outSend = new PrintWriter(new BufferedWriter( 237 | new OutputStreamWriter( 238 | socketSend.getOutputStream())), true); 239 | outSend.println("talk|你对 " + strReceiver + "说:" 240 | + strTalkInfo); 241 | } 242 | } 243 | } 244 | } 245 | } 246 | 247 | /** 248 | * 在线用户列表 249 | * 250 | * @throws IOException 251 | */ 252 | private void freshClientsOnline() throws IOException { 253 | String strOnline = "online"; 254 | String[] userList = new String[20]; 255 | String useName = null; 256 | 257 | for (int i = 0; i < onlineUser.size(); i++) { 258 | strOnline += "|" + onlineUser.elementAt(i); 259 | useName = " " + onlineUser.elementAt(i); 260 | userList[i] = useName; 261 | } 262 | 263 | sFrame.txtNumber.setText("" + onlineUser.size()); 264 | sFrame.lstUser.setListData(userList); 265 | System.out.println(strOnline); 266 | out.println(strOnline); 267 | } 268 | 269 | /** 270 | * 信息群发 271 | * 272 | * @param strSend 273 | */ 274 | private void sendAll(String strSend) { 275 | Socket socketSend; 276 | PrintWriter outSend; 277 | try { 278 | for (int i = 0; i < socketUser.size(); i++) { 279 | socketSend = (Socket) socketUser.elementAt(i); 280 | outSend = new PrintWriter(new BufferedWriter( 281 | new OutputStreamWriter(socketSend.getOutputStream())), 282 | true); 283 | outSend.println(strSend); 284 | } 285 | } catch (IOException e) { 286 | System.out.println("[ERROR] send all fail!"); 287 | } 288 | } 289 | 290 | public void log(String log) { 291 | String newlog = sFrame.taLog.getText() + "\n" + log; 292 | sFrame.taLog.setText(newlog); 293 | } 294 | 295 | /** 296 | * 关闭套接字,并将用户信息从在线列表中删除 297 | * 298 | * @return 299 | */ 300 | private String closeSocket() { 301 | String strUser = ""; 302 | for (int i = 0; i < socketUser.size(); i++) { 303 | if (socket.equals((Socket) socketUser.elementAt(i))) { 304 | strUser = onlineUser.elementAt(i).toString(); 305 | socketUser.removeElementAt(i); 306 | onlineUser.removeElementAt(i); 307 | try { 308 | freshClientsOnline(); 309 | } catch (IOException e) { 310 | e.printStackTrace(); 311 | } 312 | sendAll("remove|" + strUser); 313 | } 314 | } 315 | try { 316 | in.close(); 317 | out.close(); 318 | socket.close(); 319 | } catch (IOException e) { 320 | System.out.println("[ERROR] " + e); 321 | } 322 | 323 | return strUser; 324 | } 325 | } --------------------------------------------------------------------------------