Java Socket编程入门:TCP连接与多线程通信实战
Java Socket编程是实现C/S(客户机/服务器)架构的核心技术。通过理解端口分配机制,掌握TCP流式Socket的连接建立、数据输入输出流操作,并利用线程技术解决并发通信问题,开发者可以快速构建稳定的局域网或互联网应用。
Socket编程基础与C/S架构
在网络应用中,常见的模式包括主机/终端模式和客户机/服务器(Client/Server, C/S)模式。WWW服务即建立在C/S模式之上,利用HTML和HTTP协议提供跨平台信息服务。在C/S模式中,服务器端需同时响应多个客户端请求。为了标识特定的通信线程,除了IP地址外,还引入了端口号(Port)。IP地址与端口号的组合被称为网络套接字(Socket)。
端口管理规范
- 0~1023:系统专用端口。例如HTTP协议默认使用80端口,Telnet使用23端口。
- 1024~65535:供应用程序自定义使用。
两种Socket通信方式对比
Java中根据传输协议的不同,主要分为流式和数据报式两种Socket:
- 流式Socket (TCP):面向连接的通信。在数据传输前必须建立连接,传输结束后断开。优势在于传输的正确性和可靠性,适用于需要保证数据完整性的场景。
- 数据报Socket (UDP):无连接的通信。将数据打包直接发送,无需建立和拆除连接,速度较快,但不提供可靠性保证。
基于TCP的Socket编程步骤
流式Socket通信遵循固定的操作顺序:建立连接 → 获取输入/输出流 → 数据读写 → 关闭连接。以下是核心类及其使用方法。
第1步:客户端连接服务器
在客户端,使用 Socket 类发起连接。构造函数通常需要指定服务器的IP地址(或域名)和端口号。
try {
// 创建Socket对象,连接至localhost的1860端口
Socket mySocket = new Socket("localhost", 1860);
} catch (IOException e) {
System.out.println("连接失败");
}
连接成功后,通过 getInputStream() 获取输入流以读取服务端数据,通过 getOutputStream() 获取输出流以向服务端写入数据。
第2步:服务器端监听与接受连接
在服务端,首先创建 ServerSocket 对象并绑定监听端口。随后调用 accept() 方法阻塞等待客户端请求。
try {
// 创建ServerSocket并监听1860端口
ServerSocket serverSocket = new ServerSocket(1860);
// 阻塞等待,直到有客户端连接
Socket sc = serverSocket.accept();
} catch (IOException e) {
System.out.println("服务端错误");
}
accept() 返回一个 Socket 对象,该对象代表与特定客户端的连接通道。使用该对象即可进行后续的数据交互。通信完成后,记得调用 close() 释放资源。
第3步:实现双向数据传输示例
以下示例演示了基础的C/S通信:客户端发送字符串,服务端接收并回复确认信息。
客户端代码 (Client.java):
import java.io.*;
import java.net.*;
public class Client {
public static void main(String args[]) {
Socket mySocket = null;
DataInputStream in = null;
DataOutputStream out = null;
try {
// 连接本地主机的4441端口
mySocket = new Socket("localhost", 4441);
// 获取数据流
in = new DataInputStream(mySocket.getInputStream());
out = new DataOutputStream(mySocket.getOutputStream());
// 发送消息
out.writeUTF("good server!");
// 循环读取服务器返回的消息
String s;
while ((s = in.readUTF()) != null) {
System.out.println(s);
break; // 简单起见只读一次
}
mySocket.close();
} catch (IOException e) {
System.out.println("can't connect");
}
}
}
服务端代码 (Server.java):
import java.io.*;
import java.net.*;
public class Server {
public static void main(String args[]) {
ServerSocket server = null;
Socket you = null;
DataInputStream in = null;
DataOutputStream out = null;
try {
// 监听4441端口
server = new ServerSocket(4441);
} catch (IOException e1) {
System.out.println("ERROR: " + e1);
return;
}
try {
// 接受客户端连接
you = server.accept();
in = new DataInputStream(you.getInputStream());
out = new DataOutputStream(you.getOutputStream());
// 读取客户端消息
String s;
if ((s = in.readUTF()) != null) {
// 回复客户端
out.writeUTF("客户,你好,我是服务器");
}
out.close();
you.close();
} catch (IOException e) {
System.out.println("ERROR: " + e);
}
}
}
提升并发能力:引入多线程
上述单线程服务端模型在接收到一个客户端后,会阻塞在该连接的处理上,无法立即响应其他客户端。为了充分利用计算机的多核处理能力,应为每个客户端连接启动独立的线程。
第4步:构建多线程服务端
服务端逻辑调整为:监听端口 → 接受连接 → 为每个新连接创建一个新线程 → 线程内完成数据读写。这样即使某个客户端连接断开或正在传输大数据,也不会影响其他客户端的正常通信。
多线程服务端核心逻辑示意:
// 在Server类的main循环中
while (true) {
Socket socket = serverSocket.accept();
// 创建新线程处理该连接
Thread thread = new Thread(new ClientHandler(socket));
thread.start();
}
相应的,客户端也可以采用线程来处理后台接收消息的操作,避免界面冻结。在GUI应用中,可以将接收逻辑放在独立线程中,通过事件调度线程更新UI显示,从而实现流畅的双向聊天功能。
总结与注意事项
Java Socket编程是网络应用的基石。开发时需特别注意以下几点:
- 异常处理:网络I/O操作极易抛出
IOException,务必使用 try-catch 块包裹。 - 资源关闭:无论通信是否成功,最终都必须按顺序关闭
OutputStream、InputStream和Socket,防止端口和资源泄露。 - 数据安全:对于敏感数据,原生Socket仅提供了二进制传输通道,实际生产中应结合SSL/TLS加密或使用高级框架(如Netty)。
以上就是Java Socket网络编程入门及多线程通信的详细内容,更多关于Java网络编程的基础资料和进阶教程请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













