如何读取服务器套接字 JAVA 中的所有 Inputstream

Posted

技术标签:

【中文标题】如何读取服务器套接字 JAVA 中的所有 Inputstream【英文标题】:How to read all of Inputstream in Server Socket JAVA 【发布时间】: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();
    

谢谢。

【问题讨论】:

【参考方案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); 需要使用读取计数的是这段代码。你的“处理数据”挥手甚至没有提到它。

以上是关于如何读取服务器套接字 JAVA 中的所有 Inputstream的主要内容,如果未能解决你的问题,请参考以下文章

如何递归遍历目录,通过 node.js 中的套接字发送所有文件名?

QT 套接字不读取所有数据

java 逐行读取txt文本如何解决中文乱码

java读取request中的xml

如何确定是不是有数据可从 boost::asio 中的套接字读取?

如何使用套接字通道读取和写入数据并接受连接