【问题标题】:How to read all of Inputstream in Server Socket JAVA如何读取服务器套接字 JAVA 中的所有 Inputstream
【发布时间】:2013-11-19 06:56:25
【问题描述】:

我在我的一个项目中使用 Java.net。 我编写了一个从客户端获取 inputStream 的 App Server。 但有时我的(缓冲的)InputStream 无法获取客户端发送到我的服务器的所有 OutputStream。 我怎样才能写一个等待或类似的东西,让我的 InputStream 获得客户端的所有 OutputStream?

(我的 InputStream 不是字符串)

private Socket clientSocket;
private ServerSocket server;
private BufferedOutputStream outputS;
private BufferedInputStream inputS;
private InputStream inBS;
private OutputStream outBS;

server = new ServerSocket(30501, 100);
clientSocket = server.accept();

public void getStreamFromClient()  {
    try {
        outBS = clientSocket.getOutputStream();
        outputS = new BufferedOutputStream( outBS);
        outputS.flush();

        inBS = clientSocket.getInputStream();
        inputS = new BufferedInputStream( inBS );

    } catch (Exception e) {
        e.printStackTrace();
    }
}

谢谢。

【问题讨论】:

    标签: java network-programming inputstream serversocket bufferedinputstream


    【解决方案1】:

    您遇到的问题与 TCP 流性质有关。

    您从服务器发送 100 字节(例如)这一事实并不意味着您在第一次读取时会在客户端读取 100 字节。可能从服务器发送的字节在几个 TCP 段中到达客户端。

    您需要实现一个循环,在该循环中阅读直到收到整条消息。 让我用DataInputStream 代替BufferedinputStream 提供一个示例。很简单的事,给你举个例子。

    假设您事先知道服务器要发送 100 字节的数据。

    在客户端你需要写:

    byte[] messageByte = new byte[1000];
    boolean end = false;
    String dataString = "";
    
    try 
    {
        DataInputStream in = new DataInputStream(clientSocket.getInputStream());
    
        while(!end)
        {
            int bytesRead = in.read(messageByte);
            dataString += new String(messageByte, 0, bytesRead);
            if (dataString.length == 100)
            {
                end = true;
            }
        }
        System.out.println("MESSAGE: " + dataString);
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    

    现在,通常事先不知道一个节点(此处为服务器)发送的数据大小。然后你需要定义自己的小协议,用于服务器和客户端(或任何两个节点)之间的通信,使用 TCP 进行通信。

    最常见最简单的就是定义TLV:Type、Length、Value。因此,您定义从服务器发送到客户端的每条消息都带有:

    • 1 字节指示类型(例如,它也可以是 2 或其他)。
    • 1 字节(或其他)消息长度
    • N 字节的值(N 表示长度)。

    所以你知道你必须至少接收 2 个字节,并且通过第二个字节你知道你需要读取多少后续字节。

    这只是一个可能的协议的建议。你也可以去掉“Type”。

    所以它会是这样的:

    byte[] messageByte = new byte[1000];
    boolean end = false;
    String dataString = "";
    
    try 
    {
        DataInputStream in = new DataInputStream(clientSocket.getInputStream());
        int bytesRead = 0;
    
        messageByte[0] = in.readByte();
        messageByte[1] = in.readByte();
    
        int bytesToRead = messageByte[1];
    
        while(!end)
        {
            bytesRead = in.read(messageByte);
            dataString += new String(messageByte, 0, bytesRead);
            if (dataString.length == bytesToRead )
            {
                end = true;
            }
        }
        System.out.println("MESSAGE: " + dataString);
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    

    以下代码编译后看起来更好。它假定提供长度的前两个字节以二进制格式以网络字节序(大字节序)形式到达。没有关注消息其余部分的不同编码类型。

    import java.nio.ByteBuffer;
    import java.io.DataInputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    class Test
    {
        public static void main(String[] args)
        {
            byte[] messageByte = new byte[1000];
            boolean end = false;
            String dataString = "";
    
            try 
            {
                Socket clientSocket;
                ServerSocket server;
    
                server = new ServerSocket(30501, 100);
                clientSocket = server.accept();
    
                DataInputStream in = new DataInputStream(clientSocket.getInputStream());
                int bytesRead = 0;
    
                messageByte[0] = in.readByte();
                messageByte[1] = in.readByte();
                ByteBuffer byteBuffer = ByteBuffer.wrap(messageByte, 0, 2);
    
                int bytesToRead = byteBuffer.getShort();
                System.out.println("About to read " + bytesToRead + " octets");
    
                //The following code shows in detail how to read from a TCP socket
    
                while(!end)
                {
                    bytesRead = in.read(messageByte);
                    dataString += new String(messageByte, 0, bytesRead);
                    if (dataString.length() == bytesToRead )
                    {
                        end = true;
                    }
                }
    
                //All the code in the loop can be replaced by these two lines
                //in.readFully(messageByte, 0, bytesToRead);
                //dataString = new String(messageByte, 0, bytesToRead);
    
                System.out.println("MESSAGE: " + dataString);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    

    【讨论】:

    • 谢谢罗德克。我在编写 HTTP 服务器时遇到了类似的问题。我设法使用“Content-Length”标头作为等待来自客户端的确切数据量的机制来解决问题。
    • @SaliHoo,是的,这就是这样做的方法:使用 HTTP,您需要阅读直到找到“Content-Length”(然后您使用获得的数字阅读其余部分)但还有其他两个需要考虑的情况:当 'Content-Length' 不存在并且您发现两个 '\r\n'(内容为 0)以及消息包含块时。
    • @GeorgedeLemos 问题很可能是客户端正在使用它正在读取的同一线程处理消息(我想是这样)。不管是什么,客户都在花费太多时间进行额外的处理。 TCP 有一种流量控制机制,如果客户端以较慢的速度消耗从服务器到达的数据,则在某个时候 TCP 接收缓冲区将被填满,并且 TCP 将告诉服务器停止发送,并使用值为 0 的广告窗口(零)。那应该管理这种情况。但是,您的担忧必须与客户端的速度有多慢有关。使用不同的线程来处理。
    • @stdout 排序由 TCP 处理。它按顺序将数据传递到应用程序层。你不需要在应用层担心它。异步通信还有其他复杂的场景,但我想你没有提到它。
    • @stdout,很难理解你的评论。 TCP 连接可以用于 HTTP 或任何其他应用层协议。在 HTTP 中,服务器的目标是读取完整的 HTTP 请求。为此,您必须应用我在回复中解释的内容。您需要阅读所有内容,直到在 HTTP 标头中找到 HTTP 消息长度,无论它在什么位置。然后您就知道完成请求需要读取的字节总数。
    【解决方案2】:
    int c;
        String raw = "";
        do {
            c = inputstream.read();
            raw+=(char)c;
        } while(inputstream.available()>0);
    

    InputStream.available() 仅在读取一个字节后显示可用字节,因此执行 ..while

    【讨论】:

    • 在网络通信中,长度信息不能被.available()查询,只能通过header查询
    【解决方案3】:

    您可以像这样读取 BufferedInputStream。它将读取数据直到到达流的末尾,由 -1 指示。

    inputS = new BufferedInputStream(inBS);
    byte[] buffer = new byte[1024];    //If you handle larger data use a bigger buffer size
    int read;
    while((read = inputS.read(buffer)) != -1) {
        System.out.println(read);
        // Your code to handle the data
    }
    

    【讨论】:

    • 我只是展示了如何读取整个输入流。 println() 在这里只是一个占位符。 OP 没有询问如何获取读取计数!如果他了解如何读取整个流,他将了解如何获取读取计数。
    • OP 正在使用 InputStream。除非他设置超时,否则 while 循环将永远挂起,例如:socket.setSoTimeout(1000);
    • 需要使用读取计数的是这段代码。你的“处理数据”挥手甚至没有提到它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    • 2015-05-24
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多