如何读取服务器套接字 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 中的套接字发送所有文件名?