* Чат сървър и клиент с 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 си работи... В мен някъде ли е проблемът?
Да, използвам един и същи порт, но не винаги се получава.