zoukankan      html  css  js  c++  java
  • 使用ObjectOutputStream进行socket通信的时候出现固定读到四个字节乱码的问题

    问题描述:

    最近在写一个通信相关的项目,服务器端和客户端通过socket进行通信。本来想利用read的阻塞特性,服务器端和客户端按照一定的流程进行文件读写。结果发现客户端或者服务器read方法一直都返回乱码。而且读到的一端可能是客户端,可能是服务器端,固定的读到前面有四个字节的乱码,后续读到的字节码都是正常的。

    原因分析:

    开始以为是流没有正常关闭。修改了代码确保正确关闭之后,发现即使重新启动服务器和客户端,还是会固定读到四个字节乱码。后面查资料分析才找出真正的原因:由于我实现的socket通信既有字符串通信,又有对象通信。所以我在传递字符串的时候,使用的是socket.getOutputStream得到的流。而在进行对象传输的时候,我在前面的输出流外面包裹了一层ObjectOutputStream。因为我是在一开始就对socket的输出流进行了包裹,而如果用ObjectOutputStream装饰输出流,默认的会自动在流前面带上四个字节的前缀。而因为开始我发消息只是发送字符串,所以我是直接使用socket的输出流。这就导致将前面的四个字节前缀发送出去,导致最终的乱码。具体参见下面相关代码:

     1 /**
     2      * Creates an ObjectOutputStream that writes to the specified OutputStream.
     3      * This constructor writes the serialization stream header to the
     4      * underlying stream; callers may wish to flush the stream immediately to
     5      * ensure that constructors for receiving ObjectInputStreams will not block
     6      * when reading the header.
     7      *
     8      * <p>If a security manager is installed, this constructor will check for
     9      * the "enableSubclassImplementation" SerializablePermission when invoked
    10      * directly or indirectly by the constructor of a subclass which overrides
    11      * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
    12      * methods.
    13      *
    14      * @param    out output stream to write to
    15      * @throws    IOException if an I/O error occurs while writing stream header
    16      * @throws    SecurityException if untrusted subclass illegally overrides
    17      *         security-sensitive methods
    18      * @throws    NullPointerException if <code>out</code> is <code>null</code>
    19      * @since    1.4
    20      * @see    ObjectOutputStream#ObjectOutputStream()
    21      * @see    ObjectOutputStream#putFields()
    22      * @see    ObjectInputStream#ObjectInputStream(InputStream)
    23      */
    24     public ObjectOutputStream(OutputStream out) throws IOException {
    25     verifySubclass();
    26     bout = new BlockDataOutputStream(out);
    27     handles = new HandleTable(10, (float) 3.00);
    28     subs = new ReplaceTable(10, (float) 3.00);
    29     enableOverride = false;
    30     writeStreamHeader();
    31     bout.setBlockDataMode(true);
    32         if (extendedDebugInfo) {
    33         debugInfoStack = new DebugTraceInfoStack();
    34     } else {
    35         debugInfoStack = null;
    36         }   
    37     }
    38 
    39 
    40 
    41     /**
    42      * The writeStreamHeader method is provided so subclasses can append or
    43      * prepend their own header to the stream.  It writes the magic number and
    44      * version to the stream.
    45      *
    46      * @throws    IOException if I/O errors occur while writing to the underlying
    47      *         stream
    48      */
    49     protected void writeStreamHeader() throws IOException {
    50     bout.writeShort(STREAM_MAGIC);
    51     bout.writeShort(STREAM_VERSION);
    52     }
    ObjectOutputStream

    解决办法:

    既然直接用ObjectOutputStream将原来的socket的输出流进行包裹之后会出现固定四个字节的乱码,那么可以考虑用原来的socket输出流进行写数据的时候,接收方固定丢弃四个字节乱码。这样虽然可以实现,但是总感觉很别扭。最终我优化了相关的读写对象方法,只是用原来socket的输出流进行对象读写,具体代码实现如下:

     1     public <T> void writeObj(T obj) throws ZSocketException {
     2         if (obj == null) {
     3             return;
     4         }
     5         try(ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
     6             ObjectOutputStream objOut = new ObjectOutputStream(byteOut)) {   // 这个只是为了计算出对象大小而用的中介输出流
     7             objOut.writeObject(obj);
     8             byte[] ObjByte = byteOut.toByteArray();
     9             Header header = new Header(StringMsgType.OBJECT, ObjByte.length);
    10             HeaderAnalyser analyser = new HeaderAnalyser();
    11 
    12             // 先写消息头,再写消息内容
    13             output.write(analyser.formatHeader(header), 0, Constants.HEADER_LEN);
    14             output.write(ObjByte, 0, ObjByte.length);
    15             output.flush();
    16 
    17         } catch (IOException e) {
    18             throw new ZSocketException(e);
    19         }
    20     }
    21 
    22     public <T> T readObj(long len, Class<T> clazz) throws ZSocketException {
    23         if (len < 0 || clazz == null) {
    24             throw new IllegalArgumentException("Negative read length or null object class!");
    25         }
    26 
    27         try(ByteArrayOutputStream out = new ByteArrayOutputStream(Constants.BUFF_SIZE)) {
    28             writeData(input, out, len);
    29             try (ByteArrayInputStream byteIn = new ByteArrayInputStream(out.toByteArray());
    30                 ObjectInputStream objIn = new ObjectInputStream(byteIn)) {
    31                 @SuppressWarnings("unchecked")
    32                 T result =  (T) objIn.readObject();
    33                 return result;
    34             }
    35         } catch (Exception e) {
    36             throw new ZSocketException(e);
    37         }
    38 
    39     }
    ObjectWriteRead
  • 相关阅读:
    “连城决”——预示2008年手机营销体式格式新打破
    都会演出连城诀—诺基亚N78决战入手入手了!
    Lyx:阔别单调的 LaTeX 节制命令
    [转载]Oracle 11g R1下的自动内存经管(2)
    假造化手艺是决胜企业IT化的关头
    请各位博友对HyperV的运用终了指摘
    有199元的Office,还要用盗版吗?
    十一回南通,当晚和同学去小石桥附近的网吧
    Windows 消息
    WinAPI: 钩子回调函数之 MsgFilterProc
  • 原文地址:https://www.cnblogs.com/Kidezyq/p/5815611.html
Copyright © 2011-2022 走看看