【Java】网络编程基础与聊天室架构分析
IP 和 端口号
IP 用于标识网络中的主机 , 是如计算机 , 路由器等网络层设备的地址 .
端口号用于标识主机上运行的具体进程或服务 , 是传输层的逻辑接口 .
常见的 IPv4 地址为 32 位长 , 通常写成点分十进制形式 , 如 192.168.1.100
. IPv4 地址范围从 0.0.0.0
到 255.255.255.255
, 其中较为特殊的是 回环地址 127.0.0.1
, 表示当前设备本身 .
端口号为 16 位整数 . 取值范围 0 - 65535 , 其中 0 - 1023 为通常由操作系统保留的系统端口,1024 - 65535 为用户或动态分配的端口 .
IP 类似于主机的门牌号 , 确定访问网络的设备的具体地址 . 端口号类似于门牌上的门或信箱 , 用来区分该主机上的不同网络服务 .
TCP 和 UDP
TCP 是一种面向连接的可靠传输协议 . TCP 在传输前需要经过三次握手建立连接 , 传输过程中提供错误校验 , 重传 , 顺序控制 , 流量控制和拥塞控制 , 确保数据无差错 , 不丢失 , 不重复 , 按序到达 .
因此 TCP 适用于对数据完整性和准确性要求较高的场景 , 如文件传输 , 电子邮件 , Web 浏览等 . 由于功能开销大 , TCP 头部长度固定为 20 字节以上 , 传输成本较高 .
UDP 是一种无连接的不可靠传输协议 . UDP 发送数据前不建立连接 . 不提供重传和顺序保证 , 只尽最大努力交付 . UDP 头部只有 8 字节 . 开销较小 .
因此 UDP 适用于实时性要求高 , 对丢包容忍度较大的场景 , 如网络语音 , 视频会议 , 在线游戏 , DNS查询等 .
TCP 三次握手与四次挥手
TCP 使用三次握手的过程来建立可靠的连接 , 确保通信双方具备收发能力 , 并提前协商好消息传输的顺序 .
其流程如下 :
- 客户端向服务器发送连接请求 , 表明希望建立通信 .
- 服务器收到请求后 , 回复一个应答 , 表示同意连接 , 同时也发出自己的连接请求 .
- 客户端再回应一个确认消息 , 表示双方可以开始通信 .
至此 , 客户端和服务器之间的连接正式建立 , 可以开始稳定的数据传输 .
而连接的断开则采用四个步骤进行 , 目的是让双方都有机会完成剩余的数据发送 , 避免信息丢失 .
关闭流程如下 :
- 一方先提出断开连接的请求 .
- 另一方收到后 , 先表示收到了请求 , 但还会继续发送剩余的数据 .
- 等数据发完后 , 再向对方表示自己也准备断开 .
- 对方再确认一次 , 并稍作等待 , 确保断开消息被成功收到 , 然后才完全关闭连接 .
这种方式确保了通信中没有遗漏或突然中断 , 体现了 TCP 协议核心安全特性 .
Java TCP通信
Java 使用 ServerSocket 和 Socket 类实现 TCP 通信 . 服务器端代码示例 :
// Server.java - 服务器端
ServerSocket server = new ServerSocket(12345); // 在端口12345监听
Socket client = server.accept(); // 接受客户端连接
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); // 读客户端消息
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); // 写出消息到客户端
String msg = in.readLine(); // 读取一行文本
out.write("服务器收到了: " + msg + "\n");
out.flush();
客户端代码示例 :
// Client.java - 客户端
Socket socket = new Socket("localhost", 12345); // 连接到本机12345端口
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); // 写消息到服务器
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 读服务器回应
out.write("你好,服务器!\n");
out.flush();
String response = in.readLine(); // 读取服务器回复
System.out.println("服务器说: " + response);
Java UDP通信
Java 使用 DatagramSocket 类和 DatagramPacket 类实现 UDP 通信。
发送数据的步骤包括 : 创建 DatagramSocket 和 DatagramPacket , 调用 send()
方法发送 , 最后关闭 Socket .
DatagramSocket ds = new DatagramSocket(); // 不指定端口,由系统随机分配
String str = "Hello UDP";
byte[] data = str.getBytes();
InetAddress addr = InetAddress.getByName("localhost");
int port = 12345;
DatagramPacket dp = new DatagramPacket(data, data.length, addr, port);
ds.send(dp); // 发送数据包
ds.close();
接收数据的步骤包括 : 创建带监听端口的 DatagramSocket , 创建空的 DatagramPacket 作为接收容器 , 调用 receive()
方法接收数据包 , 从 DatagramPacket 中提取数据并解析 .
DatagramSocket ds = new DatagramSocket(12345); // 监听12345端口
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp); // 接收数据包(阻塞等待)
String received = new String(dp.getData(), 0, dp.getLength(), "UTF-8");
System.out.println("收到UDP消息: " + received);
ds.close();
该示例中,发送端通过DatagramPacket
指定目标IP和端口发送消息;接收端通过绑定端口的DatagramSocket
接收并解析数据包。
C/S 与 P2P
局域网聊天室中常用的两种架构是 **客户端-服务器 C/S 模式 **和 点对点 P2P 模式 .
C/S : 所有客户端都连接到一台中央服务器 , 客户端之间不直接通信 , 而是通过服务器转发信息 . 服务器负责维护在线用户列表 , 接收并广播消息给各客户端 , 实现群聊功能 .
该架构优点是实现简单 , 便于集中管理和广播消息 ; 缺点是服务器承载较大负载 , 存在单点故障风险 .
P2P : 每个节点既可作为客户端也可作为服务器 , 节点之间直接连接通信 . 常见的做法是让所有客户端先向中心服务器登记地址 , 获取其他在线节点列表 .请求通信时 , 客户端直接连接目标节点进行点对点聊天 . 登录后 , 客户端还会启动一个监听线程 , 接收其他节点的连接请求 .
P2P架构的优点是没有单点服务器 , 节点可以更灵活扩展 : 缺点是实现较为复杂 , 节点需要维护对等连接 , 有时需要处理网络地址转换等问题 .
特性 | C/S架构 | P2P架构 |
---|---|---|
连接方式 | 客户端主动连接服务器 | 每个节点同时监听和主动连接其他节点 |
消息转发 | 所有消息先汇集到服务器 , 由服务器转发给目标客户端或广播 | 客户端之间直接通信 , 发送方直接将消息发给目标节点 |
连接维护 | 服务器维护所有客户端的连接列表 , 统一管理上下线 | 每个客户端维护自身与其他节点的连接或地址列表 , 节点自治 |
上述区别在代码实现上也有所体现 :
-
C/S 服务器端使用 ServerSocket 接受客户端连接并保存客户端 Socket 列表 , 客户端统一与服务器的 Socket 读写消息 ; P2P 每个客户端除了作为客户端启动连接 , 也要打开 ServerSocket 监听端口以接收其它客户端的连接请求 , 节点既是客户端也是服务器 .
-
消息转发逻辑方面 , C/S 由服务器集中转发 , 而 P2P 是点对点直接发送 .
-
连接维护方面 , C/S 由服务器负责跟踪所有连接 . P2P 则需要各节点自行维护已连接对等方的信息 .