关于DataOutputStream类的writeUTF的异常

文章目录

  • 1.进入writeUTF()
  • 2. 解决长字符串的wirte问题
  • 3. readUTF()

最近还是在做多文件的项目,到最后的阶段发现使用DataOutputStream时,writeUTF总是出错,所以就去看了writeUTF的源码,来揭晓原因。

1.进入writeUTF()

// 在调用writeUTF方法时,
//实际调用了static int writeUTF(String str, DataOutput out)方法
public final void writeUTF(String str) throws IOException {
        writeUTF(str, this);
}

// 进入static int writeUTF(String str, DataOutput out)
/*
首先,通过writeShort方法将两个字节写入出去,给出后面的字节数。
这个值是实际输出的字节数,而不是字符串的长度。
在长度之后,使用修改后的UTF-8编码顺序输出字符串的每个字符。
如果没有抛出异常,则写入的计数器将按写入输出流的总字节数递增。
这至少是str的长度加2,最多是str的长度加2倍
*/
static int writeUTF(String str, DataOutput out) throws IOException {
        int strlen = str.length(); //得到字符串的长度
        int utflen = 0;
        int c, count = 0;

        /* use charAt instead of copying String to char array */
        /* 这个utflen是每个字符的长度,比如一个汉字就是三个字符长度,这里需要注意*/
        for (int i = 0; i < strlen; i++) {
            c = str.charAt(i); //判断字符是多少位的,
            if ((c >= 0x0001) && (c <= 0x007F)) {
                utflen++;
            } else if (c > 0x07FF) {
                utflen += 3; //utf-8
            } else {
                utflen += 2;
            }
        }

		// 如果字符长度超过65535,那么之间抛出异常,这就是我犯错的地方
        if (utflen > 65535)
            throw new UTFDataFormatException(
                "encoded string too long: " + utflen + " bytes");

        byte[] bytearr = null;
        if (out instanceof DataOutputStream) {
            DataOutputStream dos = (DataOutputStream)out;
            if(dos.bytearr == null || (dos.bytearr.length < (utflen+2)))
                dos.bytearr = new byte[(utflen*2) + 2];
            bytearr = dos.bytearr;
        } else {
            bytearr = new byte[utflen+2];
        }

        bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
        bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);

        int i=0;
        for (i=0; i<strlen; i++) {
           c = str.charAt(i);
           if (!((c >= 0x0001) && (c <= 0x007F))) break;
           bytearr[count++] = (byte) c;
        }

        for (;i < strlen; i++){
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                bytearr[count++] = (byte) c;

            } else if (c > 0x07FF) {
                bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte) (0x80 | ((c >>  6) & 0x3F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            } else {
                bytearr[count++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            }
        }
        out.write(bytearr, 0, utflen+2);
        return utflen + 2;
}

所以我们就要对我们字符的长度进行判断了。

2. 解决长字符串的wirte问题

public static final int WRITE_READ_UTF_MAX_LENGTH = 65535;

// 传入输出流和要发送的字符串
public static void sendStr(DataOutputStream dos, String sendStr) throws IOException {
		//sendStr.length()在底层获取的是char[]的长度,所以不用担心字符串长度的不匹配
        dos.writeUTF(String.valueOf(sendStr.length())); // 先将字符串的长度发过去

		//如果字符串的大于65535,那么就需要进行切片发送了
        if (sendStr.length() > WRITE_READ_UTF_MAX_LENGTH) {
            for (int i = 1; i < sendStr.length() / WRITE_READ_UTF_MAX_LENGTH + 2; i++) {
                dos.writeUTF(sendStr.substring(WRITE_READ_UTF_MAX_LENGTH*(i-1) ,
                        WRITE_READ_UTF_MAX_LENGTH * i < sendStr.length()
                                ? WRITE_READ_UTF_MAX_LENGTH*i : sendStr.length()));
            }
        } else {
            dos.writeUTF(sendStr);
        }
    }

总体来说,就是一个切分思想,先发头部,再发真正的数据。
我们既然把消息已经发过去了,那么也就需要完整的将数据接收过来

3. readUTF()

分片发送,自然就要接收片段,将其整合到一起。

// 传入输入流,此方法要和sendStr一起使用。
public static String getStr(DataInputStream dis) throws IOException {
        String length = dis.readUTF(); //先读取字符串的长度
        int len = Integer.valueOf(length); //用字符串的长度来控制循环

        StringBuilder sBulider = new StringBuilder(); //字符串的拼接
        //如果长度大于等于65535,继续读取
        while(len >= WRITE_READ_UTF_MAX_LENGTH) {
            String tempStr = dis.readUTF();
            sBulider.append(tempStr);
            len -= tempStr.length();
        }
        //最后接收小于WRITE_READ_UTF_MAX_LENGTH的字符串
        String tempStr = dis.readUTF();
        sBulider.append(tempStr);
        return sBulider.toString();//返回结果
    }

网络编程的水真的是太深了,如果不对发送的数据和接收的数据的长度进行控制,那真的是一场灾难,并且debug真的很难发现。
敬畏Java,敬畏网络编程

你可能感兴趣的:(#,异常处理,java,开发语言)