Socket读取固定长度的报文

响应报文格式如下

000530<?xml version="1.0" encoding="utf-8"?><root>
  <dstype>resp</dstype>
  <dscode>DS01</dscode>
  <seqnum>144819675072211</seqnum>
  <dstime>20151122 20:52</dstime>
  <rescode>1</rescode>
  <resdesc>成功</resdesc>
  <dwbh>003870</dwbh>
  <dwmc>xxxxx人民政府接待科</dwmc>
  <dwxz></dwxz>
  <xzqh>110102</xzqh>
  <dwdz>***政大楼一楼104</dwdz>
  <dwfzr>xx</dwfzr>
  <lxdh> dsd</lxdh>
  <dwgjjzh>0</dwgjjzh>
  <hjqsny>201511</hjqsny>
<khrq>34</khrq>
<jbjgbh>xx支行</jbjgbh>
<zck>90</zck>
  <jcrs>652</jcrs>
  <jczt>缴存</jczt>
</root>

报文前6位是包长度,刚开始使用下面这种方式读取数据:先读取前6位,得到包长度,每次读取1024个字节,按1024个字节计算出需要读取的次数,剩余的再一次性读取。
例如包长为2049,则读取两次1024个字节,然后剩余的1个字节再一次性读取出来。
以上逻辑在实际操作的时候碰到一个问题,在执行read方法读取1024个字节的时候,并非每次都能正常读取到1024个字节,最终导致报文不完整。

方法一:

        int packSize = 1024;
        Socket socket = null;
        PrintWriter printWriter = null;
        DataInputStream dis = null;
        ByteArrayOutputStream out = null;
        try
        {
            socket = new Socket(config.getIp(), config.getPort());
            //通过printWriter 来向服务器发送消息
            printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
            //发送消息
            printWriter.println(xml);
            printWriter.flush();
            //接收信息 获取包的长度
            dis = new DataInputStream(socket.getInputStream());
            byte[] lenBytes = new byte[6];
            dis.read(lenBytes);
            String lenStr = new String(lenBytes, "UTF-8");
            Integer len = Integer.valueOf(lenStr);
            //包的数量
            int packetCount = len / packSize;
            //最后一个包的大小
            int lastDataPacket = len - (packSize * packetCount);
            byte[] data = new byte[packSize];
            out = new ByteArrayOutputStream();
            for (int i = 0; i < packetCount; i++)
            {
                int j = dis.read(data);
                out.write(data, 0, j);
            }
            if (lastDataPacket > 0)
            {
                data = new byte[lastDataPacket];
                int j = dis.read(data);
                out.write(data, 0, j);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            //关闭连接
            try
            {
                if (out != null) out.close();
                if (printWriter != null) printWriter.close();
                if (dis != null) dis.close();
                if (socket != null) socket.close();

            }
            catch (Exception ignored)
            {
            }
        }

        if (out != null)
        {
            return new String(out.toByteArray(), "UTF-8");
        }

方法二:

具体实现如下,依然先获取到包长,是为了读取完了之后关闭连接。
依然每次读取1024个字节,在while循环里每次记录剩余需要读取的字节数(将包长减去实际读取的字节数,可能是1024,也可能不是)。
当剩余需要读取的字节数小于或者等于0的时候,表示报文已经读取完整了,可以关闭连接了。

public static void main(String[] args) {
        
        String host = "localhost";
        int port = 8899;        
        String msg = "hello";
        
        try (Socket socket = new Socket(host, port);
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()
        ) {
            // 向服务端socket推送报文
            OutputStream out = socket.getOutputStream();
            out.write(msg.getBytes(StandardCharsets.UTF_8));
            out.flush();
            InputStream in = socket.getInputStream();
            byte[] buf = new byte[1024];

            // 接收服务端socket返回的6位报文长度
            int len;
            byte[] lenBytes = new byte[6];
            BufferedInputStream bufferIn = new BufferedInputStream(in);
            bufferIn.read(lenBytes);
            int dataLen = Integer.parseInt(new String(lenBytes));

            // 根据6位报文长度读取报文内容
            while ((len = bufferIn.read(buf)) != -1) {
                byteArrayOutputStream.write(buf, 0, len);
                // 包长减去实际读取的字节数,可能是1024,也可能不是
                dataLen = dataLen - len;
                // 剩余需要读取的字节数小于或者等于0的时候,表示报文已经读取完整
                if (dataLen <= 0)
                    break;
            }
            String result = byteArrayOutputStream.toString("UTF-8");
            System.out.println(result);
            
            socket.shutdownInput();
            socket.shutdownOutput();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

方法三:


import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class SocketClient {

    public static void main(String[] args) {

        String host = "localhost";
        int port = 8899;
        String msg = "hello";

        try (Socket socket = new Socket(host, port);
             InputStream in = socket.getInputStream();
             BufferedInputStream bufferIn = new BufferedInputStream(in)
        ) {
            // ------------------------------向服务端socket推送报文
            OutputStream out = socket.getOutputStream();
            out.write(msg.getBytes(StandardCharsets.UTF_8));
            out.flush();
            socket.shutdownOutput();

            // ------------------------------接收socket服务端响应定长报文
            // 接收服务端socket返回的6位报文长度
            byte[] lenBytes = new byte[6];
            bufferIn.read(lenBytes);
            // 6位报文长度
            int dataLen = Integer.parseInt(new String(lenBytes));

            int offset = 0;
            // 已读取报文长度
            int readLen;
            byte[] res = new byte[dataLen];
            // 根据6位报文长度读取报文内容
            while (offset < dataLen) {
                readLen = in.read(res, offset, Math.min(1024, dataLen - offset));
                offset += readLen;
            }
            String result = new String(res, StandardCharsets.UTF_8);
            System.out.println("接收socket服务端响应定长报文:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值