* Чат сървър и клиент с GUI
Публикувано на 17 декември 2013 в раздел ПИК3 Java.
В това упражнение беше показано как може да се изгради графичен интерфейс за чат клиент приложението от предишното упражнение. Използва се Swing, като се използват различни контроли - JLabel, JTextArea, JScrollPane, JTextField и JButton. Освен допълнителния клас (ClientGUI) има промени по другите два - в ClientThread всички System.out.println са променени така, че да обновяват графичните контроли, а в основния клас Client e премахната функционалността за четене на съобщение от клавиатурата чрез конзолата (тази дейност е прехвърлена към действието на бутона). Промени по сървъра няма и той продължава да си работи в конзолен режим. Демонстрирани са два често използвани Layouts - FlowLayout и BorderLayout - за разполагане на контролите. Не на последно място - използва се Event Dispatch Thread (EDT) чрез SwingUtilities.invokeLater(...), за да се прави синхронизирано обновяване на информацията по графичния интерфейс.
1. Сървър
chatserver.java
package chatserver; import java.io.IOException; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; import java.net.ServerSocket; import java.util.Vector; public class chatserver{ static Vector<User> users = new Vector<User>(10); static int port = 1111; public static void main(String[] args){ // Starting server ServerSocket servSock; try{ servSock = new ServerSocket(port); } catch(IOException e){ System.err.println("Can't start server"); return; } System.out.println("Server started"); //Accepting new users while(true){ try{ Socket newConnection = servSock.accept(); User u = new User(newConnection); u.start(); } catch(IOException e){ System.err.println("ERR: Can't connect to user: "+e.getMessage()); } } } synchronized static void removeUser(User u){ for(int i=0; i<users.size(); i++){ if(users.get(i).equals(u)){ users.remove(i); System.out.println("User removed"); break; } } } synchronized static boolean usernameIsFree(String username){ boolean result = true; for(User u: users){ if(u.username.equals(username)){ result = false; break; } } return result; } synchronized static void sendToAll(String message) throws IOException{ for(User u: users){ u.send(message); } } } class User extends Thread{ String username; Socket mySocket; DataInputStream in; DataOutputStream out; public User(Socket s) throws IOException{ this.mySocket = s; this.in = new DataInputStream(s.getInputStream()); this.out = new DataOutputStream(s.getOutputStream()); } public void run(){ try{ out.writeUTF("SRV: Please reply with your username"); this.username = in.readUTF(); if(!chatserver.usernameIsFree(this.username)){ out.writeUTF("SRV: Username is taken"); } else{ out.writeUTF("SRV: Welcome to our chat"); chatserver.users.add(this); this.startUserChat(); } } catch(IOException e){ System.err.println("ERR: User died. Reason: "+e.getMessage()); } finally{ chatserver.removeUser(this); try{ if(this.in!=null) in.close(); if(this.out!=null) out.close(); if(this.mySocket!=null) mySocket.close(); } catch(IOException e){ System.err.println("ERR: Can't close socket: "+e.getMessage()); } } } private void startUserChat() throws IOException{ String message; do{ message = this.in.readUTF(); chatserver.sendToAll(this.username+" says: "+message); } while(!message.equalsIgnoreCase("exit")); } void send(String message) throws IOException{ out.writeUTF(message); } }
2. Клиент
chatclient.java
package chatclient; import java.util.Scanner; import java.io.IOException; import java.net.Socket; import java.io.DataOutputStream; import java.io.DataInputStream; import javax.swing.SwingUtilities; import java.awt.FlowLayout; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JButton; public class chatclient{ final static String host = "localhost"; final static int port = 1111; final static ClientThread c = new ClientThread(chatclient.host, chatclient.port); public static void main(String[] args){ SwingUtilities.invokeLater(new ClientGUI()); } } class ClientThread extends Thread{ private Socket socket; private DataOutputStream out; private DataInputStream in; public ClientThread(String host, int port){ try { this.socket = new Socket(host, port); ClientGUI.setStatusText("Connected!"); this.in = new DataInputStream(this.socket.getInputStream()); this.out = new DataOutputStream(this.socket.getOutputStream()); this.start(); } catch(IOException e){ ClientGUI.setStatusText("Can't connect to server"); ClientGUI.disableInputAndButton(); this.close(); } } public void run(){ try{ String message; do{ message = this.in.readUTF(); ClientGUI.appendChatText(message); }while(!message.equalsIgnoreCase("exit")); ClientGUI.setStatusText("Disconnected"); } catch(IOException e){ ClientGUI.setStatusText("Connection lost"); ClientGUI.disableInputAndButton(); } finally{ this.close(); } } void sendMessage(String message){ try{ this.out.writeUTF(message); this.out.flush(); } catch(IOException e){ ClientGUI.setStatusText("Can't send message"); ClientGUI.disableInputAndButton(); this.close(); } } void close(){ try{ if(this.in!=null) in.close(); if(this.out!=null) out.close(); if(this.socket!=null) socket.close(); } catch(IOException e){ ClientGUI.setStatusText("Can't close socket: "+e.getMessage()); } } } class ClientGUI extends JFrame implements Runnable{ // For the system messages private static JPanel panel1 = new JPanel(new FlowLayout()); private static JLabel status = new JLabel("Connecting to server..."); // For the textbox private static JPanel panel2 = new JPanel(new BorderLayout()); private static JTextArea chatText = new JTextArea(); private static JScrollPane chatTextPane = new JScrollPane(chatText, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); // For writing and sending messages private static JPanel panel3 = new JPanel(new FlowLayout()); private static JTextField userInput = new JTextField(20); private static JButton sendButton = new JButton("Send"); public void run(){ // We are ready to go! this.configFrame(); this.configPanel1(); this.configPanel2(); this.configPanel3(); this.setVisible(true); } private void configFrame(){ this.setLayout(new BorderLayout()); this.setTitle("My Chat"); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.add(panel1, BorderLayout.NORTH); this.add(panel2, BorderLayout.CENTER); this.add(panel3, BorderLayout.SOUTH); this.pack(); this.setSize(400, 300); } private void configPanel1(){ // Adding the status to panel1 ClientGUI.panel1.add(this.status); } private void configPanel2(){ // Configuring and adding the textbox to panel2 ClientGUI.chatText.setLineWrap(true); ClientGUI.chatText.setEditable(false); ClientGUI.chatText.setRows(10); ClientGUI.panel2.add(ClientGUI.chatTextPane, BorderLayout.CENTER); } private void configPanel3(){ // Adding the input field to panel3 ClientGUI.panel3.add(userInput); // Adding the SEND button to panel3 sendButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { String text = ClientGUI.userInput.getText(); if(text != null && !text.isEmpty()){ chatclient.c.sendMessage(text); if(text.equalsIgnoreCase("exit")){ chatclient.c.interrupt(); ClientGUI.disableInputAndButton(); ClientGUI.appendChatText("Bye bye!\n"); } } ClientGUI.userInput.setText(""); } }); sendButton.setToolTipText("Send"); panel3.getRootPane().setDefaultButton(sendButton); ClientGUI.panel3.add(sendButton); } static void autoScrollTextBox(){ ClientGUI.chatText.setCaretPosition( ClientGUI.chatText.getDocument().getLength() ); } static void setStatusText(final String s){ SwingUtilities.invokeLater(new Runnable(){ public void run(){ ClientGUI.status.setText(s); } }); } static void appendChatText(final String s){ SwingUtilities.invokeLater(new Runnable(){ public void run(){ ClientGUI.chatText.append(s+'\n'); ClientGUI.autoScrollTextBox(); } }); } static void disableInputAndButton(){ SwingUtilities.invokeLater(new Runnable(){ public void run(){ ClientGUI.userInput.setEnabled(false); ClientGUI.sendButton.setEnabled(false); } }); } }
Допълнителна задача: Направете така, че първоначално текущите контроли да са изключени, а клиентът да добавя host и port в два допълнителни JTextField. Свързването към сървъра да се извърши с допълнителен Connect бутон. След като Connect бъде натиснат, ако свързването е успешно, контролите се активират, а "Connect" бутона да стане "Disconnect". При повторно настикане (т.е. натисканена Disconnect) да се подава команда "exit" към сървъра и отново всичко да се връща в първоначалното си състояние.
Здравейте, искам да Ви попитам, как да прекъсвам връзката след като клиентът получи отговор на заявката си към сървъра и при въвеждане на нова команда, връзката да се установи отново? Примерът - 28 вариант от последното домашно.
Поздрави :)
Затваряш стария сокет, после отваряш нов сокет.
Клиентът трябва да се свърже точно на този порт, на който сървъра слуша.
Защо когато променя порта на сървъра и клиента, програмата не работи- изписва ми can't connect to server? От друга страна на порт 1111 си работи... В мен някъде ли е проблемът?
Да, използвам един и същи порт, но не винаги се получава.