一:客户端代码
客户端传输协议:所有的整数采用大端序,当CRC校验不通过时,客户端需要重传1次(当前只是成功,不成功服务端回传个客服端信息即可)。仍然失败则提示,发送失败。
字节 | 内容 | 说明 |
---|---|---|
4 | 0x1111AAAA | 魔数 |
4 | 整数 | 版本号 |
4 | 整数 n | 数据区个数 |
4 | 整数 | 数据区编号 |
4 | 整数 len | 数据区长度 |
len | 二进制数据 | 数据内容 |
… | … | … |
8 | 整数 | CRC校验和,不包含魔数和版本号 |
CRC校验可以去掉,拼接在文件末端,当前时CRC16校验为4字节,AS自有的API是CRC32校验为8字节。
public static final int MAGIC_NUMBER = 0x1111AAAA; Socket socket = new Socket("127.0.0.1", 8084); if (socket.isConnected()) { Log.d(TAG, "connect1: 客服端连接成功"); } //上传 OutputStream outputStream = socket.getOutputStream(); BufferedOutputStream os = new BufferedOutputStream(outputStream); //文件头 os.write(intToByte(MAGIC_NUMBER)); byte[] bytesSend = new byte[8]; //版本号和数据个数 System.arraycopy(intToByte(1), 0, bytesSend, 0, 4); System.arraycopy(intToByte(2), 0, bytesSend, 4, 4); os.write(bytesSend); //左边数据 os.write(intToByte(1)); byte[] left_name = "u396".getBytes(); Log.d(TAG, "connect1666: " + left_name.length); byte[] left_byte = intToByte(left_name.length); os.write(left_byte); os.write(left_name); //右边数据 os.write(intToByte(2)); byte[] right_name = "u397".getBytes(); byte[] right_byte = intToByte(right_name.length); os.write(right_byte); os.write(right_name); //CRC校验 byte[] allBytes = new byte[16 + left_name.length + right_name.length]; System.arraycopy(intToByte(1), 0, allBytes, 0, 4); System.arraycopy(left_byte, 0, allBytes, 4, 4); System.arraycopy(left_name, 0, allBytes, 8, left_name.length); System.arraycopy(intToByte(2), 0, allBytes, 8 + left_name.length, 4); System.arraycopy(right_byte, 0, allBytes, 12 + left_name.length, 4); System.arraycopy(right_name, 0, allBytes, 16 + left_name.length, right_name.length); int crc = CRC16.calcCrc16(allBytes); byte[] crc16Byte = intToByte(crc); os.write(crc16Byte); os.flush(); socket.close();
二:服务端代码
public static final int MAGIC_NUMBER = 0x1111AAAA; Log.d(TAG, "connect: 等待客户端链接"); ServerSocket serversocket = new ServerSocket(8084); while (true) { Socket socket = serversocket.accept(); Log.d(TAG, "connect: 开始链接"); if (socket.isConnected()) { Log.d(TAG, "connect: 服务端连接成功"); binding.smallLabel.setColorFilter(Color.GREEN); //获取套接字输入流,及客户端数据 InputStream inputStream = socket.getInputStream(); BufferedInputStream is = new BufferedInputStream(inputStream); //读取所有的数据源 byte[] allBytes = new byte[1024]; is.read(allBytes); //读取文件 byte[] magicBytes = new byte[4]; System.arraycopy(allBytes, 0, magicBytes, 0, 4); int magicNum = byteToInt(magicBytes); //识别当前文件头,如果是当前数据文件,则识别成功走下面 if (magicNum == MAGIC_NUMBER) { Log.d(TAG, "connect: 文件识别成功,当前文件抬头是:" + magicNum); //读取版本号 byte[] byteVersion = new byte[4]; System.arraycopy(allBytes, 4, byteVersion, 0, 4); int versionNum = byteToInt(byteVersion); Log.d(TAG, "connect: 当前版本号:" + versionNum); //读取数据区的个数 byte[] byteData = new byte[4]; System.arraycopy(allBytes, 8, byteData, 0, 4); int byteDataNum = byteToInt(byteData); Log.d(TAG, "connect: 当前数据区个数:" + byteDataNum); //读取第一个数据区编号 byte[] byteNote = new byte[4]; System.arraycopy(allBytes, 12, byteNote, 0, 4); int byteDataNote = byteToInt(byteNote); Log.d(TAG, "connect: 第一个数据区的编号为:" + byteDataNote); //读取第一个数据区长度 byte[] byteLength = new byte[4]; System.arraycopy(allBytes, 16, byteLength, 0, 4); int byteLeftLength = byteToInt(byteLength); Log.d(TAG, "connect: 第一个数据区的长度:" + byteLeftLength); //读取第一个数据区的数据 byte[] leftData = new byte[byteLeftLength]; System.arraycopy(allBytes, 20, leftData, 0, byteLeftLength); String left_name = new String(leftData, StandardCharsets.UTF_8); Log.d(TAG, "connect: 第一个数据区的内容为:" + left_name); //读取第二个数据区编号 byte[] byteNote2 = new byte[4]; System.arraycopy(allBytes, 20 + byteLeftLength, byteNote2, 0, 4); int rightDataNote = byteToInt(byteNote2); Log.d(TAG, "connect: 第二个数据区的编号为:" + rightDataNote); //读取第二个数据区长度 byte[] rightNote = new byte[4]; System.arraycopy(allBytes, 24 + byteLeftLength, rightNote, 0, 4); int rightLength = byteToInt(rightNote); Log.d(TAG, "connect: 第二个数据区的长度:" + rightLength); //读取第二个数据区的数据 byte[] rightData = new byte[rightLength]; System.arraycopy(allBytes, 28 + byteLeftLength, rightData, 0, rightLength); String right_name = new String(rightData, StandardCharsets.UTF_8); Log.d(TAG, "connect: 第二个数据区的内容是:" + right_name); //crc校验 byte[] crcByte = new byte[4]; System.arraycopy(allBytes, 28 + byteLeftLength + rightLength, crcByte, 0, 4); int crc = byteToInt(crcByte); byte[] crcData = new byte[4 + 4 + byteLeftLength + 4 + 4 + rightLength]; System.arraycopy(allBytes, 12, crcData, 0, 16 + byteLeftLength + rightLength); int crcEd = CRC16.calcCrc16(crcData); boolean crcBoolean = crc == crcEd; if (crcBoolean) { Log.d(TAG, "connect: CRC16校验成功"); } else { Log.d(TAG, "connect: CRC校验失败"); } } } }
三:日志和部分工具类
num转byte[],四字节
public static byte[] intToByte(int num) { byte[] bytes = new byte[4]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) (num >> (24 - 8 * i) & 0xFF); } return bytes; }
byte[]转num,四字节
public static int byteToInt(byte[] data) { return ((data[0] & 0xFF) << 24) | ((data[1] & 0xFF) << 16) | ((data[2] & 0xFF) << 8) | (data[3] & 0xFF); }
日志如下: