为什么DataOutputStream.writeUTF()在开头添加额外的2个字节?

当我尝试使用sax在套接字上解析xml时,我遇到了一个奇怪的现象。 经过分析,我注意到DataOutputStream在我的数据前添加了2个字节。

DataOutputStream发送的消息:

0020 50 18 00 20 0f df 00 00 00 9d 3c 3f 78 6d 6c 20 P.. .... .. yongmook kim..10 0000 

使用Transformer发送消息:

 0020 50 18 00 20 b6 b1 00 00 3c 3f 78 6d 6c 20 76 65 P.. .... yongmook kim 00a0 c2 a7 3c 2f 6e 69 63 6b 6e 61 6d 65 3e 3c 73 61 ..100 000 

正如人们可能会注意到DataOutputStream在消息前面添加了两个字节。 因此,sax解析器抛出exception“org.xml.sax.SAXParseException:prolog中不允许使用内容。” 但是,当我跳过这两个字节时,sax解析器工作得很好。 另外我注意到DataInputStream无法读取Transformer消息。

我的问题是:为什么DataOutputStream会添加这些字节,为什么不使用Transformer?




对于那些有兴趣复制问题的人,这里有一些代码:

使用DataInputStream的服务器:

 String data = "yongmook kim§100000"; ServerSocket server = new ServerSocket(60000); Socket socket = server.accept(); DataOutputStream os = new DataOutputStream(socket.getOutputStream()); os.writeUTF(data); os.close(); socket.close(); 

使用Transformer的服务器:

 ServerSocket server = new ServerSocket(60000); Socket socket = server.accept(); Document doc = createDocument(); printXML(doc, os); os.close(); socket.close(); public synchronized static void printXML(Document document, OutputStream stream) throws TransformerException { DOMSource domSource = new DOMSource(document); StreamResult streamResult = new StreamResult(stream); TransformerFactory tf = TransformerFactory.newInstance(); Transformer serializer = tf.newTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8"); serializer.setOutputProperty(OutputKeys.INDENT, "no"); serializer.transform(domSource, streamResult); } private static Document createDocument() throws ParserConfigurationException { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Element company = document.createElement("company"); Element staff = document.createElement("staff"); Element firstname = document.createElement("firstname"); Element lastname = document.createElement("lastname"); Element nickname = document.createElement("nickname"); Element salary = document.createElement("salary"); Text firstnameText = document.createTextNode("yong"); Text lastnameText = document.createTextNode("mook kim"); Text nicknameText = document.createTextNode("§"); Text salaryText = document.createTextNode("100000"); document.appendChild(company); company.appendChild(staff); staff.appendChild(firstname); staff.appendChild(lastname); staff.appendChild(nickname); staff.appendChild(salary); firstname.appendChild(firstnameText); lastname.appendChild(lastnameText); nickname.appendChild(nicknameText); salary.appendChild(salaryText); return document; } 


使用SAX Parser的客户:

 SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); DefaultHandler handler = new MyHandler(); Socket socket = new Socket("localhost", 60000); InputSource is = new InputSource(new InputStreamReader(socket.getInputStream())); is.setEncoding("UTF-8"); //socket.getInputStream().skip(2); // skip over the 2 bytes from the DataInputStream saxParser.parse(is, handler); 

使用DataInputStream的客户端:

 Socket socket = new Socket("localhost", 60000); DataInputStream os = new DataInputStream(socket.getInputStream()); while(true) { String data = os.readUTF(); System.out.println("Data: " + data); } 

DataOutputStream.writeUTF()的输出是一种自定义格式,旨在由DataInputStream.readUTF()读取。

你正在调用的writeUTF方法的javadoc说:

使用修改后的UTF-8编码以与机器无关的方式将字符串写入基础输出流。

首先,将两个字节写入输出流,就像writeShort方法一样,给出了要遵循的字节数。 该值是实际写出的字节数,而不是字符串的长度。 在该长度之后,使用针对该字符的修改的UTF-8编码依次输出该字符串的每个字符。 如果没有抛出exception,则written的计数器将增加写入输出流的总字节数。 这将是至少两个加上str的长度,并且最多两个加上str的长度的三倍。

在读取和写入数据时始终使用相同类型的流。 如果要将流直接提供给sax解析器,则不应使用DataOutputStream。

只是用

 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); bos.write(os.getBytes("UTF-8"));