BufferedWriter / PrintWriter / OutputStreamWriter在调用.close()之前不会刷新缓冲区(通过套接字输出流)

时间:2014-06-19 16:52:32

标签: java sockets inputstream outputstream flush

每当我在标题中的任何编写者(包装OutputStreamWriter)上调用.flush()时,都不会发送刷新的数据;但是,当在编写器上调用.close()时,它会刷新并且服务器可以读取数据。我不知道我是否错误地调用.write().readLine(),或者,如果由于读取操作已被阻止,那么问题是什么?我需要找到一种方法来刷新数据而不关闭套接字(我将在另一个程序中使用它)。在我所拥有的两个SSCCE中,有System.out.println()个用于调试。在客户端,它将进入"检查4"什么都不循环(它在实际程序中执行更多的读写操作,在示例中只能将它关闭)。在服务器中,它进入"检查2"并在reader.readLine()处阻止。我已经阅读了SO,但我所提出的是readLine()对于网络程序来说并不是一个好主意,我应该实现自己的协议;但是没有一个帖子指向一个好的教程网站,或其他任何有良好的网络编码教程。以下是SSCCE:

客户SSCCE

package testClient;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class Client {

    public static void main(String[] args) {
        try {
            Socket s = new Socket("127.0.0.1",48878);
            System.out.println("check 1");
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream(),"UTF-8"));
            System.out.println("check 2");
            pw.write("Hello StackOverFlow.");
            System.out.println("check 3");
            pw.flush();
            System.out.println("check 4");
            while(true){}
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

服务器SSCCE

package testServer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {


    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(48878);
            Socket client = ss.accept();
            System.out.println("check 1");
            BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(),"UTF-8"));
            System.out.println("check 2");
            byte[] buffer = new byte[20];
            int i = 0;;
            int x;
            while((x=reader.read())!=-1) {
                buffer[i] += (byte)x;
                i = i++;
            }
            String text = new String(buffer,"UTF-8");
            System.out.println("check 3");
            System.out.println(text);
            client.close();
            ss.close();
        } catch (IOException  e) {
            e.printStackTrace();
        }
    }

}

2 个答案:

答案 0 :(得分:2)

尝试PrintWriter自动刷新属性,一旦新行方法被调用,就会刷新数据。在流中写入任何数据后无需调用flush。

  

PrintStream类不同,如果启用了自动刷新,则仅在调用printlnprintfformat方法之一时才会执行,而不是每当输出换行符时。

示例代码:

客户端:

PrintWriter pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream(),"UTF-8"),true);
pw.println("Hello StackOverFlow.");

服务器:

BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(),"UTF-8"));
System.out.println(reader.readLine());

答案 1 :(得分:2)

“bug”位于用于读取数据的循环中。仅当您关闭另一侧的流时才会发送EOF。所以循环似乎挂了,尽管事实上已经可能已经向数组添加了N个字节。

您需要做的是向服务器发送有关客户端将要发送的数据长度的提示。这可以是字节数/字符数或终止字符(换行符)。

在发件人中,发送数据+提示,然后刷新。

在接收器中,读取尽可能多的数据,而不是单个字节。这样,接收器就不会阻塞。

在您的情况下,只要您对发件人使用reader.readLine() + flush,println()就可以正常工作。