Java Socket实现多人聊天室---swing做UI标签:socket聊天javaexception服务器string2011-08-31 15:06 20679人阅读评论(25) 收藏举报分类:java(21)版权声明:本文为博主原创文章,未经博主允许不得转载。
今天翻硬盘的workspace发现一个Java Socket实现多人聊天室的源码,不记得是什么时候的事情了,貌似不是我写的。
但写得还不错,至少算个有模有样的聊天室工具。
我简单的修改了一下,拿出来跟大家分享一下,仅供参考。
界面是用swing写的,还不懒,简约大方。
有图有真相:正如上图所示,这个程序分为服务器端和客户端,说白了就是两个main class,用eclipse 直接运行之。
聊天室的设计思想是:在局域网下,利用socket进行连接通信,当服务器端启动的时候,利用Thread线程不停的等待客户端的链接;当有客户端开启连接的时候,服务器端通过IO流反馈“上线用户”信息给客户端,客户端也使用线程不停的接收服务器的信息,从而实现多人在线聊天功能。
程序中有三个类,分别Server.java(服务器端)、Client(客户端)、User.java(javabean)。
代码如下:Server.java(服务器端):[html]view plaincopy1.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.i mport java.io.IOException;11.i mport java.io.InputStreamReader;12.i mport java.io.PrintWriter;13.i mport .BindException;14.i mport .ServerSocket;15.i mport .Socket;16.i mport java.util.ArrayList;17.i mport java.util.StringTokenizer;18.19.i mport javax.swing.DefaultListModel;20.i mport javax.swing.JButton;21.i mport javax.swing.JFrame;22.i mport javax.swing.JLabel;23.i mport javax.swing.JList;24.i mport javax.swing.JOptionPane;25.i mport javax.swing.JPanel;26.i mport javax.swing.JScrollPane;27.i mport javax.swing.JSplitPane;28.i mport javax.swing.JTextArea;29.i mport javax.swing.JTextField;30.i mport javax.swing.border.TitledBorder;31.32.p ublic 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();erList = 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().getScr eenSize());//设置全屏132. int screen_width = Toolkit.getDefaultToolkit().get ScreenSize().width;133. int screen_height = Toolkit.getDefaultToolkit().ge tScreenSize().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.get Text());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.g etText());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 + ",端口:" + port191. + "\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, e xc.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 BufferedRea der(300. new InputStreamReader(sock et.getInputStream()));301. PrintWriter w = new PrintWriter(so cket302. .getOutputStream()); 303. // 接收客户端的基本用户信息304. String inf = r.readLine();305. StringTokenizer st = new StringTok enizer(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().ge tName()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 InputStrea mReader(socket354. .getInputStream()));355.writer = new PrintWriter(socket.getOutputS tream());356. // 接收客户端的基本用户信息357. String inf = reader.readLine();358. StringTokenizer st = new StringTokenizer(i nf, "@");er = new User(st.nextToken(), st.nextTok en());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() + "/" + clients368. .get(i).getUser().getIp())369. + "@";370. }371. writer.println("USERLIST@" + clients.s ize() + "@" + 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() {// 不断接收客户端的消息,进行处理。