多个套接字导致连接异常:连接被拒绝:连接

3
我正在Windows XP上使用Eclipse编写Java程序。我有一个多进程模拟程序,使用ProcessBuilder 运行一个服务器和两个客户端。服务器启动一个线程来监听两个不同的套接字-每个客户端一个。我可以注释掉每个客户端的代码,并让另一个正常运行。但是当我尝试同时运行它们时,一个客户端总会出现ConnectException:Connection refused: connect错误。似乎是哪一个客户端较慢时就会发生这种情况。我可以在启动服务器之后但在启动客户端之前暂停一下,netstat确认两个套接字都处于活动状态。这可能是什么原因呢?下面是一些简化的代码。

更新:根据评论,我已经编辑了代码,将服务器多线程处理在单个套接字上,但我仍然遇到了相同的问题。以下的代码反映了更改。看起来,其中一个客户端打开并关闭了套接字,而另一个则没有机会打开它。我可以在每个客户端的末尾添加暂停语句,使另一个客户端完成,但这只是一种解决方法,而不是真正的解决方案。那么现在真正的问题是,如何保持ServerSocket监听,直到我告诉它关闭?

服务器:

try{
    server = new ServerSocket(sockNum); 
} catch (IOException e) {
        System.out.printf("Could not listen on port %d\n",sockNum);
        System.exit(-1);
}
while(true){
    ClientWorker w;
    try{
        Socket connection = server.accept();
        w = new ClientWorker(connection);
        Thread t = new Thread(w);
        t.start();
    } catch (IOException e) {
        System.out.printf("Accept failed: %d\n",sockNum);
        System.exit(-1);
    }
}

class ClientWorker implements Runnable {
    private Socket client;

          ClientWorker(Socket client) {
           this.client = client;
          }

          public void run(){
            Object line;
            ObjectInputStream in = null;
            PrintWriter out = null;
            try{
              in = new ObjectInputStream(client.getInputStream());
              out = new PrintWriter(client.getOutputStream(), true);
            } catch (IOException e) {
              System.out.println("in or out failed");
              System.exit(-1);
            }

            while(true){
                try{
                    line = in.readObject();
                    //Send data back to client
                    out.println("Sent from broker");
                    if(line instanceof String)
                        System.out.println(line);
                    else
                        System.out.println(line.getClass());                        
                } catch (IOException e) {
                    System.out.println("Read failed");
                    System.exit(-1);
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

客户

 try{
    socket = new Socket("localhost", socketNum);
    out = new ObjectOutputStream(socket.getOutputStream());
    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    out.writeObject(message);
    String line = in.readLine();
    System.out.println(line);
    } catch (UnknownHostException e) {
        System.out.println("Unknown host: localhost.eng");
        System.exit(1);
    } catch  (IOException e) {
        System.out.println("No I/O");
        System.exit(1);
    }

控制器

ProcessBuilder server = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ServerProcess");
server.redirectErrorStream(true);
Process runServer = server.start();

ProcessBuilder clientA = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientAProcess");
clientA.redirectErrorStream(true);
Process runClientA = clientA.start();

ProcessBuilder clientB = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientBProcess");
clientB.redirectErrorStream(true);
Process runClientB = clientB.start();
4个回答

1
服务器启动一个线程来监听两个不同的套接字——每个客户端一个。
不要这样做!为每个客户端指定单独的监听套接字是异常设计。只需启动一个监听线程,两个客户端连接到它上面。当accept()返回时,启动一个读线程为该客户端服务,传递由accept()调用返回的ServerClient Socket。

我改成了多线程单套接字的方法,但仍然存在问题。请参见上面的更新获取更多信息。 - David K
你的套接字代码看起来更好了。客户端是否在从服务器读取消息后打印“来自代理”的消息?客户端循环在哪里?如果没有循环,客户端不会立即退出并关闭连接吗? - Martin James

1
通常编写服务器的方法是在循环中监听单个套接字。
对于每个传入的连接,生成一个线程来处理客户端,然后立即在同一服务器套接字上再次执行listen
在您的情况下,如果两个客户端尝试连接到同一服务器端口,则第二个客户端将始终收到“连接被拒绝”的消息。第一个客户端获取了连接,服务器没有在套接字上重新发出listen。您没有展示客户端如何获取其端口号,但我猜测它们都在尝试连接到同一端口。

我改成了多线程单套接字的方法,但仍然存在问题。请参见上面的更新获取更多信息。 - David K

0

来自 OP

在解决我认为与问题无关的问题时,我似乎已经解决了最初的问题。我添加了两个主要的东西。首先,我设置客户端在完成后发送“end”命令,以便套接字在尝试读取对象时不会阻塞。其次,(我认为这就是解决问题的原因),我为ServerSocket定义了一个超时值,而不是将其保留未定义(即无限)。在让事情正常工作后回去整理代码时,我尝试删除客户端进程中的暂停,但事情仍然有效!我的猜测是,在没有定义超时的情况下,当一个套接字关闭时,另一个套接字打开之前,ServerSocket也会关闭。有了定义的超时,ServerSocket将等待超时后再关闭。(这只是一个猜测。)

这是当前的工作代码。如果您认为有不同的原因导致修复,请随意发表评论。

服务器

try{
    server = new ServerSocket(sockNum);
    server.setSoTimeout(timeOut);
} catch (IOException e) {
    System.out.printf("Could not listen on port %d\n",sockNum);
    System.exit(-1);
}

while(isActive){
    ClientWorker w;
    try{
        Socket connection = server.accept();
        w = new ClientWorker(connection);
        Thread t = new Thread(w);
        t.start();
    } catch (IOException e) {
        if(e instanceof SocketTimeoutException)
            isActive = false;
        else{
            System.out.printf("Accept failed: %d\n",sockNum);
            System.exit(-1);
        }
    }
}

 class ClientWorker implements Runnable {
        private Socket client;
        private Source source = Source.NONE;
        private String sourceName = "?";
        private Boolean threadActive = true;

        ClientWorker(Socket client) {
            this.client = client;
        }

        public void run(){
            Object line;
            ObjectInputStream in = null;
            PrintWriter out = null;
            try{
                in = new ObjectInputStream(client.getInputStream());
                out = new PrintWriter(client.getOutputStream(), true);
            } catch (IOException e) {
                System.out.println("in or out failed");
                System.exit(-1);
            }

            while(threadActive){
                try{
                    line = in.readObject();
                    if(line instanceof String){
                        if(line.equals("end")){
                            threadActive = false;
                        }else
                            sourceName = line;
                        }
                    } else 
                        handleData;
                } catch (IOException e) {
                    System.out.println("Read failed");
                    threadActive = false;
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    threadActive = false;
                }
            }
            try {
                this.client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

0

就我所看到的,你的代码看起来很不错。你有一个 ServerSocket 监听新连接。每个连接建立时,都会设置一个新线程来处理它,并为其提供自己的 Socket。你的 Socket 和它们的线程应该是完全独立的。每个的命运应该完全独立于其他的。它们不应该知道彼此的存在。

找出它们如何交互并将它们分开。

尝试的一件事是设置100个客户端而不是2个。这可能会更明显地显示问题;一吨TNT会留下比鞭炮更大的弹坑。

一个可能存在的问题:确保您定期重置ObjectOutputStream。这种I/O非常方便,但它可能会做出非常奇怪的事情。如果没有重置,则在第二次发送相同对象时,它只会发送到第一个版本的引用,如果最新版本已更改,则此引用将无用。同样,即使进行了重置,如果发送引用另一个对象的对象,则也会将对象发送到线下。(这很好。您可以发送一个仅引用数百万个对象的庞大结构的单个对象,并且整个结构将被发送并且保留其组织形式。这是巧妙和非常有用的,如果知道其操作方式)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接