我是靠谱客的博主 年轻康乃馨,最近开发中收集的这篇文章主要介绍基于 TCP 协议的自定义数据帧的形式来进行长数据(图片)的传输,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

图像通过按照如下方式加入到 Socket 的数据包协议中,然后发送并解析,正因为这一步,所以后面我们还需要导入一个 jar 包。

在这里插入图片描述

2.2.1 代码实现

// 通过 Java 的文件流得到的图像数组,Utils 为我自己写的方法

byte[] fileArr1818 = Utils.fileToByteArray(“C:UsersSharmDesktopbokesrcmainresources实验室图像.jpg”);

assert fileArr1818 != null;

System.out.println(“数据正在编码中”);

// 将字节数组利用 Base64 解码为字符串,然后根据数据包协议进行拼接

imgStrs = Base64.encodeBase64String(fileArr1818);

sendStrs = head + insStr + imgStrs + end + terminator;

// 利用 UTF-8 编码为字节数组,用于 Socket 的传输

sendBytes = sendStrs.getBytes(StandardCharsets.UTF_8);

3 Maven

==========================================================================

因为代码中引入了一个新的 jar 包,即import org.apache.commons.codec.binary.Base64; ,所以利用 Maven 来构建该项目是极好的,如果不使用 Maven 的话,直接在该项目中导入这个 jar 包就行。

如果使用 Maven,只需在 pom.xml 中加入以下依赖,即可。

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<maven.compiler.source>11</maven.compiler.source>

<maven.compiler.target>11</maven.compiler.target>

org.apache.directory.studio

org.apache.commons.codec

1.8

不要以为不会 Maven 就放弃这个步骤,相信我,用过 Maven 之后,你就再也不想四处找 jar 包了!

Maven 不难,现阶段就把它当成是一个自动安装 jar 包的工具。有不了解的可以阅读一下这个博客。【传送门】

4 项目流程图

==========================================================================

依据需求,通过画出对应的流程图来理清自己的思路,然后根据流程图写出相应代码。

在这里插入图片描述

5 代码实现

=========================================================================

5.1 客户端代码


import org.apache.commons.codec.binary.Base64;

import java.io.*;

import java.net.Socket;

import java.nio.charset.StandardCharsets;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Scanner;

public class Client {

public static void main(String[] args) throws IOException {

int countConnect = 0;

while (true) {

try {

//连接服务器

Socket s = new Socket(“127.0.0.1”, 5612);

System.out.println(“已连接到服务器5612端口,准备向服务器发送指令。”);

//获取输出流

OutputStream out = s.getOutputStream();

while (true) {

//服务端开始输入指令

System.out.println(“请输入指令:”);

System.out.println(“1818:请求获取实验室目前的图像”);

System.out.println(“1819:请求实验室人员的数量”);

System.out.println(“1820:请求实验室人员的平均补贴”);

Scanner scanner = new Scanner(System.in);

int instruction = scanner.nextInt();

//将int型指令转化为byte[]

byte[] instructionArr = intToByteArray(instruction);

// 利用自定义数据帧的形式发送

String instructionStrs = Base64.encodeBase64String(instructionArr);

String terminator = “rn”;

String sendStrs = “Req0R” + instructionStrs + “End” + terminator;

//一个字符为一个字节,如“Req”为三个字节

byte[] sendBytes = sendStrs.getBytes(StandardCharsets.UTF_8);

// 开始在输出流中发送数据

out.write(sendBytes, 0, sendBytes.length);

//获取开始时间

long startTime = System.currentTimeMillis();

上面是发送,下面是接收

// 为了解决粘包问题所设置的接收代码

InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

int len = 0;

ArrayList arr = new ArrayList();

flag:

while ((len = in.read(buf)) != -1) {

for (byte temp : buf) {

if (temp != ‘n’) {

arr.add(temp);

} else {

break flag;

}

}

//原来两台电脑间通讯遇到的问题可以通过清空

Arrays.fill(buf, (byte) 0);

}

byte[] bs = new byte[arr.size()];

for (int j = 0; j < arr.size(); j++) {

bs[j] = arr.get(j);

}

arr.clear();

// bsStr 就是得到的最终字符串数据

String bsStr = new String(bs, StandardCharsets.UTF_8);

//得到目标文件

//这个可以按步解析出对应的指令

String head = bsStr.substring(0, 4);

String insStr = bsStr.substring(4, 12);

String resqStr = bsStr.substring(12, bsStr.length() - 4);

String end = bsStr.substring(bsStr.length() - 4, bsStr.length() - 1);

byte[] receInsBytes = Base64.decodeBase64(insStr);

byte[] receBytes = Base64.decodeBase64(resqStr);

// 按照不同的指令,进行不同的操作

int receInsInt = byteArrayToInt(receInsBytes);

switch (receInsInt) {

case 1818:

//对于图像,原来缺少几个字节真的不会影响整体

File destFile = new File(“lab.jpg”);

//下面这段代码呢,就是先将字节数组的数据放到数组流中,然后再传给节点流,参考自己的节点流复制代码

//字节数组与程序之间的管道。把数组写到流中

InputStream bais = new ByteArrayInputStream(receBytes);

//程序与新文件之间的管道

OutputStream fos = new FileOutputStream(destFile);

//一样的字节数组缓冲操作

byte[] buf2 = new byte[1024];

int len2;

while ((len2 = bais.read(buf2)) != -1) {

fos.write(buf2, 0, len2);

}

System.out.println(“图片保存完毕。”);

//获取结束时间

long endTime = System.currentTimeMillis();

System.out.printf(“实验室图片传输过来花了%d ms时间:”, (endTime - startTime) / 2);

//流的关闭

bais.close();

fos.close();

break;

case 1819:

int speed = byteArrayToInt(receBytes);

System.out.printf(“此时,实验室人员的数量有 %d 人n”, speed);

break;

case 1820:

int volume = byteArrayToInt(receBytes);

System.out.printf(“实验室人员的平均补贴为 %d 元n”, volume);

break;

default:

break;

}

}

} catch (IOException ex) {

System.out.printf(“服务器断开,请求重连,正在进行第%d次连接。”, countConnect);

System.out.println();

countConnect += 1;

}

}

}

/**

  • int到byte[]

  • @param i 输入待转换的int

  • @return 返回的对应的byte[]

*/

public static byte[] intToByteArray(int i) {

byte[] result = new byte[4];

//由高位到低位

result[0] = (byte)((i >> 24) & 0xFF);

result[1] = (byte)((i >> 16) & 0xFF);

result[2] = (byte)((i >> 8) & 0xFF);

result[3] = (byte)(i & 0xFF);

return result;

}

/**

  • byte[]转int

  • @param bytes 指定的byte[]

  • @return int型的值

*/

public static int byteArrayToInt(byte[] bytes) {

return (bytes[3] & 0xFF) |

(bytes[2] & 0xFF) << 8 |

(bytes[1] & 0xFF) << 16 |

(bytes[0] & 0xFF) << 24;

}

}

5.2 服务器端代码


import org.apache.commons.codec.binary.Base64;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.nio.charset.StandardCharsets;

import java.util.ArrayList;

import java.util.Arrays;

public class Server {

public static void main(String[] args) throws IOException {

//服务器开始监听5612端口

ServerSocket serverSocket = new ServerSocket(5612);

System.out.println(“服务端已启动,正在监听5612端口”);

//等待客户端连接

Socket s = serverSocket.accept();

System.out.println(“得到来自一个客户端的连接”);

//接收服务器的消息

InputStream in = s.getInputStream();

while (true) {

//一样的字节数组缓冲操作,将IO流中的数据放到buf数组中

byte[] buf = new byte[1024];

int len = 0;

ArrayList arr = new ArrayList();

flag:

while ((len = in.read(buf)) != -1) {

for (byte temp : buf) {

if (temp != ‘n’) {

arr.add(temp);

} else {

break flag;

}

}

//本来可以直接用JVM的垃圾自动回收机制,不过socket的read方法必须不为null,所以就用Arrays的方法

Arrays.fill(buf, (byte) 0);

}

byte[] bs = new byte[arr.size()];

for (int j = 0; j < arr.size(); j++) {

bs[j] = arr.get(j);

}

arr.clear();

String bsStr = new String(bs, StandardCharsets.UTF_8);

//原来是要在String中截取出字串,而不能在下面这个字节数组中拿出子数组来得到instruction1818。

//截取服务器请求。协议:

String insStr = bsStr.substring(5, bsStr.length()-4);

String head = “Resp”;

String end = “End”;

byte[] insBytes = Base64.decodeBase64(insStr);

System.out.println(Arrays.toString(insBytes));

//得到的是服务器的指令

int ins = byteArrayToInt(insBytes);

System.out.println(“服务器发过来的指令是:” + ins);

String imgStrs = null;

String sendStrs = null;

byte[] sendBytes = null;

String terminator = “rn”;

switch (ins) {

case 1818:

System.out.println(“正在打包图像”);

// 下面的图像的路径我写的是该图像在我电脑中的绝对路径

byte[] fileArr1818 = Utils.fileToByteArray(“C:UsersSharmDesktopbokesrcmainresources实验室图像.jpg”);

assert fileArr1818 != null;

System.out.println(“数据正在编码中”);

imgStrs = Base64.encodeBase64String(fileArr1818);

sendStrs = head + insStr + imgStrs + end + terminator;

sendBytes = sendStrs.getBytes(StandardCharsets.UTF_8);

break;

case 1819:

byte[] fileArr1819 = intToByteArray(18);

System.out.println(“数据正在编码中……”);

imgStrs = Base64.encodeBase64String(fileArr1819);

sendStrs = head + insStr + imgStrs + end + terminator;

sendBytes = sendStrs.getBytes(StandardCharsets.UTF_8);

break;

case 1820:

byte[] fileArr1820 = intToByteArray(400);

System.out.println(“数据正在编码中……”);

imgStrs = Base64.encodeBase64String(fileArr1820);

sendStrs = head + insStr + imgStrs + end + terminator;

sendBytes = sendStrs.getBytes(StandardCharsets.UTF_8);

break;

default:

break;

}

OutputStream out = s.getOutputStream();

System.out.println(“开始发送”);

assert sendBytes != null;

out.write(sendBytes, 0, sendBytes.length);

//测试发送的字节数组的前几个字节,看看是否正确

System.out.println(Arrays.toString(Arrays.copyOfRange(sendBytes, 0, 16)));

System.out.println(“发送完毕。n”);

}

}

/**

  • int到byte[]

  • @param i 输入待转换的int

  • @return 返回的对应的byte[]

*/

public static byte[] intToByteArray(int i) {

byte[] result = new byte[4];

//由高位到低位

result[0] = (byte)((i >> 24) & 0xFF);

result[1] = (byte)((i >> 16) & 0xFF);

result[2] = (byte)((i >> 8) & 0xFF);

result[3] = (byte)(i & 0xFF);

return result;

}

/**

  • byte[]转int

  • @param bytes 指定的byte[]

  • @return int型的值

*/

public static int byteArrayToInt(byte[] bytes) {

return (bytes[3] & 0xFF) |

(bytes[2] & 0xFF) << 8 |

(bytes[1] & 0xFF) << 16 |

(bytes[0] & 0xFF) << 24;

}

}

5.3 工具类:通过使用 Java 文件流将本地图像导入到程序中


import java.io.*;

public class Utils {

/**

  • 利用System.arraycopy的方法在字节数组中截取指定长度数组

  • @param src

  • @param begin

  • @param count

  • @return

*/

public static byte[] subBytes(byte[] src, int begin, int count) {

byte[] bs = new byte[count];

System.arraycopy(src, begin, bs, 0, count);

return bs;

}

/**

  • 利用Java字节流的方式将图像转化为字节数组

  • 图片到程序FileInputStream

  • 程序到数组 ByteArrayOutputStream

*/

public static byte[] fileToByteArray(String FilePath){

//1.创建源与目的的

File src=new File(FilePath);

//在字节数组输出的时候是不需要源的。

byte[] dest=null;

//2.选择流,选择文件输入流

//方便在finally中使用,设置为全局变量

InputStream is=null;

ByteArrayOutputStream os=null;

try {

is=new FileInputStream(src);

os=new ByteArrayOutputStream();

//3.操作,读文件

//10k,创建读取数据时的缓冲,每次读取的字节个数。

byte[] flush=new byte[1024*10];

//接受长度;

int len=-1;

while((len=is.read(flush))!=-1) {

//表示当还没有到文件的末尾时

//字符数组–>字符串,即是解码。将文件内容写出字节数组

os.write(flush,0,len);

最后

以上就是年轻康乃馨为你收集整理的基于 TCP 协议的自定义数据帧的形式来进行长数据(图片)的传输的全部内容,希望文章能够帮你解决基于 TCP 协议的自定义数据帧的形式来进行长数据(图片)的传输所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(78)

评论列表共有 0 条评论

立即
投稿
返回
顶部