概述
图像通过按照如下方式加入到 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 协议的自定义数据帧的形式来进行长数据(图片)的传输所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复