Java Socket收发数据本质是操作InputStream和OutputStream字节流,需处理读不满、粘包问题,用长度前缀协议,并避免用String封装二进制数据。

Java 中用 Socket 收发数据,本质就是操作它的 InputStream 和 OutputStream。TCP 是字节流协议,不区分消息边界,也不自带编码转换——所以必须用字节流,不能直接依赖字符流(如 BufferedReader)来处理任意数据。
服务端怎么收发字节流
服务端先监听端口,接受连接后,从 Socket 获取输入输出流:
-
接收数据:调用
inputStream.read(byte[] buf),它返回实际读到的字节数(可能小于数组长度),需用这个长度构造字符串或解析二进制内容;不能直接 new String(buf),否则会包含残留垃圾字节。 -
发送数据:用
outputStream.write(byte[] data)写入字节数组;若写的是字符串,必须显式指定编码,例如"hello".getBytes(StandardCharsets.UTF_8)。 -
流关闭顺序:通常先调用
socket.shutdownOutput()表示“我发完了”,再继续读取对方响应;最后才 close 所有流和 socket。
客户端怎么收发字节流
客户端主动连接,拿到 Socket 后行为对称:
- 用
socket.getOutputStream()发送请求,比如文件头、命令或原始字节块。 - 用
socket.getInputStream()接收响应;注意read()可能只读部分数据,尤其在网络不稳定或数据量大时,需循环读直到满足预期长度。 - 不要在发送完就立刻 close 输出流——除非你确定不需要响应;否则服务端可能因连接中断而无法回传结果。
为什么必须处理“读不满”和“粘包”
TCP 不保证一次 read 就拿到完整业务数据。常见两种情况:
立即学习“Java免费学习笔记(深入)”;
-
读不满:比如你分配了 1024 字节数组,但网络只送来 300 字节,
read()返回 300,剩下 724 是空的。直接转字符串会出乱码。 - 粘包:客户端连续 send 两次 "hi" 和 "ok",服务端一次 read 可能收到 "hioK"(合并)或只收到 "hi"(拆包)。TCP 没有消息边界概念。
-
解决办法是加协议:最常用的是“长度前缀法”——发数据前先发 4 字节 int 表示后续数据长度,接收方先读这 4 字节,再循环读够指定字节数。Java 提供
DataInputStream/DataOutputStream的readInt()和writeInt()直接支持。
传输文件或非文本数据的要点
图片、音频、序列化对象等必须走纯字节流:
- 绝对不用
String包裹原始字节——比如new String(bytes)会触发默认平台编码,破坏二进制结构。 - 用
BufferedInputStream/BufferedOutputStream提升效率,但注意缓冲区只是优化手段,不改变字节流本质。 - 传输整个文件夹时,需自行定义结构:比如先发一个标志字节(0x01=文件,0x02=目录),再发路径长度和路径名,再发内容长度和内容。


















