前言:最近做安卓端项目需要用到socket和PC端互发指令,但是期间遇到了这么一个现象,就是有时候PC端消息指令发的过快,导致安卓端接收了多条粘在一起的指令,这就是粘包现象。
解析:socket在发送数据的时候会对数据进行粘包和分包处理,这导致我们需要额外处理,虽然看上去没有必要(麻烦、懒得处理),但是这是socket做的优化。
什么是粘包?
就是在极短时间内发送了几次很少的数据量,正常是接收端应该也同样接收了好几次,但是发送端却把这几次的数据合成一起,一次发了过来,几个数据“粘”在了一起。
什么是拆包?
当发送端一次性发送了大量的数据时,socket将数据拆分,按批次发送,数据被“拆”开发送了。
解决方案:
1、添加标记
可以在每条指令后面加特殊字符,比如“%#*”,这样多个特殊字符集合,然后接收端按照规定的切分就行了。
2、包头+包体
因为接受的时候是字节,所以在每条消息之前加一个定长的字节数组代表包体的长度。
网上搜了一位大神的,感觉挺好的,亲测可用传送门
解决实例:(包头+包体)
首先推荐一个很好用的socket框架,内部封装的很好,可以监听连接状态,我是基于这个基础上修改的。传送门
package com.hite.communicate.socket;
import android.os.Message;
import android.util.Log;
import com.hite.communicate.CustomMessageConstant;
import com.hite.communicate.utils.DataUtils;
import com.hite.communicate.utils.EventBusUtil;
import com.koushikdutta.async.AsyncServer;
import com.koushikdutta.async.AsyncServerSocket;
import com.koushikdutta.async.AsyncSocket;
import com.koushikdutta.async.ByteBufferList;
import com.koushikdutta.async.DataEmitter;
import com.koushikdutta.async.Util;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.callback.DataCallback;
import com.koushikdutta.async.callback.ListenCallback;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Server {
private InetAddress host;
private int port;
private AsyncServerSocket asyncServerSocket;
private AsyncSocket asyncsocket;
private final int HEADER_LENGTH = 4;
private byte[] mData;
public Server(String host, int port) {
try {
this.host = InetAddress.getByName(host);
this.port = port;
setup();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
private void setup() {
asyncServerSocket = AsyncServer.getDefault().listen(host, port, new ListenCallback() {
@Override
public void onAccepted(final AsyncSocket socket) {
if(asyncsocket==null) {
asyncsocket = socket;
handleAccept(socket);
}
}
@Override
public void onListening(AsyncServerSocket socket) {
}
@Override
public void onCompleted(Exception ex) {
}
});
}
public void clear() {
mData=null;
if(asyncsocket!=null) {
asyncsocket.end();
asyncsocket.close();
asyncsocket=null;
}
if(asyncServerSocket!=null) {
asyncServerSocket.stop();
}
}
public void sendToPcMessage(String message) {
if(asyncsocket!=null) {
Util.writeAll(asyncsocket, DataUtils.getSendMsg(message.getBytes()), new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
}
});
}
}
private void handleAccept(final AsyncSocket socket) {
socket.setDataCallback(new DataCallback() {
@Override
public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {
byte[] data = bb.getAllByteArray();
if(mData!=null) {
receiveMsg(DataUtils.byteMerger(mData,data));
mData=null;
} else {
receiveMsg(data);
}
}
});
socket.setClosedCallback(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
asyncsocket=null;
mData=null;
}
});
socket.setEndCallback(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
}
});
}
private void receiveMsg(byte[] data) {
if(data.length>HEADER_LENGTH) {
byte[] size = new byte[HEADER_LENGTH];
System.arraycopy(data,0,size,0,HEADER_LENGTH);
int bodySize = DataUtils.byteArrayToInt(size);
Log.e("threadMode",bodySize+"receiveMsg");
int totalLength = bodySize+HEADER_LENGTH;
//不够一个包
if(data.length<totalLength) {
mData=data;
return;
//多于一个包
} else if(data.length>totalLength) {
DataUtils.dispatchMsg(data,bodySize);
byte[] otherData = new byte[data.length-totalLength];
System.arraycopy(data,totalLength,otherData,0,data.length-totalLength);
receiveMsg(otherData);
//正好一个包
} else {
DataUtils.dispatchMsg(data,bodySize);
}
} else {
mData = data;
}
}
}
package com.hite.communicate.utils;
import android.os.Message;
import com.hite.communicate.CustomMessageConstant;
import java.io.UnsupportedEncodingException;
public class DataUtils {
//包头+包体
public static byte[] getSendMsg(byte[] src) {
byte[] head = intToByteArray(src.length);
return byteMerger(head,src);
}
//字节数组转int
public static int byteArrayToInt(byte[] b) {
return b[3] & 0xFF |
(b[2] & 0xFF) << 8 |
(b[1] & 0xFF) << 16 |
(b[0] & 0xFF) << 24;
}
//int转字节数组
public static byte[] intToByteArray(int a) {
return new byte[] {
(byte) ((a >> 24) & 0xFF),
(byte) ((a >> 16) & 0xFF),
(byte) ((a >> 8) & 0xFF),
(byte) (a & 0xFF)
};
}
//数组合并
public static byte[] byteMerger(byte[] byte_1, byte[] byte_2){
byte[] byte_3 = new byte[byte_1.length+byte_2.length];
System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
return byte_3;
}
//拆消息体
public static void dispatchMsg(byte[] data,int bodySize) {
byte[] msgByte = new byte[bodySize];
System.arraycopy(data,4,msgByte,0,bodySize);
try {
Message message = new Message();
message.what= CustomMessageConstant.PC_MESSAGE;
message.obj = new String(msgByte,"UTF-8");
EventBusUtil.sendMessage(message);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}