先上github地址:https://github.com/szd201208/PuddingSocket/tree/master。
以下直接贴相关代码。
1.获取socket数据流
Socket socket=new Socket();
socket.connect(new InetSocketAddress(host, port));
InputStream mSocketInputStream = socket.getInputStream();
OutputStream mSocketOutputStream = socket.getOutputStream();
维护消息队列
LinkedBlockingDeque<String> mWriteBlockingQueue = new LinkedBlockingDeque<>();
2.数据形式为字符串
发送
String queueMessage = mWriteBlockingQueue.take();
if (queueMessage != null) {
Log.e(TAG, "write ---> " + queueMessage);
mSocketOutputStream.write(String.valueOf(queueMessage+"\r\n").getBytes());
mSocketOutputStream.flush();
}
接收
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(mSocketInputStream));
String readlineStr = bufferedReader.readLine();
分析:数据格式为字符串时,我们可以使用Io自带的readLine方法来读取单条数据,但readLine的时候,我们需要在已有字符串后面手动添加\r,\n或者\r\n。
readLine的相关源码如下:
这样就会导致一个问题,当我们的字符串中有\r,\n或\r\n相关字段时,一条消息就会被裁切成多条消息。
3.数据形式为byte数组
如果发送的内容不是字符串,而是一个文件,一张图片或者一段语音呢?按照上面的思路,我们有两种处理方案:当我们发送非字符串内容时,可以先调用接口发送文件至后台,当后端返回相关url之后,我们就可以将url拼接到相关字符串里,之后按照上面的方式来发送字符串数据;第二个方案就是直接发送byte数组,因为后端调接口发送文件的实质也是发送byte数组。以下我们着重就第二种方案进行分析。
因为tcp会有粘包和拆包出现,那么我们又是怎么判定一条消息的完整性呢?
基本思路就是前后端约定好byte数组的格式,比如我们约定好每一条完整的消息包含包头+包体,其中包体为消息实体的byte数组格式(1),包头为(1)中数组长度l所对应的byte数组,长度暂定为4(2),完整消息的byte数组=(1)+(2)。
以字符串为例,在LinkedBlockingDeque中,我们可以维护一个SocketMessage。
消息发送
if (queueMessage != null) {
Log.e(TAG, "write ---> " + queueMessage);
byte[] result = queueMessage.getResult();
mSocketOutputStream.write(result);
mSocketOutputStream.flush();
}
消息接收
byte[] resultLength = getBytesWithLength(inputStream, 4);
if (resultLength != null) {
int length = big_bytesToInt(resultLength); //byte[]转为int
//从inputStream获取length个字节内容,即为消息实体
resultBytes = getBytesWithLength(inputStream, length);
if (resultBytes != null) {
String res = new String(resultBytes); //byte[]转为String
}
}
4.拓展
以上发送byte数组我们是以字符串为例,那么如果我们同时需要发送String,图片,视频,音频等内容,SocketMessage我们又该如何定义呢?