简体   繁体   English

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

[英]Opening read/write streams multiple times from a Socket

In a class where I have ServerSocket listening for incoming connections, following is the code: 在我让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();
    }
}

And following is acknowledgeClient(Socket s) code. 接下来是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();

The run() method of the ClientHandler . 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();
}

And following is the way how client program communicates with this Echo Server. 下面是客户端程序与此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();
}

As you might have noticed that, after the client is acknowledged after connection, I close the read/write streams, and from new thread which is serving the client, I'm opening the stream again, and from the server reading/writing from the connected socket is started, but as soon as I attempt to read server's response on sending Hello! 您可能已经注意到,在连接后确认客户端后,我关闭了读/写流,并且从服务该客户端的新线程中,我再次打开了该流,并从服务器中读取/写入了该流。已启动已连接的套接字,但是一旦我尝试读取服务器在发送Hello!的响应Hello! by client, it crashes with EOFException instead of getting success . 由客户端,它崩溃与EOFException而不是success

I know the causes for which EOF occurs but not getting the clue that why is it happening here, I'm not attempting to read socket that has nothing in its stream (it should have success as written by server). 我知道发生EOF的原因,但不了解为什么会在这里发生,我不是在尝试读取流中没有任何内容的套接字(它应该由服务器编写success )。

Is it too early that client is attempting to read socket before server has printed Hello! 客户端在服务器打印Hello!之前尝试读取套接字是否为时过早Hello! on its end and written success as response? 最终以书面success作为回应吗?

PS : I know its not a good way to ask question by putting so much code, we're expected here to get answers of the issue and understand it rather than having our problem fixed by others and get away. PS:我知道通过放置大量代码不是问问题的好方法,我们希望在这里获得问题的答案并理解它,而不是让我们的问题被他人解决并逃脱。 So, I've provided this much code to show all aspects from the problem. 因此,我提供了很多代码来显示问题的各个方面。

I studied the source code of ObjectInputStream, and it appears that the reference to the original input stream s.getInputStream() is stored inside the ObjectInputStream. 我研究了ObjectInputStream的源代码,似乎对原始输入流s.getInputStream()的引用存储在ObjectInputStream内部。

When you close the ObjectInputStream, s.getInputStream() is closed as well. 当关闭ObjectInputStream时, s.getInputStream()也将关闭。

Once an input stream is closed, it cannot be opened again. 输入流一旦关闭,就无法再次打开。 Thus, you get an EOFException, which indicates that you are at the end of the stream (since the stream could not be opened again). 因此,您将得到一个EOFException,它指示您位于流的末尾(因为无法再次打开该流)。

You should do something like this to acknowledge the client. 您应该执行类似的操作来确认客户。

Inside the run() method of the ClientHandler: 在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();
}

If you want to isolate the acknowledge code in a seperate method, just be sure to maintain a proper reference to the same ObjectInputStream without closing the stream, then pass the reference around. 如果要使用单独的方法隔离确认代码,只需确保在不关闭流的情况下维护对相同ObjectInputStream的正确引用,然后将引用传递给周围即可。

I'm opening the stream again, and from the server reading/writing from the connected socket is started, 我再次打开流,然后从服务器开始从连接的套接字读取/写入数据,

Once a stream is close, you can't open it again. 流关闭后,您将无法再次打开它。 In fact you can't use two Object stream on the same stream this way at all. 实际上,您根本不能以这种方式在同一流上使用两个Object流。

Instead you should create an object stream for input and output once and only once and not close it until you have finished. 相反,应该只创建一次对象流来输入和输出一次,直到完成后才关闭它。

Well take a look at this program, i wrote it to understand multiple clients and server communication, your question is answered in this program. 好吧,看看这个程序,我编写它是为了了解多个客户端和服务器之间的通信,您的问题已在此程序中得到了解答。

The Client side code 客户端代码

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

    }




    }
}

Server Side code 服务器端代码

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

    }

}

Closing any input stream or output stream or reader or writer around a socket stream closes the socket, and by implication the other streams, readers, and writers. 关闭套接字流周围的任何输入流或输出流或读取器或写入器会关闭套接字,并暗示其他流,读取器和写入器。

Use the same streams, readers, writers for the life of the socket. 在套接字的生存期内使用相同的流,读取器,写入器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM