我在Java中使用DataInputStream和DataOutputStream时遇到了一个问题,如果没有一些非常愚蠢的措施,我无法解决这个问题,我想知道是否有人知道更好的方法(我很确定有,但我一直找不到(。
背景:我希望能够向服务器发送Json字符串,并让服务器将该字符串作为对象进行解析。这需要独立于语言,因为我正在研究的项目需要我有一个异构的系统。
为了用最简单的例子来说明我的问题,我将排除Gson/Json等的创建,因为它可以用任何字符串重新创建。代码如下:
public class ServerSide {
private final int PORT = 5001;
private ServerSocket servSock;
private Socket sock;
private DataInputStream dis;
private DataOutputStream dos;
public ServerSide() throws IOException {
servSock = new ServerSocket(PORT);
}
public void startListening() {
try {
sock = servSock.accept();
dis = new DataInputStream(sock.getInputStream());
byte[] bytes = new byte[1024];
dis.read(bytes);
String receivedMessage = new String(bytes);
System.out.println("Message received: " + receivedMessage);
sock.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一个简单的测试类,它创建ServerSide((并在一段时间内调用startListening(true(。
public class ClientSide {
private final int PORT = 5001;
private Socket socket;
private DataOutputStream dos;
public ClientSide() {
}
public void sendMessage(String m) {
try {
socket = new Socket("127.0.0.1", PORT);
dos = new DataOutputStream(socket.getOutputStream());
dos.writeBytes(m);
System.out.println("Message sent: " + m);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
一个简单的测试类,它创建ClientSide((对象并调用sendMessage("这是一条测试消息"(;
我遇到的问题是,我只在服务器端收到部分消息。我不确定这是输入流还是输出流的问题,只能通过多次写入和读取数据以及修剪其间的空白来找到解决方案。我的第一个问题是,当我试图将Json字符串发送到我在c#中编写的程序时,它总是在下一次读取时发送第一个字符("{"(,然后发送字符串的其余部分。我通过发送一个空格来解决这个问题,然后在服务器端忽略这个问题。当在第一次读取时读取了看似随机的字符串时,java上的问题变得更糟。
服务器端的上述代码的几个输出示例是:
Message received: This is a tes
Message received: T
Message received: T
Message received: T
is.read(bytes);
可以返回任意数量的字节-从1到bytes.length
。
从输入流中读取一定数量的字节,并将其存储到缓冲区数组b中。实际读取的字节数以整数形式返回。此方法会阻塞,直到输入数据可用、检测到文件结尾或引发异常为止。
需要重复调用此方法,直到它返回表示流结束的-1
。
从套接字读取和向套接字写入提供了一个关于如何在面向行的交换中读取所有数据的示例。对于二进制数据(DataStream实际正在生成(,您需要使用ByteArrayOutputStream、ByteArrayInputStream和DataInputStream的组合:
InputStream sis = sock.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (int len = sis.read(bytes) > 0) {
baos.write(bytes, 0, len);
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new DataInputStream(bais);
byte[] vBytes = new byte[baos.size()];
int sLen = dis.read(vBytes);
String receivedMessage = new String(vBytes, 0, sLen);
System.out.println("Message received: " + receivedMessage);
注意:上面的代码是在回答特定的问题。不要将其投入生产:(
感谢@Illya Kysil提供的帮助,我找到了一个解决方案,将代码更改为:
public class ClientSide {
private final int PORT = 5001;
private Socket socket;
private DataOutputStream dos;
public ClientSide() {
}
public void sendMessage(String m) {
try {
socket = new Socket("127.0.0.1", PORT);
dos = new DataOutputStream(socket.getOutputStream());
byte[] mBytes = m.getBytes();
dos.writeInt(mBytes.length);
dos.write(mBytes);
System.out.println("Message sent: " + m);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
和
public class ServerSide {
private final int PORT = 5001;
private ServerSocket servSock;
private Socket sock;
private DataInputStream dis;
private DataOutputStream dos;
public ServerSide() throws IOException {
servSock = new ServerSocket(PORT);
}
public void startListening() {
try {
sock = servSock.accept();
dis = new DataInputStream(sock.getInputStream());
int length = dis.readInt();
byte[] bytes = new byte[length];
dis.readFully(bytes);
String receivedMessage = new String(bytes);
System.out.println("Message received: " + receivedMessage.trim());
dis.close();
sock.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这将首先发送一个整数,该整数等于正在发送的消息的字节数组的大小。可以在服务器端创建此长度的缓冲区,并且可以使用DataInputStream的readFully方法来填充此缓冲区。