2013-02-10 09:23:41Morris
[JAVA][Socket] 聊天室試寫
![](https://photox.pchome.com.tw/s08/morris821028/2/136045925517/)
demo.jpg
當要寫成網頁版本的時候改成 JApplet, 但是在瀏覽器關閉的時候, 並不能向 Server 發出關閉的訊息。
因此設置一個類似 Heart 的部分在 Client 端。
還沒解決的部分
1. 重複的 ID 進入聊天室。
2. 由於設置 Heart, 有個環節未能解決, 導致在某個時段登入會直接踢出。
MServer.java // Server 端
import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.io.BufferedInputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.IOException; import java.util.ArrayList; import java.util.StringTokenizer; import java.util.Timer; import java.util.TimerTask; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentListener; import java.awt.event.AdjustmentEvent; import java.lang.Thread; public class MServer { private JFrame frame; private JTextArea contentArea; private JTextField txt_msg; private JTextField txt_port; private JTextField txt_max; private JButton btn_start; private JButton btn_stop; private JButton btn_send; private JPanel northPanel; private JPanel southPanel; private JScrollPane rightPanel; private JScrollPane leftPanel; private JSplitPane centerSplit; // 分頁窗格 private JList userList; private DefaultListModel listModel; private ServerSocket serverSocket; private ServerThread serverThread; // 自行定義的 class private ArrayList<ClientThread> clients; private final int ServerPort = 8765; private boolean isStart = false; public MServer() { frame = new JFrame("MServer - Beta"); contentArea = new JTextArea(); contentArea.setEditable(false); contentArea.setBackground(Color.black); contentArea.setForeground(Color.green); txt_msg = new JTextField(); txt_max = new JTextField("7"); txt_port = new JTextField("2047"); btn_start = new JButton("On Server"); btn_stop = new JButton("Off Server"); btn_send = new JButton("↵ "); btn_stop.setEnabled(false); listModel = new DefaultListModel(); userList = new JList(listModel); southPanel = new JPanel(new BorderLayout()); southPanel.setBorder(new TitledBorder("")); southPanel.add(txt_msg, BorderLayout.CENTER); southPanel.add(btn_send, BorderLayout.EAST); leftPanel = new JScrollPane(userList); leftPanel.setBorder(new TitledBorder("Who's here")); rightPanel = new JScrollPane(contentArea); rightPanel.setBorder(new TitledBorder("Chat Area")); rightPanel.getVerticalScrollBar().addAdjustmentListener( new AdjustmentListener() { int cnt = 0; public void adjustmentValueChanged(AdjustmentEvent e) { if(cnt == 3) { JScrollBar sb = rightPanel.getVerticalScrollBar(); sb.setValue(sb.getMaximum()); cnt = 0; } cnt++; } }); // 自動置底 centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel); centerSplit.setDividerLocation(100); northPanel = new JPanel(); northPanel.setLayout(new GridLayout(1, 6)); northPanel.add(new JLabel("User limit")); northPanel.add(txt_max); northPanel.add(new JLabel("Port")); northPanel.add(txt_port); northPanel.add(btn_start); northPanel.add(btn_stop); northPanel.setBorder(new TitledBorder("Setting")); frame.setLayout(new BorderLayout()); frame.add(northPanel, BorderLayout.NORTH); frame.add(southPanel, BorderLayout.SOUTH); frame.add(centerSplit, BorderLayout.CENTER); frame.setSize(800, 600); int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width; int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height; frame.setLocation((screen_width - frame.getWidth()) / 2, (screen_height - frame.getHeight()) / 2); frame.setVisible(true); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { if (isStart) closeServer(); System.exit(0); } }); txt_msg.addActionListener(new ActionListener() { // enter touch public void actionPerformed(ActionEvent e) { send(); } }); btn_send.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { send(); } }); btn_start.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (isStart) { JOptionPane.showMessageDialog(frame, "Server has opened", "Error120", JOptionPane.ERROR_MESSAGE); return; } int max, port; try { max = Integer.parseInt(txt_max.getText()); port = Integer.parseInt(txt_port.getText()); if (max < 0) throw new Exception("max <= 0"); if (port <= 0) throw new Exception("port <= 0"); openServer(max, port); if (isStart) { contentArea.append("System> Server open ! \r\n"); btn_start.setEnabled(false); txt_max.setEditable(false); txt_port.setEditable(false); btn_stop.setEnabled(true); } } catch (Exception e3) { JOptionPane.showMessageDialog(frame, "", "Error136", JOptionPane.ERROR_MESSAGE); } } }); btn_stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (!isStart) { JOptionPane.showMessageDialog(frame, "Server has closed", "Error147", JOptionPane.ERROR_MESSAGE); return; } try { closeServer(); if (!isStart) { btn_start.setEnabled(true); txt_max.setEditable(true); txt_port.setEditable(true); btn_stop.setEnabled(false); contentArea.append("System> Server close ! \r\n"); } } catch (Exception e3) { JOptionPane.showMessageDialog(frame, "", "Error158", JOptionPane.ERROR_MESSAGE); } } }); } public void send() { if (!isStart) { JOptionPane.showMessageDialog(frame, "Server closed", "Error120", JOptionPane.ERROR_MESSAGE); return; } if (clients.size() == 0) { JOptionPane.showMessageDialog(frame, "No user", "Error124", JOptionPane.ERROR_MESSAGE); return; } String msg = txt_msg.getText(); if (msg == null || msg.equals("")) { JOptionPane.showMessageDialog(frame, "msg null", "Error128", JOptionPane.ERROR_MESSAGE); return; } msg = "System> " + msg + "\r\n"; sendServerMessage("SYSTEM@" + msg); contentArea.append(msg); txt_msg.setText(null); } public void sendServerMessage(String msg) { for (int i = clients.size() - 1; i >= 0; i--) { clients.get(i).getWriter().println(msg); clients.get(i).getWriter().flush(); } } public void openServer(int max, int port) throws java.net.BindException { try { clients = new ArrayList<ClientThread>(); serverSocket = new ServerSocket(port); serverThread = new ServerThread(serverSocket, max); serverThread.start(); isStart = true; } catch (java.net.BindException e) { isStart = false; throw new java.net.BindException( "port used, please use another port"); } catch (Exception e2) { e2.printStackTrace(); isStart = false; throw new java.net.BindException("unknown exception"); } } @SuppressWarnings("deprecation") public void closeServer() { try { if (serverThread != null) serverThread.stop(); for (int i = 0; i < clients.size(); i++) { ClientThread tmp = clients.get(i); tmp.socket.setSoTimeout(3000); tmp.getWriter().println("CLOSE"); tmp.getWriter().flush(); tmp.stop(); tmp.bin.close(); tmp.wout.close(); tmp.socket.close(); clients.remove(i); i--; } if (serverSocket != null) serverSocket.close(); listModel.removeAllElements(); isStart = false; } catch (Exception e) { e.printStackTrace(); isStart = true; } } class ServerThread extends Thread { private ServerSocket serverSocket; private int max; public ServerThread(ServerSocket serverSocket, int max) { this.serverSocket = serverSocket; this.max = max; } public void run() { while (true) { try { Socket socket = serverSocket.accept(); if (clients.size() == max) { BufferedReader r = new BufferedReader( new InputStreamReader(socket.getInputStream())); PrintWriter w = new PrintWriter( socket.getOutputStream()); String inf = r.readLine(); StringTokenizer st = new StringTokenizer(inf, "@"); User user = new User(st.nextToken(), st.nextToken()); w.println("MAX@Sorry, " + user.getName() + "! Server Busy. Waiting... \r\n"); w.flush(); r.close(); socket.close(); continue; } ClientThread client = new ClientThread(socket); client.start(); clients.add(client); listModel.addElement(client.getUser().getName()); String msg; msg = "System> " + client.getUser().getName() + " has entered the room.\r\n"; contentArea.append(msg); sendServerMessage("ADD@" + client.getUser().getName()); sendServerMessage("SYSTEM@" + msg); } catch (Exception e) { e.printStackTrace(); } } } } class ClientThread extends Thread { private Socket socket; private BufferedReader bin; private PrintWriter wout; private User user; private boolean closeflag = false; Timer timer1; Timer timer2; public ClientThread(Socket socket) { try { this.socket = socket; this.socket.setSoTimeout(3000); bin = new BufferedReader(new InputStreamReader( socket.getInputStream())); wout = new PrintWriter(socket.getOutputStream()); String inf = bin.readLine(); StringTokenizer st = new StringTokenizer(inf, "@"); user = new User(st.nextToken(), st.nextToken()); for (int i = clients.size() - 1; i >= 0; i--) wout.println("ADD@" + clients.get(i).getUser().getName()); } catch (Exception e) { e.printStackTrace(); } timer1 = new Timer(); timer1.schedule(this.new Heart1(), 100, 1000); timer2 = new Timer(); timer2.schedule(this.new Heart2(), 100, 10000); } boolean alive = true, runflag = true; class Heart1 extends TimerTask { public void run() { if(runflag == true) { alive = false; runflag = false; } } } class Heart2 extends TimerTask { public void run() { if(alive == false) { try { closeConnection(); } catch(Exception e) { } } runflag = true; } } public void closeConnection() throws IOException { String msg; msg = "System> " + user.getName() + " has left the room.\r\n"; contentArea.append(msg); sendServerMessage("SYSTEM@" + msg); msg = "DELETE@" + user.getName(); sendServerMessage(msg); bin.close(); wout.close(); socket.close(); listModel.removeElement(user.getName()); for (int i = 0; i < clients.size(); i++) { if (clients.get(i).getUser().getName().equals(user.getName())) { ClientThread tmp = clients.get(i); clients.remove(i); break; } } System.out.println(clients.size()); closeflag = true; timer1.cancel(); timer2.cancel(); } public void run() { String msg = null; while (!closeflag) { try { msg = bin.readLine(); if (msg.equals("CLOSE")) { closeConnection(); break; } else if(msg.equals("A@")) { alive = true; } else { dispatcherMessage(msg); } } catch (SocketException se) { try { closeConnection(); } catch (Exception ee) { } break; } catch (Exception e) { } } } public void dispatcherMessage(String msg) { StringTokenizer st = new StringTokenizer(msg, "@"); String source = st.nextToken(); String ch = st.nextToken(); String content = st.nextToken(); while (st.hasMoreTokens()) content += st.nextToken(); msg = source + "> " + content + "\r\n"; if (ch.equals("ALL")) { contentArea.append(msg); sendServerMessage("MSG@" + msg); } } public BufferedReader getReader() { return bin; } public PrintWriter getWriter() { return wout; } public User getUser() { return user; } } public static void main(String[] args) { new MServer(); } }
MClient.java //顧客端
import java.net.Socket; import java.io.BufferedInputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.IOException; import java.util.ArrayList; import java.util.StringTokenizer; import java.util.Map; import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.Thread; public class MClient { private JFrame frame; private JTextArea contentArea; private JTextField txt_msg; private JTextField txt_port; private JTextField txt_host; private JTextField txt_name; private JButton btn_start; private JButton btn_stop; private JButton btn_send; private JPanel northPanel; private JPanel southPanel; private JScrollPane rightPanel; private JScrollPane leftPanel; private JSplitPane centerSplit; // 分頁窗格 private JList userList; private DefaultListModel listModel; private Socket socket; private PrintWriter writer; private BufferedReader reader; private boolean isConnected = false; private MessageThread messageThread; private Map<String, User> onlineUsers = new HashMap<String, User>(); public MClient() { frame = new JFrame("MClient - Beta"); contentArea = new JTextArea(); contentArea.setEditable(false); contentArea.setBackground(Color.black); contentArea.setForeground(Color.green); txt_msg = new JTextField(); txt_port = new JTextField("2047"); txt_host = new JTextField("127.0.0.1"); txt_name = new JTextField("guest"); btn_start = new JButton("LogIn"); btn_stop = new JButton("LogOut"); btn_send = new JButton("↵ "); btn_stop.setEnabled(false); listModel = new DefaultListModel(); userList = new JList(listModel); southPanel = new JPanel(new BorderLayout()); southPanel.setBorder(new TitledBorder("")); southPanel.add(txt_msg, BorderLayout.CENTER); southPanel.add(btn_send, BorderLayout.EAST); leftPanel = new JScrollPane(userList); leftPanel.setBorder(new TitledBorder("Who's here")); rightPanel = new JScrollPane(contentArea); rightPanel.setBorder(new TitledBorder("Chat Area")); rightPanel.getVerticalScrollBar().addAdjustmentListener( new AdjustmentListener() { int cnt = 0; public void adjustmentValueChanged(AdjustmentEvent e) { if(cnt == 3) { JScrollBar sb = rightPanel.getVerticalScrollBar(); sb.setValue(sb.getMaximum()); cnt = 0; } cnt++; } }); // 自動置底 centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel); centerSplit.setDividerLocation(100); northPanel = new JPanel(); northPanel.setLayout(new GridLayout(1, 7)); northPanel.add(new JLabel("Port")); northPanel.add(txt_port); northPanel.add(new JLabel("Host IP")); northPanel.add(txt_host); northPanel.add(new JLabel("Username")); northPanel.add(txt_name); northPanel.add(btn_start); northPanel.add(btn_stop); northPanel.setBorder(new TitledBorder("Setting")); frame.setLayout(new BorderLayout()); frame.add(northPanel, BorderLayout.NORTH); frame.add(southPanel, BorderLayout.SOUTH); frame.add(centerSplit, BorderLayout.CENTER); frame.setSize(800, 600); int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width; int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height; frame.setLocation((screen_width - frame.getWidth()) / 2, (screen_height - frame.getHeight()) / 2); frame.setVisible(true); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { if (isConnected) closeConnection(); System.exit(0); } }); txt_msg.addActionListener(new ActionListener() { // enter touch public void actionPerformed(ActionEvent e) { send(); } }); btn_send.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { send(); } }); btn_start.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { /*if (isConnected) { ��������������������JOptionPane.showMessageDialog(frame, "has opened", ����������������������������"Error120", JOptionPane.ERROR_MESSAGE); ��������������������return; ����������������}*/ int port; try { port = Integer.parseInt(txt_port.getText()); if (port <= 0) throw new Exception("port <= 0"); if (txt_name.getText().equals("")) throw new Exception("name too short"); openConnection(port, txt_host.getText(), txt_name.getText()); if (isConnected) { contentArea.append("System> open connection! \r\n"); btn_start.setEnabled(false); txt_port.setEditable(false); txt_name.setEditable(false); txt_host.setEditable(false); btn_stop.setEnabled(true); } else { } } catch (Exception e3) { JOptionPane.showMessageDialog(frame, "", "Error136", JOptionPane.ERROR_MESSAGE); } } }); btn_stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { /*if (!isConnected) { ��������������������JOptionPane.showMessageDialog(frame, "has closed", ����������������������������"Error147", JOptionPane.ERROR_MESSAGE); ��������������������return; ����������������}*/ try { closeConnection(); btn_start.setEnabled(true); txt_port.setEditable(true); txt_name.setEditable(true); txt_host.setEditable(true); btn_stop.setEnabled(false); contentArea.append("System> close connection! \r\n"); } catch (Exception e3) { JOptionPane.showMessageDialog(frame, "", "Error158", JOptionPane.ERROR_MESSAGE); } } }); Timer timer = new Timer(); timer.schedule(this.new Heart(), 100, 3000); } public void send() { if (!isConnected) { JOptionPane.showMessageDialog(frame, "hasn't connect.", "Error", JOptionPane.ERROR_MESSAGE); return; } String msg = txt_msg.getText(); txt_msg.setText(""); if (msg == null || msg.equals("")) { JOptionPane.showMessageDialog(frame, "msg null", "Error128", JOptionPane.ERROR_MESSAGE); return; } sendMessage(txt_name.getText() + "@ALL@" + msg); } public void sendMessage(String msg) { if(writer != null) { writer.println(msg); writer.flush(); } } public void openConnection(int port, String hostip, String name) { try { socket = new Socket(hostip, port); writer = new PrintWriter(socket.getOutputStream()); reader = new BufferedReader(new InputStreamReader( socket.getInputStream())); sendMessage(name + "@" + socket.getLocalAddress().toString()); isConnected = true; messageThread = new MessageThread(reader, contentArea); messageThread.start(); } catch (Exception e) { contentArea.append("Error> Connection fails.\r\n"); isConnected = false; } } public void closeConnection() { try { sendMessage("CLOSE"); if (reader != null) reader.close(); if (writer != null) writer.close(); if (socket != null) socket.close(); isConnected = false; messageThread.stop(); listModel.removeAllElements(); } catch (Exception e) { e.printStackTrace(); isConnected = true; } } class MessageThread extends Thread { private BufferedReader reader; private JTextArea contentArea; public MessageThread(BufferedReader reader, JTextArea contentArea) { this.reader = reader; this.contentArea = contentArea; } public void run() { String msg = ""; while(true) { try { msg = reader.readLine(); StringTokenizer st = new StringTokenizer(msg, "@"); if(msg == null || msg.equals("")) continue; String cmd = st.nextToken(); if(cmd.equals("CLOSE")) { contentArea.append("System> Server close!\r\n"); closeConnection(); } else if(cmd.equals("ADD")) { // userlist String name = st.nextToken(); listModel.addElement(name); } else if(cmd.equals("DELETE")) { // userlist String name = st.nextToken(); onlineUsers.remove(name); listModel.removeElement(name); } else if(cmd.equals("MSG")) { // other perople contentArea.append(st.nextToken() + "\r\n"); } else if(cmd.equals("SYSTEM")) { // system contentArea.append(st.nextToken() + "\r\n"); } } catch(Exception e) { e.printStackTrace(); } } } } class Heart extends TimerTask { public void run() { sendMessage("A@"); System.out.println("A@"); } } public static void main(String[] args) { new MClient(); } }
額外的 User.java
public class User { private String name; private String ip; public User(String name, String ip) { this.name = name; this.ip = ip; } public String getName() { return name; } public String getIp() { return ip; } public void setName(String name) { this.name = name; } public void setIp(String ip) { this.ip = ip; } }
下一篇:[JAVA][作業] 球
你好 想問遺下
如果外部的人要如何連到我的server?