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

时间:2012-04-16 21:24:35

标签: java multithreading sockets multiprocessing connectexception

我在Windows XP上用Eclipse编程java。我有一个多进程模拟,它使用ProcessBuilder来运行服务器和两个客户端。服务器启动一个线程来监听两个不同的套接字 - 每个客户端一个。我可以为每个客户注释掉代码,让其他工作完全正常。当我尝试同时运行它们时,一个客户端将始终使用ConnectException错误:连接被拒绝:连接。虽然很难说,但哪个客户似乎是以较慢的速度运行。我可以在启动服务器之后但在客户端之前暂停,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 个答案:

答案 0 :(得分:1)

'服务器启动一个线程来监听两个不同的套接字 - 每个客户端一个。'

不,请不要这样做!为每个客户端分配一个单独的侦听套接字是一种异常设计。只需启动两个客户端连接的一个监听线程。当accept()返回时,启动该客户端的读线程,传递accept()调用返回的ServerClient Socket。

答案 1 :(得分:1)

编写服务器的常用方法是在循环中侦听单个套接字。

对于每个传入连接,产生一个线程来处理客户端,然后立即在同一服务器套接字上再次执行listen

在您的情况下,如果两个客户端尝试连接到同一服务器端口,则第二个客户端将始终获得“连接被拒绝”。第一个客户端获取连接,服务器不会在套接字上重新发出listen。您尚未显示客户端如何获取其端口号,但我猜他们都在尝试连接到同一端口。

答案 2 :(得分:0)

在我写这篇文章时,你的代码对我来说很好看。您有一个 ServerSocket ,用于侦听新连接。每个连接发生时,都会设置一个新的线程来处理它并且给它自己的 Socket 。您的套接字以及它们的主题应完全独立。每个人的命运应该完全独立于另一个。他们不应该知道彼此的存在。

弄清楚他们如何互动并将它们分开。

要尝试的是设置100个客户端而不是2个。这可能会使问题更加明显;一吨TNT将留下比鞭炮更大的陨石坑。

另一个可能的问题:确保定期重置ObjectOutputStream。这种I / O非常方便,但它可以做非常奇怪的事情。如果没有重置,如果你发送两次相同的对象,第二次它只会发送对第一个版本的引用,如果最新版本发生了变化则没用。此外,即使重置,如果您发送一个引用另一个的对象,对象也将被发送到该行。 (这可能非常好。您可以发送一个引用涉及数百万个对象的庞大结构的单个对象,整个事件将被发送保留其组织。它非常巧妙且非常有用,如果你知道它在做什么。)

答案 3 :(得分:0)

来自OP

因此,在解决我认为无关的问题的过程中,我似乎已经解决了最初的问题。我添加了两个主要的东西。首先,我设置客户端在完成时发送“end”命令,以便在尝试readObject()时套接字不会阻塞。其次,(我认为这是修复它的原因),我为ServerSocket定义了一个超时值,而不是将其保留为undefined(也就是无限)。在我开始工作之后我回去清理我的代码时,我尝试删除客户端进程中的暂停,但事情仍然有效!我的猜测是,没有定义的超时,当一个套接字在另一个套接字打开之前关闭时,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();
            }
        }
    }