客户端无法在Java的套接字应用程序中正常工作

时间:2019-10-29 14:11:19

标签: java sockets

我正在尝试使一个简单的聊天应用程序在我的本地主机上运行。 它在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 :(得分:0)

再次阅读代码后,它偶然会在第一个实例上运行。

让我们遍历代码以查看正在发生的事情:

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

处理接收到的数据的唯一方法是将其添加到gui中。
其他客户将不会看到此消息。
您必须将数据发送给所有其他客户端。这就要求您保留所有已连接客户端的列表。

这就是事情变得复杂的地方:
您拥有一个以上的共享可变状态,可以由多个线程访问。
这意味着您必须使用同步。

好吧,让我们这样做:

  • 向您的服务器类添加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();
        }
    }
    
  • 即将完成。现在,客户实际上需要收听他们收到的消息。我留给你做。基本上与您之前在服务器中所做的相同。