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