serversocket.accept() 卡住是因阻塞设计,需客户端连接才返回;应绑定0.0.0.0、关闭防火墙、用线程池处理多连接、及时关闭资源并加心跳机制。

为什么 ServerSocket.accept() 会卡住?
因为它是阻塞调用,没客户端连上来就一直等。这不是 bug,是设计使然——但新手常误以为程序“卡死”了。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 确保客户端真的执行了
new Socket("127.0.0.1", 8080),而不是只写了服务端代码就去点运行 - 防火墙或杀毒软件可能拦截
accept()的监听行为,临时关闭试试 - 别在主线程里反复调用
accept()却不启动新线程处理连接,否则第二个客户端永远进不来 - 加个超时更可控:
serverSocket.setSoTimeout(5000),抛SocketTimeoutException后可做心跳或日志
多个客户端怎么同时收发消息而不互相干扰?
靠每个连接对应一个独立的 Socket 实例 + 单独线程(或 ExecutorService)处理读写。共享同一个 InputStream 或共用一个 BufferedReader 就会乱序、丢数据。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 每次
accept()返回新Socket,立刻交给新线程:new Thread(new ClientHandler(clientSocket)).start() - 不要在多个线程里共用同一个
PrintWriter,每个客户端配自己的输出流 - 读取消息推荐用
BufferedReader.readLine(),别用InputStream.read()逐字节——容易卡在换行符上 - 发送广播时,遍历所有活跃的
PrintWriter,跳过当前发送者,避免回显
Socket 关闭后还往里写数据会怎样?
会抛 IOException: Broken pipe 或 SocketException: Connection reset。不是所有关闭都立即报错,有时写缓存还能撑几条,但后续必崩。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 检测客户端断开:读到
null(readLine()返回值)就是 EOF,该清理资源了 - 捕获
IOException后,必须手动调用socket.close()和对应流的close(),否则端口可能被占着不放 - 别依赖
socket.isClosed()判断是否可写——它只反映本地是否调用了close(),不表示对方是否还活着 - 加个简单的心跳机制:每隔 30 秒发个
"PING",没回"PONG"就主动关连接
为什么本地测试通,一换局域网 IP 就连不上?
大概率是服务端绑定到了 127.0.0.1(localhost),这个地址只响应本机请求。其他设备访问的是你的真实局域网 IP(比如 192.168.1.102),但服务端根本没监听它。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 构造
ServerSocket时用new ServerSocket(8080)(无绑定地址),它默认监听0.0.0.0,即所有网卡 - 如果指定了地址,确认写的是
InetAddress.getByName("0.0.0.0")或空字符串,而不是"127.0.0.1" - 检查路由器是否开启 AP 隔离,开了的话同一 Wi-Fi 下的设备彼此 ping 不通,更别说连 Socket
- 安卓模拟器或某些 IDE 内置终端访问宿主机要用
10.0.2.2,不是127.0.0.1—— 这个细节漏掉就会白调半天
真正麻烦的从来不是“怎么写完”,而是连接异常时的资源清理顺序、多线程下共享集合的并发修改、还有 NAT 网络里那些看不见的中间设备对长连接的静默回收。这些地方不写日志、不加 try-catch、不测断网重连,上线后第一波用户进来就露馅。











