【问题标题】:Opening read/write streams multiple times from a Socket从 Socket 多次打开读/写流
【发布时间】:2012-06-09 16:45:30
【问题描述】:

在我有ServerSocket 监听传入连接的类中,代码如下:

while(isRunning)
{
    try
    {
        Socket s = mysocketserver.accept();
        acknowledgeClient(s);
        new ClientHandler(s).start(); //Start new thread to serve the client, and get back to accept new connections.
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
}

下面是acknowledgeClient(Socket s)代码。

ObjectInputStream in = new ObjectInputStream(s.getInputStream);
ObjectOutputStream out = new ObjectOutputStream(s.getOutStream);
String msg = in.readObject().toString();
System.out.println(msg+" is Connected"); //Show who's connected
out.writeObject("success"); //Respond with success.
in.close();
out.close();

ClientHandlerrun() 方法。

try
{
    in = new ObjectInputStream(client.getInputStream());
    out = new ObjectOutputstream(client.getOutputStream());
    String msg = "";
    while(!msg.equalsIgnoreCase("bye"))
    {
        msg = in.readObject().toString();
        System.out.println("Client Says - "+msg);
        out.writeObject("success");
    }
    in.close();
    out.close();
}
catch(Exception ex)
{
    ex.printStackTrace();
}

以下是客户端程序与此 Echo Server 通信的方式。

try
{
    int count = 10;
    client = new Socket("localhost",8666);
    in = new ObjectInputStream(client.getInputStream());
    out = new ObjectOutputstream(client.getOutputStream());
    out.writeObject("Foo");
    System.out.println("Connection Status : "+in.readObject().toString());
    while(count>0)
    {
        out.writeObject("Hello!");
        String resp = in.readObject().toString(); //Getting EOFException here.
        System.out.println("Sent with :"+resp);
        count--;
        Thread.sleep(1000);
    }
    out.close();
    in.close();
    client.close();
}
catch(Exception ex)
{
    ex.printStackTrace();
}

您可能已经注意到,在连接后确认客户端后,我关闭读/写流,并从为客户端提供服务的新线程中再次打开流,并从服务器读取/从连接的套接字开始写入,但是一旦我尝试读取服务器对客户端发送Hello! 的响应,它就会崩溃并以EOFException 而不是获得success

我知道 EOF 发生的原因,但不知道为什么会在这里发生,我没有尝试读取其流中没有任何内容的套接字(它应该有 success 由服务器编写)。

客户端在服务器端打印Hello! 并写入success 作为响应之前尝试读取套接字是否为时过早?

附注: 我知道通过放置这么多代码来提问不是一个好方法,我们希望在这里得到问题的答案并理解它,而不是让别人解决我们的问题然后逃脱。所以,我提供了这么多代码来展示问题的各个方面。

【问题讨论】:

    标签: java sockets


    【解决方案1】:

    我研究了ObjectInputStream的源代码,看来对原始输入流s.getInputStream()的引用存储在ObjectInputStream中。

    当您关闭 ObjectInputStream 时,s.getInputStream() 也会关闭。

    输入流一旦关闭,就无法再次打开。因此,您会得到一个 EOFException,这表明您处于流的末尾(因为无法再次打开流)。

    您应该这样做以确认客户。

    ClientHandler的run()方法内部:

    try {
        // acknowledge client
        ObjectInputStream in = new ObjectInputStream(s.getInputStream());
        ObjectOutputStream out = new ObjectOutputStream(s.getOutStream());
        String msg = in.readObject().toString();
        System.out.println(msg+" is Connected"); //Show who's connected
        out.writeObject("success"); //Respond with success.
        // end acknowledge client
    
        String msg = "";
        while(!msg.equalsIgnoreCase("bye"))
        {
            msg = in.readObject().toString();
            System.out.println("Client Says - "+msg);
            out.writeObject("success");
        }
        in.close();
        out.close();
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
    

    如果您想在单独的方法中隔离确认代码,只需确保在不关闭流的情况下保持对同一 ObjectInputStream 的正确引用,然后传递引用。

    【讨论】:

    • 做了同样的事情,谢谢... :-) 我以为你可以在调用 close() 后重新打开 Socket,但不知道它甚至适用于套接字流。
    • 它对我来说也是新知识。感谢您的问题,我在研究源代码时发现了这一点:)
    【解决方案2】:

    我再次打开流,并从服务器开始从连接的套接字读取/写入,

    流关闭后,您将无法再次打开它。事实上,你根本不能以这种方式在同一个流上使用两个 Object 流。

    相反,您应该为输入和输出创建一次且仅一次的对象流,并且在完成之前不要关闭它。

    【讨论】:

    • 所以我根本不能使用主类的acknowledgeClient()?并且Socket的所有IO任务都在ClientHandler的线程中完成吗?
    • 不是你写的那样。您可以在调用方法之前创建流并将它们也传递给处理程序。通常,处理程序也会进行确认。否则,缓慢的消费者可能会阻止服务器接受任何新连接。
    【解决方案3】:

    看看这个程序,我写它是为了理解多个客户端和服务器的通信,你的问题在这个程序中得到了解答。

    客户端代码

    public class ClientWala {
    
        public static void main(String[] args) throws Exception{
    
            Boolean b = true;
        Socket s = new Socket("127.0.0.1", 4444);
    
        System.out.println("connected: "+s.isConnected());
    
    
        OutputStream output = s.getOutputStream();
        PrintWriter pw = new PrintWriter(output,true);
    
        // to write data to server
        while(b){
    
            if (!b){
    
                 System.exit(0);
            }
    
            else {
                pw.write(new Scanner(System.in).nextLine());
            }
        }
    
    
        // to read data from server
        InputStream input   = s.getInputStream();
        InputStreamReader isr = new InputStreamReader(input);
        BufferedReader br = new BufferedReader(isr);
        String data = null;
    
        while ((data = br.readLine())!=null){
    
            // Print it using sysout, or do whatever you want with the incoming data from server
    
        }
    
    
    
    
        }
    }
    

    服务器端代码

    public class ServerTest {
    
        ServerSocket s;
    
        public void go() {
    
            try {
                s = new ServerSocket(44457);
    
                while (true) {
    
                    Socket incoming = s.accept();
                    Thread t = new Thread(new MyCon(incoming));
                    t.start();
                }
            } catch (IOException e) {
    
                e.printStackTrace();
            }
    
        }
    
        class MyCon implements Runnable {
    
            Socket incoming;
    
            public MyCon(Socket incoming) {
    
                this.incoming = incoming;
            }
    
            @Override
            public void run() {
    
                try {
                    PrintWriter pw = new PrintWriter(incoming.getOutputStream(),
                            true);
                    InputStreamReader isr = new InputStreamReader(
                            incoming.getInputStream());
                    BufferedReader br = new BufferedReader(isr);
                    String inp = null;
    
                    boolean isDone = true;
    
                    System.out.println("TYPE : BYE");
                    System.out.println();
                    while (isDone && ((inp = br.readLine()) != null)) {
    
                        System.out.println(inp);
                        if (inp.trim().equals("BYE")) {
                            System.out
                                    .println("THANKS FOR CONNECTING...Bye for now");
                            isDone = false;
                            s.close();
                        }
    
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    try {
                        s.close();
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    e.printStackTrace();
                }
    
            }
    
        }
    
        public static void main(String[] args) {
    
            new ServerTest().go();
    
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      关闭套接字流周围的任何输入流或输出流或读取器或写入器会关闭套接字,并暗示其他流、读取器和写入器。

      在套接字的整个生命周期中使用相同的流、读取器和写入器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-27
        • 1970-01-01
        • 2013-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多