从套接字中多次打开读/写流

4
在一个类中,我有一个 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服务器进行通信的方式。
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”作为响应写入之前尝试读取套接字是否为时机过早?
附言:我知道通过放置大量代码来提问不是一个好方法,在这里我们期望获得问题的答案并理解它,而不是让别人帮我们解决问题并离开。所以,我提供了这么多代码来展示问题的所有方面。
4个回答

5
我研究了ObjectInputStream源代码,发现指向原始输入流的引用存储在ObjectInputStream中。
当您关闭ObjectInputStream时,也会关闭。
一旦输入流关闭,就无法再次打开。因此,您会收到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,但我不知道它甚至适用于 Socket 的流。 - Kushal
这对我来说也是新知识。感谢你的问题,让我在研究源代码时发现了这个 :) - Lai Xin Chu

1
我再次打开流,从服务器开始读/写连接的套接字。
一旦流关闭,就无法再次打开它。实际上,你不能在同一个流上使用两个对象流。
相反,你应该创建一个输入输出对象流,只创建一次,直到完成后再关闭它。

那么我完全不能在主类中使用 acknowledgeClient() 吗?并且所有的 Socket 输入输出任务都必须在 ClientHandler 的线程中完成吗? - Kushal
不是你所写的方式。你可以在调用方法之前创建流并将其传递给处理程序。通常,处理程序也会进行确认。否则,慢消费者可能会阻止服务器接受任何新连接。 - Peter Lawrey

0
关闭与套接字流相关的任何输入流、输出流、读取器或写入器将关闭套接字,并暗示其他流、读取器和写入器也会被关闭。
在套接字的生命周期内使用相同的流、读取器和写入器。

0

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

客户端代码

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

    }

}

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