Клиенты не будут работать в сокетном приложении на Java - PullRequest
0 голосов
/ 29 октября 2019

Я пытаюсь сделать так, чтобы простое приложение чата работало на моем локальном хосте. На CMD все работает нормально, но всякий раз, когда я пытаюсь построить его на GUI, ошибки не оставляют меня в покое. enter image description here

Видите? Первое открытое приложение работает, как и ожидалось, но когда я пытаюсь открыть другое приложение, второе открытое приложение отправляет данные в первое приложение.

gui

package program;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

public class gui
{
    private boolean basladi=false;
    Client client;
    server _server;
    JFrame pencere;
    JButton button;
    static JTextArea area;
    JTextField type;
    public gui(){

        pencere = new JFrame("oxChat");
        pencere.setSize(640,480);
        pencere.setLayout(null);
        button = new JButton("gönder");
        button.addActionListener( new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!basladi){
                    basladi=true;
                    client = new Client("127.0.0.1",4000);
                }else{
                    client.sendData(type.getText());
                }
            }
        });
        area = new JTextArea();
        type = new JTextField();
        pencere.add(type);
        pencere.add(area);
        pencere.add(button);
        area.setBounds(0,0,640,350);
        type.setBounds(0,370,640,25);
        button.setBounds(640/2-80/2,400,80,30);
        pencere.setVisible(true);
        pencere.setResizable(false);
        pencere.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        _server = new server();
        _server.start(4000);
    }

    public static void main(String[] args) throws IOException {
        gui app = new gui();
    }

}

клиент

package program;

import java.net.*;
import java.io.*;
import java.util.Scanner;

public class Client
{
    // initialize socket and input output streams
    private Socket socket        = null;
    private DataInputStream input = null;
    private DataOutputStream out     = null;

    // constructor to put ip address and port
    public Client(String address, int port)
    {
        // establish a connection
        try
        {
            socket = new Socket(address, port);
            System.out.println("Connected");

            // takes input from terminal
            input = new DataInputStream(socket.getInputStream());

            // sends output to the socket
            out = new DataOutputStream(socket.getOutputStream());
        }
        catch(UnknownHostException u)
        {
            System.out.println(u);
        }
        catch(IOException i)
        {
            System.out.println(i);
        }

    }

    void sendData(String data){
        try{
            out.writeUTF(data);
        }catch(IOException i)
        {
        }
    }
}

сервер

package program;

// A Java program for a Server
import java.net.*;
import java.io.*;

public class server
{
    private Socket socket;
    private ServerSocket server;
    public static   String data;
    // constructor with port
    public void start(int port){
        try {
            server = new ServerSocket(port);
            while(true){
                socket = server.accept();
                new Thread (new ConnectionHandler(socket)).start();
            }
        }catch(IOException i){

        }
    }
}

class ConnectionHandler extends Thread{
    gui app;
    private String data;
    private Socket       socket = null;
    private DataInputStream in   = null;
    private DataOutputStream out     = null;
    public ConnectionHandler(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        try
        {
            System.out.println("Waiting for a client ...");
            System.out.println("Client accepted");
            in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            out = new DataOutputStream(socket.getOutputStream());

            String line = "";

            // reads message from client until "Over" is sent
            while (!line.equals("Over"))
            {
                try
                {
                    line = in.readUTF();
                    app.area.append(line+"\n");
                }
                catch(IOException i)
                {
                    System.out.println(i);
                }
            }
            System.out.println("Closing connection");

            // close connection
            socket.close();
            in.close();
        }
        catch(IOException i)
        {
            System.out.println(i);
        }
    }
    public String getServerData(){
        return  data;
    }
}

1 Ответ

0 голосов
/ 29 октября 2019

После прочтения вашего кода, просто случайно он работает для первого экземпляра.

Давайте пройдемся по коду, чтобы увидеть, что происходит:

В gui.java, высоздайте новый сервер:

_server = new server();
_server.start(4000);

, который, в свою очередь, попытается прослушать данный порт:

server = new ServerSocket(port);

Это, очевидно, не будет работать для второго экземпляра, но вы ловитеполучим IOException и просто выбросим его:

    try {
        server = new ServerSocket(port);
        while(true) {
            socket = server.accept();
            new Thread(new ConnectionHandler(socket)).start();
        }
    } catch(IOException i) {
         // Please do something here
    }

Итак, у второго экземпляра не будет сервера, который перечисляет порт.
Не следует просто игнорировать исключение, ноэто другая проблема. Таким образом, у вас работает только один сервер. Что хорошо. Один сервер, много клиентов.

В вышеописанном методе есть другая проблема, заключающаяся в том, что этот метод никогда не вернется (за исключением случаев, когда он не может привязаться к порту).

Теперь, когда клиент отправляет вам данные, сервер получит их здесь:

while (!line.equals("Over")) {
    try {
        line = in.readUTF();
        app.area.append(line+"\n");
    } catch(IOException i) {
        System.out.println(i);
    }
}

Единственное, что вы делаете с полученными данными, это добавляете их в графический интерфейс.
Другие клиенты не увидят это сообщение.
Вы должны отправить данные всем другим клиентам. Что требует, чтобы вы держали список всех подключенных клиентов вокруг.

И в этот момент все усложняется:
У вас общее изменяемое состояние, к которому обращается более одного потока.
Что означаетВы должны использовать синхронизацию.

Хорошо, давайте сделаем это:

  • Добавьте List<ConnectionHandler> к вашему классу сервера:

    List<ConnectionHandler> clients = new ArrayList<>();
    
  • Ив качестве примера: блокировка:

    Object lock = new Object();
    
  • Затем нам нужно добавить любого нового подключенного клиента в этот список:

    socket = server.accept();
    ConnectionHandler client = new ConnectionHandler(this, socket)
    synchronized (lock) {
        clients.add(client);
    }
    new Thread(client).start();
    
  • Теперь нам просто нужен метод для распределения всех входящих сообщений в классе сервера:

    void distributeMessage(String message) {
        List<ConnectionHandler> clientsCopy;
        synchronized (lock) {
            clientsCopy = new ArrayList<>(clients);
        }
        for (ConnectionHandler client : clientsCopy) {
            client.sendMessage(message);
        }
    }
    
  • Теперь нам нужно изменить ConnectionHandler, и мы начнем с очистки полей:

    private Socket socket;
    private DataInputStream in;
    private DataOutputStream out;
    private server server;
    

    Это все необходимые нам поля.

  • Далее нам нужно изменить конструктор этого класса:

    public ConnectionHandler(server server, Socket socket) {
        this.server = server;
        this.socket = socket;
        this.in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
        this.out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
    }
    

    Всеполя должны быть инициализированы в конструкторе, если это возможно.

  • Затем мы должны добавить наш новый sendMessage(String message) метод:

    public void sendMessage(String message) {
        try {
            out.writeUTF(message);
            out.flush();
        } catch (IOException e) {
            // TODO: Here you HAVE to check if the connection was closed
            // And if it was closed, call a method in the server class to
            // remove this client.
            e.printStackTrace();
        }
    }
    
  • Почтисделанный. Теперь клиенты на самом деле должны слушать сообщение, которое они получают. Я оставляю это на ваше усмотрение. В основном это то же самое, что вы делали на своем сервере раньше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...