Socket和ServerSocket传输自定义文件

一:客户端代码

客户端传输协议:所有的整数采用大端序,当CRC校验不通过时,客户端需要重传1次(当前只是成功,不成功服务端回传个客服端信息即可)。仍然失败则提示,发送失败。

字节内容说明
40x1111AAAA魔数
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);
}

日志如下:

 

对于网络文件传输,可以使用自定义协议来进行二进制传输。在C语言中,你可以使用套接字(socket)来实现网络通信。 首先,你需要创建一个服务器端和一个客户端。服务器端负责接收文件并保存,客户端负责发送文件。 在服务器端,你可以使用以下步骤来实现文件接收: 1. 创建套接字: ```c int serverSocket = socket(AF_INET, SOCK_STREAM, 0); ``` 2. 绑定套接字到一个特定的IP地址和端口: ```c struct sockaddr_in serverAddress; serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(PORT); // 选择一个合适的端口号 serverAddress.sin_addr.s_addr = INADDR_ANY; bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); ``` 3. 监听连接请求: ```c listen(serverSocket, 5); // 允许最多5个连接请求 ``` 4. 接受连接请求: ```c int clientSocket = accept(serverSocket, NULL, NULL); ``` 5. 接收文件数据: ```c FILE *file = fopen("received_file.bin", "wb"); // 打开一个二进制文件用于保存接收的数据 char buffer[1024]; int bytesRead; while ((bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) { fwrite(buffer, 1, bytesRead, file); // 将接收到的数据写入文件 } fclose(file); ``` 在客户端,你可以使用以下步骤来实现文件发送: 1. 创建套接字: ```c int clientSocket = socket(AF_INET, SOCK_STREAM, 0); ``` 2. 连接到服务器: ```c struct sockaddr_in serverAddress; serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(PORT); // 使用服务器的端口号 inet_pton(AF_INET, SERVER_IP, &(serverAddress.sin_addr)); connect(clientSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); ``` 3. 打开要发送的文件: ```c FILE *file = fopen("file_to_send.bin", "rb"); // 打开一个二进制文件用于发送 if (file == NULL) { printf("无法打开文件\n"); return -1; } ``` 4. 发送文件数据: ```c char buffer[1024]; int bytesRead; while ((bytesRead = fread(buffer, 1, sizeof(buffer), file)) > 0) { send(clientSocket, buffer, bytesRead, 0); // 发送文件数据 } fclose(file); ``` 这样,你就可以使用自定义协议在C语言中实现网络文件传输的二进制传输部分。当然,你还可以根据需求对协议进行更多的定义和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值