这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战
搞懂Socket通信(四)
上篇文章讲解了基本的Socket TCP长链接,这篇文章是对Socket TCP的进阶。探索它的高级用法和遇到的疑难杂症。
一、分包粘包如何处理
原因
TCP 是流的形式,在某些条件下对数据进行了优化
场景
当前发送方发送了两个包,两个包的内容如下:
123456789
ABCDEFGH
复制代码
我们希望接收方的情况是:收到两个包,第一个包为:123456789,第二个包为:ABCDEFGH。但是在粘包和分包出现的情况就达不到预期情况。
粘包表现
两个包在很短的时间间隔内发送,比如在0.1秒内发送了这两个包,如果包长度足够的话,那么接收方只会接收到一个包,如下:
123456789ABCDEFGH
复制代码
粘包解决方法
- 约定结束符,遇到结束符时分割数据
- 约定数据长度,根据数据长度截取
这里最简单的方法是
在发送数据的时候末尾拼上换行符,在接收数据时使用BufferedReader
的rd.readLine()
进行接收。
readLine
代表读取了一行的内容。
@Override
public void run() {
while (true) {
try {
InputStream inputStream = socket.getInputStream();
out = socket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String str = reader.readLine();
if (str == null) continue;
if (listener != null) {
listener.onReceive(this, str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
分包情况
假设包的长度最长设置为5字节(较极端的假设,一般长度设置为1000到1500之间),那么在没有粘包的情况下,接收方就会收到4个包,如下:
12345
6789
ABCDE
FGH
复制代码
分包解决方法
在粘包场景中,是需要分割数据。
在分包场景中,需要将多个包合在一起。
解决方法
约定一个分隔符,当该条数据中包含该分割符时进行分割,不包含分隔符时,就合并下一条数据,包含分割符时就进行分割,提取数据。
二、如何传输byte和String
2.1 发送
我们平时是怎么发送byte和String的呢?举例代码:
String str = "hello world"+ "\n";
byte[] bytes = 图片字节流;
// 获取socket输出流
OutputStream out = socket.getOutputStream();
// 发送字符串
out.write(str.getBytes());
// 发送byte数组
out.write(bytes);
复制代码
发送代码部分很简单,即使是String也是转了byte发送的。那客户端在接收的时候如何做区分呢,客户端怎么知道接收的byte是要转字符串,还是转图片或其它类型呢?
那么就需要改进代码了,在发送端增加一个标识
,客户端在接收到消息的时候,根据标识
进行区分,这样接收端就知道消息该怎么转了。
修改之后的发送端代码举例:
// 定义两个标识
final int SOCKET_STRING = 1;
final int SOCKET_BYTE = 2;
public void sendTcpPacket(final byte[] packet, final SendCallback sendCallback) {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
try {
OutputStream outputStream = mSocket.getOutputStream();
outputStream.write("标识");
outputStream.write(packet);
if (sendCallback != null)
sendCallback.success();
} catch (Exception e) {
if (sendCallback != null)
sendCallback.failed();
}
}
});
}
复制代码
2.2 接收
接收到字节,判断第一个标识位是什么,则对应相应的解析操作。
@Override
public void run() {
while (true) {
try {
InputStream inputStream = socket.getInputStream();
DataInputStream input = new DataInputStream(inputStream);
byte[] buffer = new byte[2048];
//消息长度
int realLength = input.read(buffer, 0, 2048);
System.out.println("接收的消息长度:" + realLength);
//传输的实际byte[]
byte[] buffer1 = new byte[realLength];
for (int i = 0; i < buffer1.length; i++) {
buffer1[i] = buffer[i];
}
if (listener != null) {
listener.onReceive(this, buffer1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END