0

I have a problem with my Java program. I have this codes:

Host.java:

public class Host {
protected static void start(JFrame window) {
    ServerSocket server = null;
    try {
        server = new ServerSocket();
        SocketAddress addr = new InetSocketAddress(hostname, port);
        server.bind(addr);

        Socket socket = server.accept();

        window.setVisible(false);

        Thread thread = new Thread(new Incomming(socket.getInputStream()));
        thread.start();
        thread.join();

        socket.close();
    } catch (UnknownHostException e) {
        [...]
}
}

Incomming.java:

public class Incomming implements Runnable {
private DataInputStream is;

public Incomming(InputStream is) {
        MyFrame frame = new MyFrame();
    frame.setVisible(true);
    frame.pack();

    this.is = new DataInputStream(is);
}

public void run() {
    try {
        while(!Thread.currentThread().isInterrupted()) {
            int n = is.readInt();
            if(n == -1) {
                break;
            }
            byte[] b = new byte[n];
            is.readFully(b);
            [...] // working with bytes
        }
        System.out.println("Stream closed.");
    } catch(IOException e) {
        [...]
    }
}
}  

Client.java is very similar to Host.java, it uses Incomming.java for socket.getInputStream() too.

So the problem is: the client connects to the host, but when it should show on server side and also on client side the MyFrame window, it doesn't load it fully. And the close button of old JFrame windows (on both sides) doesn't do anything.

I tried to remove the line with thread.join(), and then the MyFrame window loads completely and close buttons work, but it throws me exception with socket closed message, so the client is no longer connected to the host.

How could I fix this problem? Thanks for replies.

4

3 回答 3

4
  1. Thread#join 将阻塞,直到线程死亡。这会阻止您关闭窗口,因为您正在阻止事件调度线程。有关更多详细信息,请参阅Swing 中的并发。
  2. accept是一个传入的套接字,启动一个新Thread的套接字来处理该套接字,然后立即关闭套接字(很可能在线程甚至有机会开始读取它之前)。相反,关闭线程内的套接字,一旦它完成处理流

更新

Swing 是一个单线程框架。这意味着与 UI 的所有交互(创建、修改)必须在事件调度线程的上下文中执行。任何阻塞该线程的操作都会阻止 EDT 处理事件,包括重绘、鼠标和键盘事件。

您应该传递套接字,而不是将套接字的输入 put 流传递给线程。这会将管理套接字的责任传递给该线程,从而释放您当前的线程。

然后在您的Incomming课程中,您应该获取对套接字输入流的引用,执行您需要的任何操作,最后在完成后关闭输入放置流和套接字。

protected static void start(JFrame window) {
        ServerSocket server = null;
        try {
            server = new ServerSocket();
            SocketAddress addr = new InetSocketAddress(hostname, port);
            server.bind(addr);

            Socket socket = server.accept();

            window.setVisible(false);

            // Pass the socket to the thread to allow it to perform the work
            Thread thread = new Thread(new Incomming(socket));
            thread.start();

        } catch (IOException ex) {
            //...//
        }

    }

public class Incomming implements Runnable {

    private final Socket socket;

    public Incomming(Socket socket) {
        //?? What's this for, this is VERY wrong
        // UI Interaction should ONLY occur within the context of the EDT
        MyFrame frame = new MyFrame();
        frame.setVisible(true);
        frame.pack();

        this.socket = socket;

    }

    public void run() {
        if (socket != null) {
            DataInputStream is = null;
            try {
                is = new DataInputStream(socket.getInputStream());
                while (!Thread.currentThread().isInterrupted()) {
                    int n = is.readInt();
                    if (n == -1) {
                        break;
                    }
                    byte[] b = new byte[n];
                    is.readFully(b);
                    //...//
                }
                System.out.println("Stream closed.");
            } catch (IOException e) {
            } finally {
                // Finally clean up...
                try {
                    is.close();
                } catch (Exception e) {
                }
                try {
                    socket.close();
                } catch (Exception e) {
                }
            }
        }
    }
}

必须阅读Swing 中的并发

如果您打算在处理套接字时更新 UI,您很可能希望使用 aSwingWorker而不是Thread. 这提供了额外的功能,可以更轻松地将更新同步回事件调度线程

于 2013-07-11T20:35:00.873 回答
0

您编写程序的方式意味着只有一个执行“路径”。在任何时候,您都不会有 2 个线程同时做事。这是因为您调用thread.join()了 ,它等待线程退出。这意味着要么Incomming类中的代码正在执行,要么它之外的代码正在执行。

要加载 UI,您将需要一次运行 2 个线程,这意味着重新考虑处理网络连接的方式。

第一步是处理关闭Incomming类内部的套接字。这将解决SocketClosedException由于尝试从关闭的套接字读取而引起的问题。

您可以做的第二件事是在 UI 和Incomming类之间进行不同的通信,因为线程中断是从长时间运行的网络读取中唤醒线程的好方法,但没有提供有关您唤醒线程的原因的信息. 最简单的方法是在Incomming调用函数时设置的类上设置一个布尔标志。

ex(请原谅伪代码):

class Incomming

    Socket socket;
    volatile boolean running;

    run():
        while( running ){ 
            try {
                 /* stuff */
            }catch( InterruptedException e ){
                 // nothing here
                 // we are just waking up the
                 // thread to unblock io
            }
        }
        socket.close()

    stop():
        running = false;
于 2013-07-11T20:42:21.857 回答
0
  1. thread.join() 将阻塞您当前的线程,直到线程结束
  2. socket.close(); 将在您的线程结束之前执行。您必须将此代码块移动到您的线程 run() 方法中。
于 2013-07-12T08:32:18.537 回答