Socket TCP如何传递字符串和字节数组

1,507 阅读2分钟

「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战

Socket TCP如何传递字符串和字节数组

我们通过Socket TCP 可以进行双向通信,我们用聊天工具聊天的时候,不仅仅可以发送文字,也可以发送图片,这是怎么实现的呢,又是怎么区分文字和图片呢?

本质

计算机上所有的数据归根到底,都是0和1组成,无论我们发送什么样的数据,最终都要转成0和1进行存储。

在java中,我们最小操作的单位是字节,所以无论我们发送图片或者文字,都要通过字节数组作为载体进行发送。

字符串的实现方式

方法一

服务端:每次发送数据时,在数据结尾处,拼上换行符。

public void sendString(final String msg) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (LOCK) {
                    try {
                        // 拼上换行符
                        String data = msg + "\n";
                        out.write(data.getBytes());
                        // 强制把数据输出
                        out.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

客户端:使用readLine方法

    /**
     * 创建接收线程
     */
    private void startReceiveTcpThread() {
        ThreadPool.getInstance().execute(new Runnable() {
            @Override
            public void run() {

                // 获取数据
                String line = "";
                try {
                    while ((line = br.readLine()) != null) {
                        handleReceiveTcpMessage(line);
                    }
                    if (line == null) {
                        LogWrapper.e(TAG, "line == null(上位机断开),开始断开...");
                        disConnect(); // 上位机断开
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    // 没有正常关闭时会出现
                    LogWrapper.e(TAG, "接收消息异常, 开始断开...:" + e.toString());
                    disConnect();
                }
            }
        });
    }

方法二

获取字节数组,字节数组转String.

           //读取服务端返回的数据,使用socket读取流
			InputStream inputStream = socket.getInputStream();
			byte[] buf = new byte[1024];
			int len = inputStream.read(buf);
			String textString = new String(buf, 0, len);
			System.out.println(textString);
			socket.close();

同时接收字符串和字节数组

因为readLine 方法读取的是换行符,这肯定行不通了;我们就尝试第二种方法,在发送端的时候,尝试去区分类型。比如

发送端:

定义两个不同的常量,作为标记,区分不同的类型

 final byte BYTE_TYPE = 0x1;
 final byte STRING_TYPE = 0x2;
 
 // byte数组类型
 out.write(BYTE_TYPE);
 out.write(bytes);
 
 // string 类型
 out.write(STRING_TYPE);
 out.write(string.getBytes());

接收端:

提取类型,并做相应的操作

 private void startReceiveTcpThread() {
        ThreadPool.getInstance().execute(new Runnable() {
            @Override
            public void run() {

                while (true) {
                    try {
                        byte[] bytePacket = new byte[BUFFER_LENGTH];
                        int actLength = mSocket.getInputStream().read(bytePacket);
                        byte[] actPacket = new byte[actLength];
                        System.arraycopy(bytePacket, 0, actPacket, 0, actLength);

                        int type = actPacket[0];

                        if (type == BYTE_TYPE){
                            // 去除第一个类型后,得到真实的byte
                        }

                        if (type == STRING_TYPE){
                            ...
                            // 去除第一个类型后,再进行转换
                            new String() ;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

仔细看这段代码 byte[] bytePacket = new byte[BUFFER_LENGTH],实际上我们将 BUFFER_LENGTH 设置成什么值,都不合适,正确的做法应该是固定BUFFER_LENGTH的值,然后做合包操作。

分包合包下篇文章再讲。