1. 传输层协议
- 传输层中有两个协议: TCP 和 UDP
1.1. TCP
1.1.1. TCP 协议的概述
- 传输控制协议 TCP(Transmission Control Protocol) 协议,
是一种面向连接的, 可靠的, 基于字节流的传输层通信协议.- 面向连接, 传输可靠(保证数据正确性, 保证数据顺序)
- 用于传输大量数据(流模式)
- 速度慢, 建立连接需要开销较多(时间, 系统资源)
1.1.2. TCP 的传输特点
通过面向连接, 端到端和可靠的数据包发送.
- TCP 为了保证报文传输的可靠, 就给每个包一个序号,
同时序号也保证了传送到接收端实体的包的按序接收.- 若接收端实体对已成功收到的字节发回一个相应的确认(ACK);
- 若发送端实体在合理的往返时延(RTT)内未收到确认, 对应的数据将会被重传.
- TCP 为了保证报文传输的可靠, 就给每个包一个序号,
分为服务端和客户端
1.1.3. TCP 建立连接的过程
TCP 是因特网中的传输层协议, 使用三次握手协议建立连接.
当主动方发出 SYN 连接请求后, 等待对方回答 SYN + ACK ,
并最终对对方的 SYN 执行 ACK 确认.这种建立连接的方法可以防止产生错误的连接,
TCP 使用的流量控制协议是可变大小的滑动窗口协议.TCP 建立连接的过程如下:
客户端发送
SYN(SEQ=x)
报文给服务器端, 进入SYN_SEND
状态.服务器端收到
SYN
报文, 回应一个SYN(SEQ=y)ACK(ACK=x+1)
报文,
进入SYN_RECV
状态.客户端收到服务器端的
SYN
报文, 回应一个ACK(ACK=y+1)
报文,
进入Established
状态.三次握手完成, TCP 客户端和服务器端成功地建立连接, 可以开始传输数据了.
TCP 断开连接的过程如下:
建立一个连接需要三次握手, 而终止一个连接要经过四次握手,
这是由 TCP 的半关闭(half-close
)造成的.某个应用进程首先调用 close, 称该端执行"主动关闭"(
active close
) 该端的 TCP 于是发送一个 FIN 分节, 表示数据发送完毕.接收到这个 FIN 的对端执行"被动关闭"(
passive close
),
这个 FIN 由 TCP 确认.注意:
FIN 的接收也作为一个文件结束符(end-of-file
)传递给接收端应用进程,
放在已排队等候该应用进程接收的任何其他数据之后, 因为, FIN 的接收意味着
接收端应用进程在相应连接上再无额外数据可接收.一段时间后, 接收到这个文件结束符的应用进程将调用 close 关闭它的套接字.
这导致它的 TCP 也发送一个 FIN.接收这个最终 FIN 的原发送端 TCP(即执行主动关闭的那一端)确认这个 FIN.
整个过程每个方向都需要一个 FIN 和一个 ACK, 因此通常需要 4 个分节.
1.2. UDP
用户数据报协议 UDP(
User Datagram protocol
) 一种无连接的传输层协议,
提供面向事务的简单不可靠信息传送服务.面向非连接, 传输不可靠(可能丢包/数据丢失),
用于传输少量数据(数据包模式), 速度快.分为发送端和接收端
2. TCP 操作实例
- 涉及到两个类, 分别对应服务端和客户端
- 因此需要分别写出服务端和客户端的程序
- 这两个类为:
java.net.Socket
此类实现客户端套接字java.net.ServerSocket
此类实现服务器套接字
2.1. TCP 程序实例
2.1.1. 创建服务端程序
public class Server{
public static void main(String[] args) throws Exception{
//创建服务端对象, 指定端口为8888
ServerSocket server = new ServerSocket(8888);
//接收连接服务端的客户端对象
Socket client = server.accept();
//定义服务端返回数据, 并传递给客户端
Sting alert = "Connect Success";
//获取客户端对象的输出流对象, 表明将服务端数据传送给客户端
OutputStream os = client.getOutputStream();
//用打印流包装客户端输出流, 让数据直接输出到客户端程序中
PrintStream ps = new PrintStream(os);
ps.println(alert);
//关闭打印流和服务器
ps.close();
server.close();
}
}
2.1.2. 创建客户端程序
public class Server{
public static void main(String[] args) throws Exception{
//创建客户端对象, 指定连接服务器IP为127.0.0.1, 指定端口为8888
Socket client = new Socket("127.0.0.1", 8888);
//客户端获取从服务器中输入的数据, 调用客户端对象的输入流即可获取
//同时用扫描器类进行封装
Scanner sc = new Scanner(client.getInputStream);
//在客户端的控制台进行输出
while(sc.hasNextLine()){
String line = sc.nextLine();
System.out.println(line);
}
//关闭输入流和客户端对象
sc.close();
client.close();
}
}
2.1.3. 备注
- 若服务端程序要一直接受客户端对象, 则需要用死循环去执行程序.
3. UDP 操作实例
涉及两个类, 分别用来接受/发送数据和封装数据成数据包
java.net.DatagramSocket
此类表示用来发送和接收数据报包的套接字.java.net.DatagramPacket
此类表示数据报包
程序也要分开为发送端和接收端来进行编写.
3.1. UDP 程序实例
3.1.1. 发送端
public class Sender{
public static void main(String[] args) throws Exception{
//创建发送端对象, 指定发送端口为10010
DatagramSocket sender = new DatagramSocket(10010);
//定义发送的数据
String data = "this is the data";
//封装要发送的数据
DatagramPacket dp = new DatagramPacket(
data.getBytes(), //发送的数据转换成byte数组
data.getBytes().length, //发送的数据的字节长度
"127.0.0.1", //发送的目标地址的IP
10086 //发送的目标地址所开放的端口号
);
//发送数据
sender.send(dp);
//关闭发送对象
sender.close();
}
}
3.1.2. 接收端
public class Receiver{
public static void main(String[] args) throws Exception{
//创建接收端对象, 指定接收端口为10086
DatagramSocket receiver = new DatagramSocket(10086);
//先定义一个缓冲区用以接受数据
byte[] b = new byte[1024];
//接收端对象利用缓冲区来接受数据, 并存入一个数据包中
DatagramPacket dp = new DatagramPacket(b, 1024);
receiver.receive(dp);
//获取接受的数据并转化成字符串
String msg = new String(dp.getData(), 0, dp.getLength());
//在接收端后台输出接受的数据
System.out.println(msg);
//关闭接收端对象
receiver.close();
}
}