跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

大话计算机网络(上)

一、TCP和UDP的区别: 1. 连接方式 TCP(Transmission Control Protocol,传输控制协议) 面向连接,传输前必须先建立连接(三次握手),通信结束后要断开连接(四次挥手)。

UDP(User Datagram Protocol,用户数据报协议) 无连接,直接发数据,不需要建立或断开连接。

2. 可靠性 TCP 提供可靠传输:有确认机制、重传机制、流量控制、拥塞控制。

能保证数据 不丢失、不重复、按顺序到达 。

UDP 不保证可靠性:没有确认和重传机制。

可能丢包、乱序。

3. 传输效率 TCP 开销大(需要建立连接、维护状态、做校验、重传等)。

传输效率相对较低。

UDP 头部开销小(8 字节,对比 TCP 的 20 字节起步)。

传输效率高,延迟低。

4. 应用场景 TCP 适合可靠传输场景 如:HTTP/HTTPS、FTP、SMTP、数据库连接。

(需要保证数据完整性和顺序性) UDP 适合实时场景 如:视频直播、语音通话、在线游戏、DNS 查询。

(容忍少量丢包,但需要低延迟) 一句话 : TCP = 慢但可靠(适合“文件传输”类) UDP = 快但不可靠(适合“实时传输”类) 实例代码: TCP: TCP服务端:

import java.io.*;

import java.net.*;

public class TcpServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(6666); // 监听端口 System.out.println("TCP 服务端启动,等待连接...");

Socket socket = serverSocket.accept(); // 阻塞等待客户端连接 System.out.println("客户端已连接: " + socket.getInetAddress());

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

String msg = in.readLine(); System.out.println("收到客户端消息: " + msg);

out.println("服务端回复: 已收到 -> " + msg);

socket.close(); serverSocket.close(); } }

TCP客户端:

import java.io.*;

import java.net.*;

public class TcpClient { public static void main(String[] args) throws IOException { Socket socket = new Socket("localhost", 6666); // 连接服务端

PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

out.println("Hello TCP!"); // 发送消息 String reply = in.readLine(); // 接收回复 System.out.println("收到服务端回复: " + reply);

socket.close(); } }

运行效果: 1、先启动TcpServer,它会阻塞等待连接 2、再运行TcpServer,服务端会打印受到的消息,并回复客户端 UDP示例 UDP服务端:

import java.net.*;

public class UdpServer { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8888); // 监听端口 byte[] buf = new byte[1024];

System.out.println("UDP 服务端启动,等待数据...");

DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive(packet); // 阻塞等待数据

String msg = new String(packet.getData(), 0, packet.getLength()); System.out.println("收到客户端消息: " + msg);

// 回复消息 String reply = "服务端收到 -> " + msg; byte[] data = reply.getBytes(); DatagramPacket replyPacket = new DatagramPacket( data, data.length, packet.getAddress(), packet.getPort()); socket.send(replyPacket);

socket.close(); } }

UDP客户端:

import java.net.*;

public class UdpClient { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket();

String msg = "Hello UDP!"; byte[] data = msg.getBytes();

InetAddress address = InetAddress.getByName("localhost"); DatagramPacket packet = new DatagramPacket(data, data.length, address, 8888);

socket.send(packet); // 发送消息

// 接收服务端回复 byte[] buf = new byte[1024]; DatagramPacket replyPacket = new DatagramPacket(buf, buf.length); socket.receive(replyPacket);

String reply = new String(replyPacket.getData(), 0, replyPacket.getLength()); System.out.println("收到服务端回复: " + reply);

socket.close(); } }

二、TCP三次握手

1. 为什么需要三次握手?

TCP 是 面向连接的、可靠的 协议,建立连接前要确保 双方都具备收发能力 。

一次握手:只能确认 客户端 → 服务端 的发送没问题。

两次握手:只能确认 双向传输通路 ,但不能防止 旧连接请求 造成混乱。

三次握手:既能确认收发能力,也能避免历史报文影响,最终保证连接可靠。

2. 三次握手过程 假设:客户端 (Client),服务端 (Server)。

第一次握手 Client → Server Client 发送

SYN = 1

,选择一个初始序列号

Seq = x

。

表示: “我要建立连接,这是我的起始序号。

” 第二次握手 Server → Client Server 收到请求后,回复

SYN = 1, ACK = 1

,确认号

Ack = x + 1

,并生成自己的初始序列号

Seq = y

。

表示: “我收到了你的请求,也要建立连接,这是我的起始序号。

” 第三次握手 Client → Server Client 收到回复后,再发

ACK = 1

,确认号

Ack = y + 1

,序号

Seq = x + 1

。

表示: “我确认收到你的序号,连接可以正式建立。

” 此时, 双方连接建立成功 ,进入 ESTABLISHED 状态,可以开始传输数据。

三次握手保证了 双方序列号的同步 。

避免了 历史连接请求报文 导致的错误连接。

握手完成后,通信双方进入 全双工通信 (可以同时收发)。

一句话总结 : TCP 三次握手是 确保客户端和服务端都有收发能力 、并且 序列号同步 的过程,是可靠传输的前提。

三、TCP四次挥手 TCP 四次挥手是 TCP 协议中断开连接时的过程。

它比“三次握手”要多一次,是因为 TCP 需要确保双方的数据都已经传输完成。

1. 四次挥手步骤 第一步:主动关闭方 → 发送 FIN 主动关闭连接的一方(通常是客户端)发送 FIN 报文段 ,表示自己已经没有数据要发送了。

进入 FIN_WAIT_1 状态。

第二步:被动关闭方 → 回复 ACK 被动关闭的一方(通常是服务器)收到 FIN 后,发送 ACK 确认报文段 。

此时,主动方知道对方已经收到了“我要关闭”的请求。

主动方进入 FIN_WAIT_2 状态。

被动方进入 CLOSE_WAIT 状态(说明它还可能有数据没发完)。

第三步:被动关闭方 → 发送 FIN 等被动关闭方的数据发完,它会主动发送 FIN 报文段 ,告诉对方“我也没有数据要发了”。

被动方进入 LAST_ACK 状态。

第四步:主动关闭方 → 回复 ACK 主动关闭方收到 FIN 后,回复一个 ACK 报文段 ,确认对方的关闭请求。

主动方进入 TIME_WAIT 状态。

最后主动方进入 CLOSED 状态,连接彻底断开。

2. 为什么需要四次?

因为 TCP 全双工 ,双方必须分别关闭发送通道。

如果只有“三次”,一方可能还没发完数据就被迫关闭了。

所以要多一步,保证双方数据传输彻底完成。

3. 代码示例 Client主动关闭端

import java.io.InputStream;

import java.io.OutputStream; import java.net.Socket;

public class TcpClient { public static void main(String[] args) throws Exception { Socket socket = new Socket("127.0.0.1", 8888);

OutputStream out = socket.getOutputStream(); InputStream in = socket.getInputStream();

// 发送数据 out.write("你好,服务器".getBytes());

// 接收服务器响应 byte[] buffer = new byte[1024]; int len = in.read(buffer); System.out.println("收到服务端数据: " + new String(buffer, 0, len));

// 等一会儿再关闭,模拟 TIME_WAIT 观察 Thread.sleep(2000); System.out.println("客户端准备关闭 (FIN)"); socket.close(); } }

Server端:

import java.io.InputStream;

import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;

public class TcpServer { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务器启动,等待连接...");

Socket socket = serverSocket.accept(); System.out.println("客户端连接来自: " + socket.getInetAddress());

InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream();

byte[] buffer = new byte[1024]; int len = in.read(buffer); System.out.println("收到客户端数据: " + new String(buffer, 0, len));

// 回复客户端 out.write("服务端回复: 收到数据".getBytes());

System.out.println("等待客户端关闭..."); // 如果客户端先关闭,read 会返回 -1 while ((len = in.read(buffer)) != -1) { System.out.println("额外数据: " + new String(buffer, 0, len)); }

System.out.println("检测到客户端 FIN,服务器准备关闭"); socket.close(); serverSocket.close(); } }

相关文章