Java网络编程,使用UDP实现TCP(二), 实现数据传输过程
2023-12-13 11:34:49
简介:
经过了三次握手过程,我们的服务端和客户端已经建立了连接。我们接下来需要做的就是数据的传输。
主要步骤:
-
数据发送:客户端或服务器将数据打包成一个或多个数据段,每个数据段都有一个序列号(SEQ)和确认号(ACK)。
-
数据接收:接收方接收到数据段后,将根据数据段的序列号(SEQ)进行排序,以确保数据的有序性。
-
确认接收:接收方会为每个接收到的数据段发送一个确认(ACK)。这个确认包含了确认号(ACK),该确认号是接收方期望接收的下一个数据段的序列号,也就是最后一个接收到的数据段的序列号加1。
-
丢包重传:如果发送方在一定时间内没有接收到某个数据段的确认,它会认为该数据段已经丢失,并进行重传。
-
流量控制:接收方可以使用窗口大小字段来控制发送方的发送速度,以防止自己被大量的数据淹没。
-
拥塞控制:如果网络发生拥塞,TCP会降低数据的发送速度,以减少网络的拥塞程度。(未实现)
所以在数据传输段过程中,每一段都需要Seq(序列号),ACK(确认号)
实现过程:
- 确认号(ACK)是基于之前接收到的最后一个数据包的序列号+1。也就是说,ACK的值是接收方期望接收的下一个数据包的序列号。这是因为,在TCP协议中,ACK的值总是等于接收方已经成功接收的最后一个数据包的序列号+1。
- 例如,假设在三次握手的过程中,客户端发送给服务器的最后一个ACK是101(这是第三次握手的ACK),那么在数据传输开始时,如果服务器是第一个发送数据的话,服务器发送的第一个数据包的序列号应该是101,而客户端回复的ACK就应该是102,表示客户端已经接收到了序列号为101的数据包,期望接收的下一个数据包的序列号是102。
服务端发送数据到客户端
- 注意由于数据的传输是双向的,所以在发送后可以接收客户端发来的数据。
- 在接收数据后,我们在服务端需要进行超时判断,如若超时会进行超时重传。超时判断我们使用了 setSoTimeout() 方法。
- 关于超时的时间,根据《TCP/IP详解》卷二 的计时器篇中可知,超时时间严苛来说,需要考虑发送端到接收端接收数据时间,接收端数据处理时间,接收端发送确认消息到客服端时间。超时时间的选择应该是多次数据传输花费时间的均值。
- 在《TCP/IP详解》卷二 的 tcp 篇详细中规定了数据重传不能超过三次,这里我使用了retryCount这个计数器来计数。
//数据传输开始
System.out.println("====================");
System.out.println("数据发送...");
String SeqD1 = String.valueOf(connectionMarks.getSeq());
String ACKD1 = String.valueOf(Integer.parseInt(strArr3[1]) + 1);
String dataMsg = SeqD1 + " " + "我是马尔咖里斯,我是不朽的!" + " " + ACKD1;
byte[] datasD1 = dataMsg.getBytes();
DatagramPacket datagramPacketD1 = new DatagramPacket(datasD1, 0,datasD1.length, new InetSocketAddress("localhost",8888));
int maxRetries = 3; // 最大重试次数
int retryCount = 0; // 当前重试次数
boolean success = false; // 是否成功标志
while (!success && retryCount < maxRetries) {
try {
// 设置超时时间
datagramSocket.setSoTimeout(50000);
// 发送数据
datagramSocket.send(datagramPacket);
// 接收响应
byte[] buffer = new byte[1024];
DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
datagramSocket.receive(responsePacket);
// 处理响应
String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
System.out.println("接收到响应:" + response);
// 设置成功标志
success = true;
} catch (java.net.SocketTimeoutException e) {
// 超时后重新发送数据
retryCount++;
System.out.println("超时,进行第 " + retryCount + " 次重试");
} catch (IOException e) {
// 处理其他异常
e.printStackTrace();
}
}
if (!success) {
System.out.println("重试次数超过最大限制,操作失败");
}
客户端发送数据到服务端
注意:在tcp三次握手后数据段的传输也需要添加ACK,和Seq,如前文所述。这里为了便于实现在传回数据时只写了数据返回 “收到消息”?是不符合规定的,需要修改。
System.out.println("====================");
System.out.println("开始接收数据段...");
byte[] bytes1 = new byte[1024];
DatagramPacket datagramPacketD1 = new DatagramPacket(bytes1, bytes1.length);
datagramSocket.receive(datagramPacketD1);
String receiveMsg = new String(datagramPacketD1.getData(), 0, datagramPacketD1.getLength());
System.out.println("接收到的数据段为:" + receiveMsg);
String[] split = receiveMsg.split(" ");
String SeqD1 = split[0];
System.out.println("====================");
System.out.println("数据消息确认返回");
String replyMsg = "收到消息";
clientMsg.sendMsg(replyMsg, datagramSocket);
文章来源:https://blog.csdn.net/weixin_74783792/article/details/134957037
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!