socket.getinputstream()和outputstream()必须在连接成功后调用,否则抛ioexception;未连接、已关闭、多线程并发close或未flush均会导致读写异常。

Socket.getInputStream() 和 Socket.getOutputStream() 什么时候可用
必须在 Socket 成功连接后才能调用,否则会抛 IOException: Socket is not connected。常见错误是新建 Socket 实例后立刻调 getInputStream(),但还没执行 connect() 或构造时没指定地址(如 new Socket() 后忘掉 connect(new InetSocketAddress(...)))。
实操建议:
- 使用带地址的构造函数最安全:
new Socket("localhost", 8080),内部自动完成连接,流可立即获取 - 若用无参构造,务必检查
socket.isConnected()和!socket.isClosed()再取流 - 阻塞模式下,
connect()返回即表示已连通;非阻塞需配合isConnectionPending()+finishConnect()
输入流读不到数据?检查 close() 和 shutdown() 的副作用
Socket.shutdownInput() 或对方主动关闭连接,会导致 InputStream.read() 返回 -1(正常 EOF),但很多人误以为是网络异常。更隐蔽的问题是:调用 socket.close() 会同时关闭底层输入输出流,后续再对流操作会抛 IOException: Stream closed。
实操建议:
- 不要在读循环外提前关闭
Socket;读到 -1 表示对方已断开,此时才应清理资源 - 避免对同一
Socket多次调getInputStream()并分别关闭——流是共享的,第二次close()会失败 - 使用 try-with-resources 时,只包装
BufferedInputStream等装饰流,别直接包装socket.getInputStream(),否则可能提前关掉 socket
为什么 write() 后对方收不到数据?缓冲区和 flush() 的关系
TCP 是字节流协议,OutputStream.write() 默认写入内核发送缓冲区,并不保证立即发出去。如果只写小数据且未调 flush(),又没触发 TCP Nagle 算法的合并条件(如未满 MSS、前一个包未确认),数据就会卡住。
实操建议:
- 对
Socket.getOutputStream()套一层BufferedOutputStream时,每次写完必须显式flush(),否则缓冲区不会清空 - 禁用 Nagle 算法可减少延迟:
socket.setTcpNoDelay(true),适合实时性要求高的场景(如游戏、RPC) - 注意
PrintWriter构造时第二个参数为true才自动 flush,否则println()也不发数据
多线程下共用 Socket 流的安全边界
InputStream 和 OutputStream 本身不是线程安全的,但只要不交叉读写同一 Socket 的同一方向流(比如两个线程同时 read()),就无竞争。真正危险的是:一个线程在读,另一个线程调 socket.close(),导致读线程抛 SocketException: Socket closed。
实操建议:
- 读写分离:固定一个线程负责
read(),另一个线程负责write(),避免同步锁流对象 - 关闭控制权集中:由主线程或连接管理器统一 close,其他线程检测流是否可用(如
socket.isClosed())再决定是否退出 - 不要用
synchronized(socket)保护流操作——synchronized 对 native 层 I/O 无效,且容易死锁
最常被忽略的点:流对象本身不维护连接状态,所有判断都得回溯到 Socket 实例。哪怕你拿到 InputStream,也得通过 socket.isConnected() && !socket.isClosed() 确认它还能用。










