24h購物| | PChome| 登入
2013-02-10 09:23:41| 人氣7,548| 回應1 | 上一篇 | 下一篇

[JAVA][Socket] 聊天室試寫

推薦 0 收藏 0 轉貼0 訂閱站台



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; } }

台長: Morris
人氣(7,548) | 回應(1)| 推薦 (0)| 收藏 (0)| 轉寄
全站分類: 不分類 | 個人分類: [學習]Java |
此分類下一篇:[JAVA][作業] 球
此分類上一篇:[JAVA][Thread] 試寫

Andy
你好 想問遺下
如果外部的人要如何連到我的server?
2015-11-12 23:04:04
版主回應
通常會遇到防火牆 port 80 沒有對外開放,才導致無法連入。沒有實體 IP 的連入方法,沒有實驗過。
2015-11-13 11:37:46
是 (若未登入"個人新聞台帳號"則看不到回覆唷!)
* 請輸入識別碼:
請輸入圖片中算式的結果(可能為0) 
(有*為必填)
TOP
詳全文