「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」
Socket TCP如何传递字符串和字节数组
我们通过Socket TCP 可以进行双向通信,我们用聊天工具聊天的时候,不仅仅可以发送文字,也可以发送图片,这是怎么实现的呢,又是怎么区分文字和图片呢?
本质
计算机上所有的数据归根到底,都是0和1组成,无论我们发送什么样的数据,最终都要转成0和1进行存储。
在java中,我们最小操作的单位是字节,所以无论我们发送图片或者文字,都要通过字节数组作为载体进行发送。
字符串的实现方式
方法一
服务端:每次发送数据时,在数据结尾处,拼上换行符。
public void sendString(final String msg) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (LOCK) {
try {
// 拼上换行符
String data = msg + "\n";
out.write(data.getBytes());
// 强制把数据输出
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
客户端:使用readLine方法
/**
* 创建接收线程
*/
private void startReceiveTcpThread() {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
// 获取数据
String line = "";
try {
while ((line = br.readLine()) != null) {
handleReceiveTcpMessage(line);
}
if (line == null) {
LogWrapper.e(TAG, "line == null(上位机断开),开始断开...");
disConnect(); // 上位机断开
}
} catch (IOException e) {
e.printStackTrace();
// 没有正常关闭时会出现
LogWrapper.e(TAG, "接收消息异常, 开始断开...:" + e.toString());
disConnect();
}
}
});
}
方法二
获取字节数组,字节数组转String.
//读取服务端返回的数据,使用socket读取流
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int len = inputStream.read(buf);
String textString = new String(buf, 0, len);
System.out.println(textString);
socket.close();
同时接收字符串和字节数组
因为readLine 方法读取的是换行符,这肯定行不通了;我们就尝试第二种方法,在发送端的时候,尝试去区分类型。比如
发送端:
定义两个不同的常量,作为标记,区分不同的类型
final byte BYTE_TYPE = 0x1;
final byte STRING_TYPE = 0x2;
// byte数组类型
out.write(BYTE_TYPE);
out.write(bytes);
// string 类型
out.write(STRING_TYPE);
out.write(string.getBytes());
接收端:
提取类型,并做相应的操作
private void startReceiveTcpThread() {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
while (true) {
try {
byte[] bytePacket = new byte[BUFFER_LENGTH];
int actLength = mSocket.getInputStream().read(bytePacket);
byte[] actPacket = new byte[actLength];
System.arraycopy(bytePacket, 0, actPacket, 0, actLength);
int type = actPacket[0];
if (type == BYTE_TYPE){
// 去除第一个类型后,得到真实的byte
}
if (type == STRING_TYPE){
...
// 去除第一个类型后,再进行转换
new String() ;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
仔细看这段代码 byte[] bytePacket = new byte[BUFFER_LENGTH],实际上我们将 BUFFER_LENGTH 设置成什么值,都不合适,正确的做法应该是固定BUFFER_LENGTH的值,然后做合包操作。
分包合包下篇文章再讲。