1

Тема: Простенький чат

Вирішив написати простий (елементарний) чат на джаві. Так, як з сокетами, ще не мав справи, вирішив задачу розбити на таку послідовність:
1. Чат-сервер (консольний)
2. Чат-сервер з GUI
3. Чат-клієнт
Наразі написав код для консольного чат-серверу, буду дуже-вдячний за критику:

package main;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.Scanner;
import java.util.ArrayList;

import java.io.PrintWriter;
import java.io.IOException;

import java.net.ServerSocket;
import java.net.Socket;

//Aborts (stop) the application in command line (cmd) Ctrl+C

public class Server {

    private final int PORT;
    private final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private int uniqueId = 0;
    private ArrayList<ClientHandler> clientsList = new ArrayList<ClientHandler>();
    private boolean keepGoing = true;

    public Server(int port) {
        PORT = port;
    }

    public void start() {
        try (ServerSocket serverSocket = new ServerSocket(PORT);) {
            while (keepGoing) {
                serverDisplay("Server waiting for client on port " + PORT
                        + "...");
                Socket clientSocket = serverSocket.accept();
                ClientHandler clientHandler = new ClientHandler(clientSocket);
                Thread thread = new Thread(clientHandler);
                clientsList.add(clientHandler);
                thread.start();
                serverDisplay(clientHandler.name + " just connected.");
                serverDisplay("On the server, " + clientsList.size()
                        + " client(s).");
            }
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void serverDisplay(String msg) {
        System.out.println(DATE_FORMAT.format(new Date()) + " " + msg);
    }

    private synchronized void tellEveryone(String msg) {
        for (int i = clientsList.size(); --i >= 0;) {
            clientsList.get(i).writer.println(DATE_FORMAT.format(new Date())
                    + " " + msg);
        }
    }

    private synchronized void removeClient(int id) {
        for (int i = 0; i < clientsList.size(); i++) {
            ClientHandler client = clientsList.get(i);
            if (client.id == id) {
                clientsList.remove(i);
                return;
            }
        }
    }

    // For the GUI to stop the servser
    public void stopServer() {
        keepGoing = false;
        for (int i = 0; i < clientsList.size(); i++) {
            ClientHandler client = clientsList.get(i);
            client.writer.close();
            try {
                client.socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            serverDisplay(client.name + " disconnected.");
            tellEveryone(client.name + " disconnected.");
        }
        serverDisplay("Server is stopped.");
    }

    public static void main(String[] arr) {
        Server server = new Server(5000);
        server.start();
    }

    public class ClientHandler implements Runnable {

        private int id;
        private String name;
        private Socket socket;
        private PrintWriter writer;
        private Scanner read;

        public ClientHandler(Socket socket) {
            id = ++uniqueId;
            this.socket = socket;
            name = socket.getInetAddress().getHostName();
            try {
                writer = new PrintWriter(socket.getOutputStream(), true);
                read = new Scanner(socket.getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            writer.println("Hello " + name + "!");
            tellEveryone(name + " just connected.");
            boolean keepGoing = true;
            while (keepGoing && read.hasNextLine()) {
                String message = read.nextLine().trim();
                if (message.length() > 0) {
                    if (message.equals("EXIT")) {
                        keepGoing = false;
                        writer.println("Bye-bye " + name + "!");
                    } else {
                        tellEveryone("<" + name + "> " + message);
                    }
                }
            }
            removeClient(id);
            close();
        }

        private void close() {
            writer.close();
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            serverDisplay(name + " disconnected.");
            tellEveryone(name + " disconnected.");
            serverDisplay("On the server, " + clientsList.size()
                    + " client(s).");
            serverDisplay("Server waiting for client on port " + PORT + "...");
        }
    }
}

2 Востаннє редагувалося fed_lviv (09.06.2015 14:21:14)

Re: Простенький чат

Пункт 2 виконав, звичайно для цього трошки підкорегував класс Server + в даному класі зробив ще деякі зміни. Ось, що маю на даний момент:



Server.java

package gui;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.Scanner;
import java.util.ArrayList;

import java.io.PrintWriter;
import java.io.IOException;

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    private static final String NEW_LINE = System.getProperty("line.separator");
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private final int port;
    private int uniqueId = 0;
    private ArrayList<ClientHandler> clientsList = new ArrayList<ClientHandler>();
    private boolean keepGoing = true;
    private GUI frame;

    public Server(int port, GUI frame) {
        this.port = port;
        this.frame = frame;
    }

    public void start() {
        try (ServerSocket serverSocket = new ServerSocket(port);) {
            serverDisplayMsg("***SERVER STARTING!***");
            while (keepGoing) {
                serverDisplayMsg("Server waiting for client on port " + port
                        + "...");
                Socket clientSocket = serverSocket.accept();
                if (keepGoing) {
                    ClientHandler clientHandler = new ClientHandler(
                            clientSocket);
                    Thread thread = new Thread(clientHandler);
                    clientsList.add(clientHandler);
                    frame.getListModel().addElement(clientHandler.name);
                    thread.start();
                    serverDisplayMsg(clientHandler.name + " just connected.");
                    serverDisplayMsg("On the server, " + clientsList.size()
                            + " client(s).");
                }
            }
            serverDisplayMsg("***SERVER IS STOPPED!***");
            for (int i = 0; i < clientsList.size(); i++) {
                ClientHandler client = clientsList.get(i);
                client.close();
            }
        } catch (IOException e) {
            serverDisplayErr(e.getMessage());
        }
    }

    private void serverDisplayMsg(String msg) {
        frame.getTxtMsg().append(
                DATE_FORMAT.format(new Date()) + " " + msg + NEW_LINE);
    }

    private void serverDisplayErr(String msg) {
        frame.getTxtErr().append(
                DATE_FORMAT.format(new Date()) + " " + msg + NEW_LINE);
    }

    private synchronized void tellEveryone(String msg) {
        for (int i = clientsList.size(); --i >= 0;) {
            clientsList.get(i).writer.println(DATE_FORMAT.format(new Date())
                    + " " + msg);
        }
    }

    private synchronized void removeClient(int id) {
        for (int i = 0; i < clientsList.size(); i++) {
            ClientHandler client = clientsList.get(i);
            if (client.id == id) {
                clientsList.remove(i);
                frame.getListModel().remove(i);
                return;
            }
        }
    }

    public void stopServer() {
        keepGoing = false;
        try {
            new Socket("localhost", port);
        } catch (IOException e) {
            serverDisplayErr(e.getMessage());
        }
    }

    public class ClientHandler implements Runnable {

        private int id;
        private String name;
        private Socket socket;
        private PrintWriter writer;
        private Scanner read;

        public ClientHandler(Socket socket) {
            id = ++uniqueId;
            this.socket = socket;
            name = socket.getInetAddress().getHostName();
            try {
                writer = new PrintWriter(socket.getOutputStream(), true);
                read = new Scanner(socket.getInputStream());
            } catch (IOException e) {
                serverDisplayErr(e.getMessage());
            }
        }

        @Override
        public void run() {
            writer.println("Hello " + name + "!");
            tellEveryone(name + " just connected.");
            boolean keepGoing = true;
            while (keepGoing && read.hasNextLine()) {
                String message = read.nextLine().trim();
                if (message.length() > 0) {
                    if (message.equals("EXIT")) {
                        keepGoing = false;
                    } else {
                        tellEveryone("<" + name + "> " + message);
                    }
                }
            }
            serverDisplayMsg(name + " disconnected.");
            tellEveryone(name + " disconnected.");
            writer.println("Bye-bye " + name + "!");
            removeClient(id);
            close();
            if (Server.this.keepGoing) {
                serverDisplayMsg("On the server, " + clientsList.size()
                        + " client(s).");
                serverDisplayMsg("Server waiting for client on port " + port
                        + "...");
            }
        }

        private void close() {
            writer.close();
            try {
                socket.close();
            } catch (IOException e) {
                serverDisplayErr(e.getMessage());
            }
        }
    }
}

GUI.java

package gui;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.net.InetAddress;
import java.net.UnknownHostException;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JPanel;
import javax.swing.JList;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.SwingUtilities;
import javax.swing.text.DefaultCaret;

public class GUI extends JFrame {
    private static final String NEW_LINE = System.getProperty("line.separator");
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private JTextField txtPort = new JTextField(" 5000 ", 4);
    private final int width = txtPort.getPreferredSize().width;
    private final int height = txtPort.getPreferredSize().height;
    private JTextArea txtMsg = new JTextArea();
    private JTextArea txtErr = new JTextArea();
    private DefaultListModel<String> listModel = new DefaultListModel<String>();
    private Server server;

    public GUI() {
        setTitle("ChatServer");
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                if (server != null) {
                    server.stopServer();
                }
                System.exit(0);
            }
        });
        createNorthPanel();
        createCenterPanel();
        createEastPanel();
        createSouthPanel();
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private void createNorthPanel() {
        JPanel panel = new JPanel();
        ((FlowLayout) panel.getLayout()).setHgap(10);
        panel.add(new JLabel("IP: "));
        JTextField txtIP = new JTextField(9);
        txtIP.setEditable(false);
        String ip = null;
        try {
            ip = " " + InetAddress.getLocalHost().getHostAddress() + " ";
        } catch (UnknownHostException e) {
            txtErr.append(DATE_FORMAT.format(new Date()) + " "
                    + "Unknown host exception." + NEW_LINE);
        }
        txtIP.setText(ip);
        panel.add(txtIP);
        panel.add(new JLabel("PORT: "));
        panel.add(txtPort);
        add(panel, BorderLayout.PAGE_START);
    }

    private void createCenterPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(2, 1));
        panel.add(createFormatPane(txtMsg, Color.GREEN, "Server messages:"));
        panel.add(createFormatPane(txtErr, Color.RED, "Server errors:"));
        add(panel, BorderLayout.CENTER);
    }

    private JScrollPane createFormatPane(JTextArea txtArr, Color colTxt,
            String title) {
        txtArr.setEditable(false);
        txtArr.setLineWrap(true);
        txtArr.setWrapStyleWord(true);
        txtArr.setBackground(Color.BLACK);
        txtArr.setForeground(colTxt);
        DefaultCaret caretArr = (DefaultCaret) txtArr.getCaret();
        caretArr.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        JScrollPane pane = new JScrollPane(txtArr);
        pane.setBorder(BorderFactory.createTitledBorder(title));
        pane.setPreferredSize(new Dimension(width * 10, height * 10));
        return pane;
    }

    private void createEastPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout());
        JScrollPane pane = new JScrollPane(new JList<String>(listModel));
        pane.setBorder(BorderFactory.createTitledBorder("Client list:"));
        panel.add(pane);
        panel.setPreferredSize(new Dimension(width * 4, 0));
        add(panel, BorderLayout.LINE_END);
    }

    private void createSouthPanel() {
        JPanel panel = new JPanel();
        panel.add(new JButton(new ButtonAction()));
        add(panel, BorderLayout.PAGE_END);
    }

    private int getPort() {
        return Integer.parseInt(txtPort.getText().trim());
    }

    public JTextArea getTxtMsg() {
        return txtMsg;
    }

    public JTextArea getTxtErr() {
        return txtMsg;
    }

    public DefaultListModel<String> getListModel() {
        return listModel;
    }

    public static void main(String arr[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GUI();
            }
        });
    }

    public class ButtonAction extends AbstractAction {

        public ButtonAction() {
            putValue(AbstractAction.NAME, "Start");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            switch (getValue(AbstractAction.NAME).toString()) {
            case "Start":
                try {
                    server = new Server(getPort(), GUI.this);
                    Thread thread = new Thread(new ServerRunning());
                    thread.start();
                    txtPort.setEditable(false);
                    putValue(AbstractAction.NAME, "Stop");
                    break;
                } catch (NumberFormatException err) {
                    txtErr.append(DATE_FORMAT.format(new Date()) + " "
                            + "Invalid port number." + NEW_LINE);
                    return;
                }
            case "Stop":
                server.stopServer();
                server = null;
                txtPort.setEditable(true);
                putValue(AbstractAction.NAME, "Start");
                break;
            }
        }
    }

    public class ServerRunning implements Runnable {

        @Override
        public void run() {
            server.start();
        }
    }
}
Post's attachments

1.jpg 65.13 kb, 234 downloads since 2015-06-09 

3 Востаннє редагувалося fed_lviv (12.06.2015 10:11:12)

Re: Простенький чат

Трошки підшаманив код. В класі Server зробив зміни, щоб ім'я користувач сама прописував, а не бралося по замовчуванню ім'я ПК. В класі GUI замінив JTextArea на JTextPane, щоб можна було файніше виводити повідомлення:
Server

package main;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.Scanner;
import java.util.ArrayList;

import java.io.PrintWriter;
import java.io.IOException;

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private final int port;
    private int uniqueId = 0;
    private ArrayList<ClientHandler> clientsList = new ArrayList<ClientHandler>();
    private boolean keepGoing = true;
    private GUI frame;

    public Server(int port, GUI frame) {
        this.port = port;
        this.frame = frame;
    }

    public void start() {
        try (ServerSocket serverSocket = new ServerSocket(port);) {
            serverDisplayMsg("***SERVER STARTING!***");
            while (keepGoing) {
                serverDisplayMsg("Server waiting for client on port " + port
                        + "...");
                Socket clientSocket = serverSocket.accept();
                if (keepGoing) {
                    ClientHandler clientHandler = new ClientHandler(
                            clientSocket);
                    Thread thread = new Thread(clientHandler);
                    clientsList.add(clientHandler);
                    frame.getListModel().addElement(clientHandler.name);
                    thread.start();
                    serverDisplayMsg(clientHandler.name + " just connected.");
                    serverDisplayMsg("On the server, " + clientsList.size()
                            + " client(s).");
                }
            }
            serverDisplayMsg("***SERVER IS STOPPED!***");
            for (int i = 0; i < clientsList.size(); i++) {
                ClientHandler client = clientsList.get(i);
                client.close();
            }
        } catch (IOException e) {
            serverDisplayErr(e.getMessage());
        }
    }

    private void serverDisplayMsg(String msg) {
        frame.appendMessage(frame.getTxtMsg(), DATE_FORMAT.format(new Date())
                + " " + msg);
    }

    private void serverDisplayErr(String msg) {
        frame.appendMessage(frame.getTxtErr(), DATE_FORMAT.format(new Date())
                + " " + msg);
    }

    private synchronized void tellEveryone(String msg) {
        for (int i = clientsList.size(); --i >= 0;) {
            clientsList.get(i).writer.println(DATE_FORMAT.format(new Date())
                    + " " + msg);
        }
    }

    private synchronized void removeClient(int id) {
        for (int i = 0; i < clientsList.size(); i++) {
            ClientHandler client = clientsList.get(i);
            if (client.id == id) {
                clientsList.remove(i);
                frame.getListModel().remove(i);
                return;
            }
        }
    }

    public void stopServer() {
        keepGoing = false;
        try {
            new Socket("localhost", port);
        } catch (IOException e) {
            serverDisplayErr(e.getMessage());
        }
    }

    public class ClientHandler implements Runnable {

        private int id;
        private String name;
        private Socket socket;
        private PrintWriter writer;
        private Scanner read;

        public ClientHandler(Socket socket) {
            id = ++uniqueId;
            this.socket = socket;
            try {
                writer = new PrintWriter(socket.getOutputStream(), true);
                read = new Scanner(socket.getInputStream());
                name = read.nextLine();
                writer.println("Hello " + name + "!");
            } catch (IOException e) {
                serverDisplayErr(e.getMessage());
            }
        }

        @Override
        public void run() {
            tellEveryone(name + " just connected.");
            boolean keepGoing = true;
            while (keepGoing && read.hasNextLine()) {
                String message = read.nextLine().trim();
                if (message.length() > 0) {
                    if (message.equals("EXIT")) {
                        keepGoing = false;
                    } else {
                        tellEveryone("<" + name + "> " + message);
                    }
                }
            }
            serverDisplayMsg(name + " disconnected.");
            tellEveryone(name + " disconnected.");
            writer.println("Bye-bye " + name + "!");
            removeClient(id);
            close();
            if (Server.this.keepGoing) {
                serverDisplayMsg("On the server, " + clientsList.size()
                        + " client(s).");
                serverDisplayMsg("Server waiting for client on port " + port
                        + "...");
            }
        }

        private void close() {
            writer.close();
            try {
                socket.close();
            } catch (IOException e) {
                serverDisplayErr(e.getMessage());
            }
        }
    }
}

GUI

package main;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.net.InetAddress;
import java.net.UnknownHostException;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JList;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.BadLocationException;

public class GUI extends JFrame {
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private static final String PATTERN_TIME = "[0-2][0-9]:[0-5][0-9]:[0-5][0-9]";
    private SimpleAttributeSet styleTime = new SimpleAttributeSet();
    private SimpleAttributeSet styleMsg = new SimpleAttributeSet();
    private JTextField txtPort = new JTextField(" 5000 ", 4);
    private final int width = txtPort.getPreferredSize().width;
    private final int height = txtPort.getPreferredSize().height;
    private JTextPane txtMsg = new JTextPane();
    private JTextPane txtErr = new JTextPane();
    private DefaultListModel<String> listModel = new DefaultListModel<String>();
    private Server server;

    public GUI() {
        setTitle("Chat Server");
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                if (server != null) {
                    server.stopServer();
                }
                System.exit(0);
            }
        });
        createStylesTextPane();
        createNorthPanel();
        createCenterPanel();
        createEastPanel();
        createSouthPanel();
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private void createStylesTextPane() {
        StyleConstants.setItalic(styleTime, true);
        StyleConstants.setFontSize(styleTime,
                StyleConstants.getFontSize(styleTime) - 1);
        StyleConstants.setBold(styleMsg, true);
        StyleConstants.setBackground(styleMsg, Color.GRAY.darker());
    }

    public void appendMessage(JTextPane pane, String msg) {
        try {
            Document doc = pane.getDocument();
            String time = null;
            if (msg.length() >= 8
                    && (time = msg.substring(0, 8)).matches(PATTERN_TIME)) {
                doc.insertString(doc.getLength(), time, styleTime);
                if (msg.indexOf(" ***") == 8 && msg.indexOf("***", 10) >= 10) {
                    doc.insertString(doc.getLength(), " ", null);
                    String txt = msg.substring(9);
                    doc.insertString(doc.getLength(), txt, styleMsg);
                } else {
                    String text = msg.substring(8);
                    doc.insertString(doc.getLength(), text, null);
                }
            } else {
                doc.insertString(doc.getLength(), msg, null);
            }
            doc.insertString(doc.getLength(), "\n", null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void createNorthPanel() {
        JPanel panel = new JPanel();
        ((FlowLayout) panel.getLayout()).setHgap(10);
        panel.add(new JLabel("IP: "));
        JTextField txtIP = new JTextField(9);
        txtIP.setEditable(false);
        String ip = null;
        try {
            ip = " " + InetAddress.getLocalHost().getHostAddress() + " ";
        } catch (UnknownHostException e) {
            appendMessage(txtErr, DATE_FORMAT.format(new Date()) + " "
                    + "Unknown host exception.");
        }
        txtIP.setText(ip);
        panel.add(txtIP);
        panel.add(new JLabel("PORT: "));
        panel.add(txtPort);
        add(panel, BorderLayout.PAGE_START);
    }

    private void createCenterPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(2, 1));
        panel.add(createFormatPane(txtMsg, Color.GREEN, "Server messages:"));
        panel.add(createFormatPane(txtErr, Color.RED, "Server errors:"));
        add(panel, BorderLayout.CENTER);
    }

    private JScrollPane createFormatPane(JTextPane txtPane, Color colTxt,
            String title) {
        txtPane.setEditable(false);
        txtPane.setBackground(Color.BLACK);
        txtPane.setForeground(colTxt);
        JScrollPane pane = new JScrollPane(txtPane);
        pane.setBorder(BorderFactory.createTitledBorder(title));
        pane.setPreferredSize(new Dimension(width * 10, height * 10));
        return pane;
    }

    private void createEastPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout());
        JScrollPane pane = new JScrollPane(new JList<String>(listModel));
        pane.setBorder(BorderFactory.createTitledBorder("Client list:"));
        panel.add(pane);
        panel.setPreferredSize(new Dimension(width * 4, 0));
        add(panel, BorderLayout.LINE_END);
    }

    private void createSouthPanel() {
        JPanel panel = new JPanel();
        panel.add(new JButton(new ButtonAction()));
        add(panel, BorderLayout.PAGE_END);
    }

    private int getPort() {
        return Integer.parseInt(txtPort.getText().trim());
    }

    public JTextPane getTxtMsg() {
        return txtMsg;
    }

    public JTextPane getTxtErr() {
        return txtMsg;
    }

    public DefaultListModel<String> getListModel() {
        return listModel;
    }

    public static void main(String arr[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GUI();
            }
        });
    }

    public class ButtonAction extends AbstractAction {

        public ButtonAction() {
            putValue(AbstractAction.NAME, "Start");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            switch (getValue(AbstractAction.NAME).toString()) {
            case "Start":
                try {
                    server = new Server(getPort(), GUI.this);
                    Thread thread = new Thread(new ServerRunning());
                    thread.start();
                    txtPort.setEditable(false);
                    putValue(AbstractAction.NAME, "Stop");
                    break;
                } catch (NumberFormatException err) {
                    appendMessage(txtErr, DATE_FORMAT.format(new Date()) + " "
                            + "Invalid port number.");
                    return;
                }
            case "Stop":
                server.stopServer();
                server = null;
                txtPort.setEditable(true);
                putValue(AbstractAction.NAME, "Start");
                break;
            }
        }
    }

    public class ServerRunning implements Runnable {

        @Override
        public void run() {
            server.start();
        }
    }
}
Post's attachments

1.jpg 72.99 kb, 249 downloads since 2015-06-12 

4 Востаннє редагувалося fed_lviv (18.06.2015 13:40:50)

Re: Простенький чат

Ось і "заключний" етап, клієнт:
Client:

package main;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.Scanner;

import java.io.PrintWriter;
import java.io.IOException;

import java.net.Socket;

public class Client {
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private Socket socket;
    private PrintWriter writer;
    private Scanner read;
    private boolean keepGoing = true;
    private GUI frame;

    public Client(String serverIP, int port, String name, GUI frame)
            throws IOException {
        this.frame = frame;
        try {
            socket = new Socket(serverIP, port);
        } catch (IOException e) {
            throw new IOException("Error connectiong to server: "
                    + e.getMessage());
        }
        try {
            writer = new PrintWriter(socket.getOutputStream(), true);
            read = new Scanner(socket.getInputStream());
        } catch (IOException e) {
            throw new IOException("Exception creating Input/Output streams: "
                    + e.getMessage());
        }
        writer.println(name);
        new Thread(new ServerListener()).start();
    }

    private void clientDisplayMsg(String msg) {
        frame.appendMessage(msg, frame.getTxtChat());
    }

    private void clientDisplayErr(String msg) {
        frame.appendMessage(DATE_FORMAT.format(new Date()) + " " + msg,
                frame.getTxtErr());
    }

    public void sendMessage(String msg) {
        if (msg.equals("EXIT")) {
            keepGoing = false;
        }
        writer.println(msg);
    }

    private void disconnect() {
        read.close();
        writer.close();
        try {
            socket.close();
        } catch (IOException e) {
            clientDisplayErr(e.getMessage());
        } finally {
            frame.disconnect();
        }
    }

    public class ServerListener implements Runnable {
        @Override
        public void run() {
            while (keepGoing && read.hasNextLine()) {
                clientDisplayMsg(read.nextLine());
            }
            disconnect();
        }
    }
}

GUI

package main;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.event.WindowEvent;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.KeyAdapter;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.BorderFactory;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.BadLocationException;

public class GUI extends JFrame {
    private static final String PATTERN_TIME = "[0-2][0-9]:[0-5][0-9]:[0-5][0-9]";
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private static final SimpleAttributeSet STYLE_TIME = new SimpleAttributeSet();
    private static final SimpleAttributeSet STYLE_LOG = new SimpleAttributeSet();
    private static final SimpleAttributeSet STYLE_LOG_MSG = new SimpleAttributeSet();
    private static final ImageIcon ICON = new ImageIcon(
            GUI.class.getResource("image/user.png"));
    private JTextField txtIP = new JTextField(" 192.168.1.94 ", 9);
    private JTextField txtPort = new JTextField(" 5000 ", 4);
    private JTextField txtLog = new JTextField(7);
    private final int width = txtIP.getPreferredSize().width;
    private final int height = txtIP.getPreferredSize().height;
    private JTextPane txtChat = new JTextPane();
    private JTextPane txtErr = new JTextPane();
    private JTextArea txtMsg = new JTextArea();
    private Client client;
    private ConnectAction connect = new ConnectAction();

    public GUI() {
        createStylesTextPane();
        createNorthPanel();
        createCenterPanel();
        createEastPanel();
        createSouthPanel();
        setListeners();
        setTitle("Chat");
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        pack();
        setLocationRelativeTo(null);
        txtLog.requestFocusInWindow();
        setVisible(true);
    }

    private void createStylesTextPane() {
        StyleConstants.setItalic(STYLE_TIME, true);
        StyleConstants.setFontSize(STYLE_TIME,
                StyleConstants.getFontSize(STYLE_TIME) - 1);
        StyleConstants.setForeground(STYLE_LOG, Color.YELLOW);
        StyleConstants.setBold(STYLE_LOG_MSG, true);
        StyleConstants.setForeground(STYLE_LOG_MSG, Color.MAGENTA);
    }

    private void createNorthPanel() {
        JPanel panel = new JPanel();
        ((FlowLayout) panel.getLayout()).setHgap(10);
        panel.add(new JLabel("Server ip: "));
        panel.add(txtIP);
        panel.add(new JLabel("Server port: "));
        panel.add(txtPort);
        panel.add(new JLabel("Login: "));
        panel.add(txtLog);
        panel.add(new JButton(connect));
        add(panel, BorderLayout.PAGE_START);
    }

    private void createCenterPanel() {
        add(createFormatPane(txtChat, Color.GREEN, "Chat:"));
    }

    private JScrollPane createFormatPane(JTextPane txtPane, Color colTxt,
            String title) {
        txtPane.setEditable(false);
        txtPane.setBackground(Color.BLACK);
        txtPane.setForeground(colTxt);
        JScrollPane pane = new JScrollPane(txtPane);
        pane.setBorder(BorderFactory.createTitledBorder(title));
        pane.setPreferredSize(new Dimension(width * 5, height * 5));
        return pane;
    }

    private void createEastPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        txtMsg.setEnabled(false);
        txtMsg.setLineWrap(true);
        txtMsg.setWrapStyleWord(true);
        JScrollPane pane = new JScrollPane(txtMsg);
        pane.setPreferredSize(new Dimension(width * 2, height * 10));
        panel.add(pane);
        panel.setBorder(BorderFactory.createTitledBorder("Your message:"));
        add(panel, BorderLayout.LINE_END);
    }

    private void createSouthPanel() {
        add(createFormatPane(txtErr, Color.RED, "Errors:"),
                BorderLayout.PAGE_END);
    }

    private void setListeners() {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                if (client != null) {
                    client.sendMessage("EXIT");
                }
                System.exit(0);
            }
        });
        txtMsg.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    e.consume();
                    String txt = txtMsg.getText().trim();
                    if (txt.length() > 0) {
                        client.sendMessage(txt);
                        txtMsg.setText("");
                        txtMsg.requestFocusInWindow();
                    }
                }
            }
        });
    }

    public void appendMessage(String string, JTextPane pane) {
        try {
            Document doc = pane.getDocument();
            String time = null;
            if (string.length() >= 8
                    && (time = string.substring(0, 8)).matches(PATTERN_TIME)) {
                doc.insertString(doc.getLength(), time, STYLE_TIME);
                int index;
                if (string.indexOf(" <") == 8
                        && (index = string.indexOf("> ") + 1) > 11) {
                    String log = string.substring(8, index);
                    doc.insertString(doc.getLength(), log, STYLE_LOG);
                    pane.setCaretPosition(doc.getLength());
                    pane.insertIcon(ICON);
                    String text = string.substring(index);
                    doc.insertString(doc.getLength(), text, STYLE_LOG_MSG);
                } else {
                    String text = string.substring(8);
                    doc.insertString(doc.getLength(), text, null);
                }
            } else {
                doc.insertString(doc.getLength(), string, null);
            }
            doc.insertString(doc.getLength(), "\n", null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private String getIP() throws Exception {
        String txt = txtIP.getText().trim();
        if (txt.length() == 0) {
            throw new Exception("Please enter IP address server.");
        } else {
            return txt;
        }
    }

    private int getPort() throws Exception {
        try {
            int x = Integer.parseInt(txtPort.getText().trim());
            return x;
        } catch (NumberFormatException err) {
            throw new NumberFormatException("Invalid port number.");
        }
    }

    private String getLogin() throws Exception {
        String txt = txtLog.getText().trim();
        if (txt.length() == 0) {
            throw new Exception("Please enter your login.");
        } else {
            return txt;
        }
    }

    public JTextPane getTxtChat() {
        return txtChat;
    }

    public JTextPane getTxtErr() {
        return txtErr;
    }

    public void disconnect() {
        connect.putValue(AbstractAction.NAME, "Connect");
        txtIP.setEnabled(true);
        txtPort.setEnabled(true);
        txtLog.setEnabled(true);
        txtMsg.setEnabled(false);
    }

    public static void main(String arr[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GUI();
            }
        });
    }

    public class ConnectAction extends AbstractAction {

        public ConnectAction() {
            putValue(AbstractAction.NAME, "Connect");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            switch (getValue(AbstractAction.NAME).toString()) {
            case "Connect":
                try {
                    client = new Client(getIP(), getPort(), getLogin(),
                            GUI.this);
                    txtIP.setEnabled(false);
                    txtPort.setEnabled(false);
                    txtLog.setEnabled(false);
                    txtMsg.setEnabled(true);
                    txtMsg.requestFocusInWindow();
                    putValue(AbstractAction.NAME, "Disconnect");
                } catch (Exception err) {
                    appendMessage(
                            DATE_FORMAT.format(new Date()) + " "
                                    + err.getMessage(), txtErr);
                    txtLog.requestFocusInWindow();
                    return;
                }
                break;
            case "Disconnect":
                client.sendMessage("EXIT");
                client = null;
                txtLog.requestFocusInWindow();
                break;
            }
        }
    }
}

Тільки, під час написання клієнта зрозумів, що посилати Stringи то не дуже цікаво. Тому планую дописати клас Message, сервер і клієнти будуть "спілкуватися" об'єктами класа Message

Post's attachments

1.jpg 59.26 kb, 247 downloads since 2015-06-18 

5 Востаннє редагувалося fed_lviv (01.07.2015 08:53:27)

Re: Простенький чат

Змінив спілкування в чаті Stringи на клас Message. Тако ж була проблема при зупинці сервера, не завжди закривалося з'єднання з клієнтами (проблема при ітерації списка клієнтів), замінив ArrayList на CopyOnWriteArrayList. Ще добавив шрифт для клієнта:
MESSAGE.JAVA

package main;

import java.io.Serializable;

public class Message implements Serializable {

    private static final long serialVersionUID = 6341796648975819075L;
    public static final int TEXT = 0;
    public static final int EXIT = 1;
    private int type;
    private String text;

    public Message(String text) {
        this(Message.TEXT, text);
    }

    public Message(int type, String text) {
        this.type = type;
        this.text = text;
    }

    public int getType() {
        return type;
    }

    public String getText() {
        return text;
    }
}

SERVER.JAVA

package main;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.concurrent.CopyOnWriteArrayList;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private final int port;
    private int uniqueId = 0;
    private CopyOnWriteArrayList<ClientHandler> clientsList = new CopyOnWriteArrayList<ClientHandler>();
    private boolean keepGoing = true;
    private GUI frame;

    public Server(int port, GUI frame) {
        this.port = port;
        this.frame = frame;
    }

    public void start() {
        try (ServerSocket serverSocket = new ServerSocket(port);) {
            serverDisplayMsg("***SERVER STARTING!***");
            while (keepGoing) {
                serverDisplayMsg("Server waiting for client on port " + port
                        + "...");
                Socket clientSocket = serverSocket.accept();
                if (keepGoing) {
                    ClientHandler clientHandler = new ClientHandler(
                            clientSocket);
                    Thread thread = new Thread(clientHandler);
                    clientsList.add(clientHandler);
                    frame.getListModel().addElement(clientHandler.name);
                    thread.start();
                    serverDisplayMsg(clientHandler.name + " just connected.");
                    serverDisplayMsg("On the server, " + clientsList.size()
                            + " client(s).");
                }
            }
            serverDisplayMsg("***SERVER IS STOPPED!***");
            for (ClientHandler client : clientsList) {
                client.close();
            }
        } catch (IOException e) {
            serverDisplayErr(e.getMessage());
        }
    }

    private void serverDisplayMsg(String msg) {
        frame.appendMessage(frame.getTxtMsg(), DATE_FORMAT.format(new Date())
                + " " + msg);
    }

    private void serverDisplayErr(String msg) {
        frame.appendMessage(frame.getTxtErr(), DATE_FORMAT.format(new Date())
                + " " + msg);
    }

    private synchronized void tellEveryone(String msg) {
        for (int i = clientsList.size(); --i >= 0;) {
            try {
                clientsList.get(i).writer.writeObject(new Message(DATE_FORMAT
                        .format(new Date()) + " " + msg));
            } catch (IOException e) {
                serverDisplayErr(e.getMessage());
            }
        }
    }

    private synchronized void removeClient(int id) {
        for (int i = 0; i < clientsList.size(); i++) {
            ClientHandler client = clientsList.get(i);
            if (client.id == id) {
                clientsList.remove(i);
                frame.getListModel().remove(i);
                return;
            }
        }
    }

    public void stopServer() {
        keepGoing = false;
        try {
            new Socket("localhost", port);
        } catch (IOException e) {
            serverDisplayErr(e.getMessage());
        }
    }

    public class ClientHandler implements Runnable {

        private int id;
        private String name;
        private Socket socket;
        private ObjectOutputStream writer;
        private ObjectInputStream read;

        public ClientHandler(Socket socket) {
            id = ++uniqueId;
            this.socket = socket;
            try {
                writer = new ObjectOutputStream(socket.getOutputStream());
                read = new ObjectInputStream(socket.getInputStream());
                name = ((Message) read.readObject()).getText();
                writer.writeObject(new Message("Hello " + name + "!"));
            } catch (ClassNotFoundException | IOException e) {
                serverDisplayErr(name + " " + e.getMessage());
            }
        }

        @Override
        public void run() {
            tellEveryone(name + " just connected.");
            boolean keepGoing = true;
            while (keepGoing) {
                Message msg;
                try {
                    msg = (Message) read.readObject();
                } catch (ClassNotFoundException | IOException e) {
                    if (Server.this.keepGoing) {
                        serverDisplayErr(name + " " + e.getMessage());
                    }
                    break;
                }
                switch (msg.getType()) {
                case Message.TEXT:
                    tellEveryone("<" + name + "> " + msg.getText());
                    break;
                case Message.EXIT:
                    keepGoing = false;
                    serverDisplayMsg(name + " disconnected.");
                    tellEveryone(name + " disconnected.");
                    try {
                        writer.writeObject(new Message("Bye-bye " + name + "!"));
                    } catch (IOException e) {
                        serverDisplayErr(name + " " + e.getMessage());
                    }
                    break;
                }
            }
            removeClient(id);
            close();
            if (Server.this.keepGoing) {
                serverDisplayMsg("On the server, " + clientsList.size()
                        + " client(s).");
                serverDisplayMsg("Server waiting for client on port " + port
                        + "...");
            }
        }

        private void close() {
            try {
                writer.close();
            } catch (IOException e1) {
                serverDisplayErr(name + " " + e1.getMessage());
            }
            try {
                read.close();
            } catch (IOException e2) {
                serverDisplayErr(name + " " + e2.getMessage());
            }
            try {
                socket.close();
            } catch (IOException e3) {
                serverDisplayErr(name + " " + e3.getMessage());
            }
        }
    }
}

GUI

package main;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.net.InetAddress;
import java.net.UnknownHostException;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JList;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.BadLocationException;

public class GUI extends JFrame {

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private static final String PATTERN_TIME = "[0-2][0-9]:[0-5][0-9]:[0-5][0-9]";
    private SimpleAttributeSet styleTime = new SimpleAttributeSet();
    private SimpleAttributeSet styleMsg = new SimpleAttributeSet();
    private JTextField txtPort = new JTextField(" 5000 ", 4);
    private final int width = txtPort.getPreferredSize().width;
    private final int height = txtPort.getPreferredSize().height;
    private JTextPane txtMsg = new JTextPane();
    private JTextPane txtErr = new JTextPane();
    private DefaultListModel<String> listModel = new DefaultListModel<String>();
    private Server server;

    public GUI() {
        setTitle("Chat Server");
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                if (server != null) {
                    server.stopServer();
                }
                System.exit(0);
            }
        });
        createStylesTextPane();
        createNorthPanel();
        createCenterPanel();
        createEastPanel();
        createSouthPanel();
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private void createStylesTextPane() {
        StyleConstants.setItalic(styleTime, true);
        StyleConstants.setFontSize(styleTime,
                StyleConstants.getFontSize(styleTime) - 1);
        StyleConstants.setBold(styleMsg, true);
        StyleConstants.setBackground(styleMsg, Color.GRAY.darker());
    }

    public void appendMessage(JTextPane pane, String msg) {
        try {
            Document doc = pane.getDocument();
            String time = null;
            if (msg.length() >= 8
                    && (time = msg.substring(0, 8)).matches(PATTERN_TIME)) {
                doc.insertString(doc.getLength(), time, styleTime);
                if (msg.indexOf(" ***") == 8 && msg.indexOf("***", 10) >= 10) {
                    doc.insertString(doc.getLength(), " ", null);
                    String txt = msg.substring(9);
                    doc.insertString(doc.getLength(), txt, styleMsg);
                } else {
                    String text = msg.substring(8);
                    doc.insertString(doc.getLength(), text, null);
                }
            } else {
                doc.insertString(doc.getLength(), msg, null);
            }
            doc.insertString(doc.getLength(), "\n", null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void createNorthPanel() {
        JPanel panel = new JPanel();
        ((FlowLayout) panel.getLayout()).setHgap(10);
        panel.add(new JLabel("IP: "));
        JTextField txtIP = new JTextField(9);
        txtIP.setEditable(false);
        String ip = null;
        try {
            ip = " " + InetAddress.getLocalHost().getHostAddress() + " ";
        } catch (UnknownHostException e) {
            appendMessage(txtErr, DATE_FORMAT.format(new Date()) + " "
                    + "Unknown host exception.");
        }
        txtIP.setText(ip);
        panel.add(txtIP);
        panel.add(new JLabel("PORT: "));
        panel.add(txtPort);
        add(panel, BorderLayout.PAGE_START);
    }

    private void createCenterPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(2, 1));
        panel.add(createFormatPane(txtMsg, Color.GREEN, "Server messages:"));
        panel.add(createFormatPane(txtErr, Color.RED, "Server errors:"));
        add(panel, BorderLayout.CENTER);
    }

    private JScrollPane createFormatPane(JTextPane txtPane, Color colTxt,
            String title) {
        txtPane.setEditable(false);
        txtPane.setBackground(Color.BLACK);
        txtPane.setForeground(colTxt);
        JScrollPane pane = new JScrollPane(txtPane);
        pane.setBorder(BorderFactory.createTitledBorder(title));
        pane.setPreferredSize(new Dimension(width * 10, height * 10));
        return pane;
    }

    private void createEastPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout());
        JScrollPane pane = new JScrollPane(new JList<String>(listModel));
        pane.setBorder(BorderFactory.createTitledBorder("Client list:"));
        panel.add(pane);
        panel.setPreferredSize(new Dimension(width * 4, 0));
        add(panel, BorderLayout.LINE_END);
    }

    private void createSouthPanel() {
        JPanel panel = new JPanel();
        panel.add(new JButton(new ButtonAction()));
        add(panel, BorderLayout.PAGE_END);
    }

    private int getPort() {
        return Integer.parseInt(txtPort.getText().trim());
    }

    public JTextPane getTxtMsg() {
        return txtMsg;
    }

    public JTextPane getTxtErr() {
        return txtErr;
    }

    public DefaultListModel<String> getListModel() {
        return listModel;
    }

    public static void main(String arr[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GUI();
            }
        });
    }

    public class ButtonAction extends AbstractAction {

        public ButtonAction() {
            putValue(AbstractAction.NAME, "Start");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            switch (getValue(AbstractAction.NAME).toString()) {
            case "Start":
                try {
                    txtMsg.setText("");
                    txtErr.setText("");
                    server = new Server(getPort(), GUI.this);
                    Thread thread = new Thread(new ServerRunning());
                    thread.start();
                    txtPort.setEditable(false);
                    putValue(AbstractAction.NAME, "Stop");
                    break;
                } catch (NumberFormatException err) {
                    appendMessage(txtErr, DATE_FORMAT.format(new Date()) + " "
                            + "Invalid port number.");
                    return;
                }
            case "Stop":
                server.stopServer();
                server = null;
                txtPort.setEditable(true);
                putValue(AbstractAction.NAME, "Start");
                break;
            }
        }
    }

    public class ServerRunning implements Runnable {

        @Override
        public void run() {
            server.start();
        }
    }
}

CLIENT.JAVA

package main;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

import java.net.Socket;

public class Client {

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private Socket socket;
    private ObjectOutputStream writer;
    private ObjectInputStream read;
    private GUI frame;

    public Client(String serverIP, int port, String name, GUI frame)
            throws IOException {
        this.frame = frame;
        try {
            socket = new Socket(serverIP, port);
        } catch (IOException e) {
            throw new IOException("Error connectiong to server: "
                    + e.getMessage());
        }
        try {
            writer = new ObjectOutputStream(socket.getOutputStream());
            read = new ObjectInputStream(socket.getInputStream());
        } catch (IOException e) {
            throw new IOException("Exception creating Input/Output streams: "
                    + e.getMessage());
        }
        writer.writeObject(new Message(name));
        new Thread(new ServerListener()).start();
    }

    private void clientDisplayMsg(String msg) {
        frame.appendMessage(msg, frame.getTxtChat());
    }

    private void clientDisplayErr(String msg) {
        frame.appendMessage(DATE_FORMAT.format(new Date()) + " " + msg,
                frame.getTxtErr());
    }

    public void sendMessage(Message msg) {
        try {
            writer.writeObject(msg);
        } catch (IOException e) {
            clientDisplayErr(e.getMessage());
        }
    }

    private void disconnect() {
        try {
            writer.close();
        } catch (IOException e1) {
            clientDisplayErr(e1.getMessage());
        }
        try {
            read.close();
        } catch (IOException e2) {
            clientDisplayErr(e2.getMessage());
        }
        try {
            socket.close();
        } catch (IOException e3) {
            clientDisplayErr(e3.getMessage());
        } finally {
            frame.disconnect();
        }
    }

    public class ServerListener implements Runnable {
        @Override
        public void run() {
            while (true) {
                Message msg;
                try {
                    msg = (Message) read.readObject();
                    switch (msg.getType()) {
                    case Message.TEXT:
                        clientDisplayMsg(msg.getText());
                        break;
                    }
                } catch (Exception e1) {
                    try {
                        if (e1 instanceof IOException
                                && !frame.getLastMessage().equals(
                                        "Bye-bye " + frame.getLogin() + "!")) {
                            clientDisplayMsg("Server has close the connection!");
                        }
                        disconnect();
                        break;
                    } catch (Exception e2) {
                        clientDisplayErr(e2.getMessage());
                        disconnect();
                        break;
                    }
                }
            }
        }
    }
}

GUI.JAVA

package main;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.io.IOException;

import java.net.URL;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.GraphicsEnvironment;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.event.WindowEvent;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.BorderFactory;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.BadLocationException;

public class GUI extends JFrame {

    private static final String PATTERN_TIME = "[0-2][0-9]:[0-5][0-9]:[0-5][0-9]";
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "HH:mm:ss");
    private static final SimpleAttributeSet STYLE_TIME = new SimpleAttributeSet();
    private static final SimpleAttributeSet STYLE_LOG = new SimpleAttributeSet();
    private static final SimpleAttributeSet STYLE_LOG_MSG = new SimpleAttributeSet();
    private static final SimpleAttributeSet STYLE_MSG = new SimpleAttributeSet();
    private static final ImageIcon ICON_TALK = new ImageIcon(
            GUI.class.getResource("files/talk.png"));
    private static final URL URL_FONT = GUI.class
            .getResource("files/v_CCcomicrazy.ttf");
    private static final KeyStroke KEY_SHIFT_ENTER = KeyStroke.getKeyStroke(
            KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK, true);
    private static final KeyStroke KEY_ENTER = KeyStroke.getKeyStroke("ENTER");
    private JTextField txtIP = new JTextField(" 192.168.1.94 ", 9);
    private JTextField txtPort = new JTextField(" 5000 ", 4);
    private JTextField txtLog = new JTextField(7);
    private final int width = txtIP.getPreferredSize().width;
    private final int height = txtIP.getPreferredSize().height;
    private JTextPane txtChat = new JTextPane();
    private JTextPane txtErr = new JTextPane();
    private JTextArea txtMsg = new JTextArea();
    private Client client;
    private ConnectAction conAct = new ConnectAction();

    public GUI() {
        createStylesTextPane();
        createNorthPanel();
        createCenterPanel();
        createEastPanel();
        createSouthPanel();
        setListeners();
        setTitle("Chat");
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        pack();
        setLocationRelativeTo(null);
        txtLog.requestFocusInWindow();
        setVisible(true);
    }

    private void createStylesTextPane() {
        StyleConstants.setItalic(STYLE_TIME, true);
        StyleConstants.setFontSize(STYLE_TIME,
                StyleConstants.getFontSize(STYLE_TIME) - 1);
        StyleConstants.setForeground(STYLE_LOG, Color.YELLOW);
        try {
            Font font = Font.createFont(Font.TRUETYPE_FONT,
                    URL_FONT.openStream());
            GraphicsEnvironment ge = GraphicsEnvironment
                    .getLocalGraphicsEnvironment();
            ge.registerFont(font);
        } catch (FontFormatException | IOException e) {
            e.printStackTrace();
        }
        StyleConstants.setFontFamily(STYLE_LOG_MSG, "v_CCcomicrazy");
        StyleConstants.setBold(STYLE_LOG_MSG, true);
        StyleConstants.setForeground(STYLE_LOG_MSG, Color.MAGENTA);
        StyleConstants.setFontSize(STYLE_MSG,
                StyleConstants.getFontSize(STYLE_MSG) + 1);
        StyleConstants.setBold(STYLE_MSG, true);
        StyleConstants.setUnderline(STYLE_MSG, true);
    }

    private void createNorthPanel() {
        JPanel panel = new JPanel();
        ((FlowLayout) panel.getLayout()).setHgap(10);
        panel.add(new JLabel("Server ip: "));
        panel.add(txtIP);
        panel.add(new JLabel("Server port: "));
        panel.add(txtPort);
        panel.add(new JLabel("Login: "));
        panel.add(txtLog);
        panel.add(new JButton(conAct));
        add(panel, BorderLayout.PAGE_START);
    }

    private void createCenterPanel() {
        add(createFormatPane(txtChat, Color.GREEN, "Chat:"));
    }

    private JScrollPane createFormatPane(JTextPane txtPane, Color colTxt,
            String title) {
        txtPane.setEditable(false);
        txtPane.setBackground(Color.BLACK);
        txtPane.setForeground(colTxt);
        JScrollPane pane = new JScrollPane(txtPane);
        pane.setBorder(BorderFactory.createTitledBorder(title));
        pane.setPreferredSize(new Dimension(width * 5, height * 5));
        return pane;
    }

    private void createEastPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        txtMsg.setEnabled(false);
        txtMsg.setLineWrap(true);
        txtMsg.setWrapStyleWord(true);
        JScrollPane pane = new JScrollPane(txtMsg);
        pane.setPreferredSize(new Dimension(width * 2, height * 10));
        panel.add(pane);
        panel.setBorder(BorderFactory.createTitledBorder("Your message:"));
        add(panel, BorderLayout.LINE_END);
    }

    private void createSouthPanel() {
        add(createFormatPane(txtErr, Color.RED, "Errors:"),
                BorderLayout.PAGE_END);
    }

    private void setListeners() {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent we) {
                if (client != null) {
                    client.sendMessage(new Message(Message.EXIT, ""));
                }
                System.exit(0);
            }
        });
        txtMsg.getInputMap().put(KEY_SHIFT_ENTER, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                txtMsg.append("\n");
            }
        });
        txtMsg.getInputMap().put(KEY_ENTER, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                String txt = txtMsg.getText().trim();
                if (txt.length() > 0) {
                    client.sendMessage(new Message(txt));
                    txtMsg.setText("");
                    txtMsg.requestFocusInWindow();
                }
            }
        });
    }

    public void appendMessage(String string, JTextPane pane) {
        try {
            Document doc = pane.getDocument();
            String time = null;
            if (string.length() >= 8
                    && (time = string.substring(0, 8)).matches(PATTERN_TIME)) {
                doc.insertString(doc.getLength(), time, STYLE_TIME);
                int index;
                if (string.indexOf(" <") == 8
                        && (index = string.indexOf("> ") + 1) > 11) {
                    String log = string.substring(8, index);
                    doc.insertString(doc.getLength(), log, STYLE_LOG);
                    pane.setCaretPosition(doc.getLength());
                    pane.insertIcon(ICON_TALK);
                    String text = string.substring(index);
                    doc.insertString(doc.getLength(), text, STYLE_LOG_MSG);
                } else {
                    String text = string.substring(8);
                    doc.insertString(doc.getLength(), text, null);
                }
            } else {
                doc.insertString(doc.getLength(), string, STYLE_MSG);
            }
            doc.insertString(doc.getLength(), "\n", null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private String getIP() throws Exception {
        String txt = txtIP.getText().trim();
        if (txt.length() == 0) {
            throw new Exception("Please enter IP address server.");
        } else {
            return txt;
        }
    }

    private int getPort() throws Exception {
        try {
            int x = Integer.parseInt(txtPort.getText().trim());
            return x;
        } catch (NumberFormatException err) {
            throw new NumberFormatException("Invalid port number.");
        }
    }

    public String getLogin() throws Exception {
        String txt = txtLog.getText().trim();
        if (txt.length() == 0) {
            throw new Exception("Please enter your login.");
        } else {
            return txt;
        }
    }

    public JTextPane getTxtChat() {
        return txtChat;
    }

    public JTextPane getTxtErr() {
        return txtErr;
    }

    public String getLastMessage() {
        Document doc = txtChat.getDocument();
        String msg = null;
        try {
            String text = doc.getText(0, txtChat.getDocument().getLength() - 1);
            int x = text.lastIndexOf('\n');
            msg = doc.getText(x, doc.getLength() - x).replaceAll("\n", "");
        } catch (BadLocationException e) {
            appendMessage(
                    DATE_FORMAT.format(new Date()) + " " + e.getMessage(),
                    txtErr);
        }
        return msg;
    }

    public void disconnect() {
        conAct.putValue(AbstractAction.NAME, "Connect");
        txtIP.setEnabled(true);
        txtPort.setEnabled(true);
        txtLog.setEnabled(true);
        txtMsg.setEnabled(false);
    }

    public static void main(String arr[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GUI();
            }
        });
    }

    public class ConnectAction extends AbstractAction {

        public ConnectAction() {
            putValue(AbstractAction.NAME, "Connect");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            switch (getValue(AbstractAction.NAME).toString()) {
            case "Connect":
                try {
                    txtChat.setText("");
                    txtErr.setText("");
                    client = new Client(getIP(), getPort(), getLogin(),
                            GUI.this);
                    txtIP.setEnabled(false);
                    txtPort.setEnabled(false);
                    txtLog.setEnabled(false);
                    txtMsg.setEnabled(true);
                    txtMsg.requestFocusInWindow();
                    putValue(AbstractAction.NAME, "Disconnect");
                } catch (Exception err) {
                    appendMessage(
                            DATE_FORMAT.format(new Date()) + " "
                                    + err.getMessage(), txtErr);
                    txtLog.requestFocusInWindow();
                    return;
                }
                break;
            case "Disconnect":
                client.sendMessage(new Message(Message.EXIT, ""));
                client = null;
                txtMsg.setText(null);
                txtLog.requestFocusInWindow();
                break;
            }
        }
    }
}
Post's attachments

2.png 23.16 kb, 282 downloads since 2015-07-01 

Подякували: bees1