# 实现 TCP 协议端到端的可靠传输
## 实验须知
- 实验环境
Java 1.8.0_151
- 运行
将根目录导入 eclipse 运行 TestRun.java 即可
- RDT3.0 到选择响应所有文件变动较大,所以如果要运行 RDT3.0,需要替换一下文件,文件放在了 RDT3.0 的文件夹中
- 选择响应传输和 GBN 传输的只要在的 TCP_Sender.java,TCP_receiver.java 中 rdt_send 调用对应 send 函数和 reply 函数即可
## 实验原理
TCP 进行可靠传输以保证数据包不会丢失、失序、重复并高效:
- 滑动窗口
- 超时重传
- 选择确认
![](https://www.writebug.com/myres/static/uploads/2021/12/23/f96d00735b3ed5f2c4189e5b6b8dc290.writebug)
![](https://www.writebug.com/myres/static/uploads/2021/12/23/7e5059f43bf95b80950e3f5427fbb2a0.writebug)
## 实验内容
1. RDT2.0
- 假设:底层信道传输过程中,个别数据包的某些字节可能发生位错
- 方法:使用校验和算法检查数据包的正确性(发送前计算/接收后校验)
- 具体实现:
- 校验和原理
将发送的进行检验和运算的数据分成若干个 16 位的位串,每个位串看成一个二进制数,这里并不管字符串代表什么,是整数、浮点数还是位图都无所谓。
并且将二进制数取反后相加,如果最高位发生进位采取**循环进位**
同时**取反后相加结果等于相加后再取反**
- 根据原理可以得出如下代码
已知校验和 16 位,Java 中没有 unsigned short,这里取 int 的低 16 位,不取 short 是因为 short 数据范围在-0x1000~0x7FFF,不好判断校验和是否大于 0xFFFF,取 int 型较好判断校验和是否发生进位。并且在每次加上一个数字都需要判断是否大于 0xFFFF,否则会出错。
```java
/*计算TCP报文段校验和:只需校验TCP首部中的seq、ack和sum,以及TCP数据字段*/
public static short computeChkSum(TCP_PACKET tcpPack) {
int checkSum = 0;
TCP_HEADER header = tcpPack.getTcpH();
int[] data = tcpPack.getTcpS().getData();
int len = data.length,
flag = 0xffff,
flagmod = 65536;
checkSum = header.getTh_ack(); //防止seq字段超过0xffff
if(checkSum>flag)
checkSum = checkSum%flagmod+checkSum/flagmod;
checkSum +=header.getTh_seq(); //防止seq字段超过0xffff
if(checkSum>flag)
checkSum = checkSum%flagmod+checkSum/flagmod;
for(int i = 0;i < len;i++) {
checkSum += data[i];
if(checkSum>flag) //防止seq字段超过0xffff
checkSum = checkSum%flagmod+checkSum/flagmod;
}
checkSum += header.getTh_sum(); //防止seq字段超过0xffff
if(checkSum>flag)
checkSum = checkSum%flagmod+checkSum/flagmod;
checkSum = ~checkSum;
return (short) checkSum;
}
```
- 也可以使用 Java 封装好的 RCR
```java
public static short computeChkSum(TCP_PACKET tcpPack) {
int checkSum = 0;
//计算校验和
TCP_HEADER tcpH = tcpPack.getTcpH();
CRC32 crc = new CRC32();
crc.update(tcpH.getTh_ack());
crc.update(tcpH.getTh_seq());
for(int data : tcpPack.getTcpS().getData()) {
crc.update(data);
}
checkSum = (int) crc.getValue();
return (short) checkSum;
}
```
- 并且在接收方返回 ACK 包前加上判断
```java
//重新计算校验和,如果没有发生错误校验和应为0
if(CheckSum.computeChkSum(recvPack) == 0 ) {
//生成返回报文
ackPack = new TCP_PACKET(tcpH, tcpS, recvPack.getSourceAddr());
tcpH.setTh_sum((short) 0);
tcpH.setTh_sum(CheckSum.computeChkSum(ackPack));
//回复ACK报文段
reply(ackPack);
//有重复数据的情况下需要检查数据顺序号(确定是否接收了重复的数据)
//去除报文中的顺序号
//判断是否是重复数据:非重复数据,将数据插入data队列
int[] data = recvPack.getTcpS().getData();
dataQueue.add(data);
//更新期待接收的顺序号
sequence=sequence+data.length;
}
```
- 运行结果
![](https://www.writebug.com/myres/static/uploads/2021/12/23/85ac249749baac9c2094cdd89cf202d1.writebug)
程序运行此处不再发送包,也不再接受包。
查看日志可以看到
![](https://www.writebug.com/myres/static/uploads/2021/12/23/d36606ef9a26e3c75b3caff644747d92.writebug)
编号 5701 为 5701 包出错,客户端检查出错。
- RDT2.2
对于返回的包添加 ack 标识,对于出错的包进行重传
即在发送方和接收方都设置一个字段来设置当前传输的包,若当前的传输的包出错则不响应,发送方设置计时器,在一定时间内没有收到接收方设置的 ACK 包则进行重发,同时如果接收方发送的 ACK 包也出错的话视为接收方未响应并重发
```java
//发送端
public void rdt_send(int dataIndex, int[] appData) {
//生成TCP数据报(设置序号和数据字段/校验和),注意打包的顺序
tcpH.setTh_seq(dataIndex * appData.length + 1);//包序号设置为字节流号:你也可以使用其他编号方式,注意修改对应的接收方判断序号的部分
tcpH.setTh_sum((short)0);//先将校验码设为0,用于后续的计算
tcpS.setData(appData);
tcpPack = new TCP_PACKET(tcpH, tcpS, destinAddr);
//更新校验码;需要重新将tcpH填入到tcpPack
tcpH.setTh_sum(CheckSum.computeChkSum(tcpPack));
tcpPack.setTcpH(tcpH);
sequence = tcpPack.getTcpH().getTh_seq();
//发送TCP数据报
udt_send(tcpPack);
/**************************/
/**定时器的用法:定时器到时后完成重传,需要用UDT_Timer用于计时;计时到0后,触发UDT_RetransTask完成重传**/
timer = new UDT_Timer();
/**重传器UDT_RetransTask将发送端和发送内容作为成员变量**/
UDT_RetransTask reTrans = new UDT_RetransTask(client, tcpPack);
/**UDT_Timer开始计时第一次重传为5s,以后每间隔3s完成一次重传;如果发现对方接收成功,需要在waitACK()中关闭计时器**/
timer.schedule(reTrans, 5000, 300);
//在waitACK使用无线循环和Break,来实现停止等待;当涉及Go-Back-N 或 Selective-Response的话,就不可以用停止等待了
waitACK();
}
```
接收端
此时需要注意,在接收端无论收到的包都需要的回复,因为有可能是上一个已经确认过的包回复 ACK 包出错,但是此时不需要交互
```java
public void rdt_recv(TCP_PACKET recvPack) {
//检查校验码,生成ACK
int seq = recvPack.getTcpH().getTh_seq();
if(CheckSum.computeChkSum(recvPack) == 0 ) {
//生成ACK报文段(设置确认号)
tcpH.setTh_ack(recvPack.getTcpH().getTh_seq());
ackPack = new TCP_PACKET(tcpH, tcpS, recvPack.getSourceAddr());
tcpH.setTh_sum((short) 0);
tcpH.setTh_sum(CheckSum.computeChkSum(ackPack));
//回复ACK报文段
reply(ackPack);
//有重复数据的情况下需要检查数据顺序号(确定是否接收了重复的数据)
if(sequence == seq) {
//判断是否是重复数据:非重复数据,将数据插入data队列
int[] data = recvPack.getTcpS().getData();
dataQueue.add(data);
//更新期待接收的顺序号
sequence=sequence+data.length;
}
}
if(dataQueue.size() >= 20)
deliver_data();
}
```
运行结果
出错的包超时重传
![](https://www.writebug.com/myres/static/uploads/2021/12/23/918f60161c5a3396895e57a3a6a185
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
将发送的进行检验和运算的数据分成若干个 16 位的位串,每个位串看成一个二进制数,这里并不管字符串代表什么,是整数、浮点数还是位图都无所谓。并且将二进制数取反后相加,如果最高位发生进位采取循环进位,同时取反后相加结果等于相加后再取反。
资源推荐
资源详情
资源评论
收起资源包目录
100012105-基于Java实现 TCP 协议端到端的可靠传输.zip (75个子文件)
tcpcs
Config.ini 144B
recvData.txt 825KB
lib
TCP_Win_TestSys.jar 30KB
META-INF
MANIFEST.MF 63B
com
ouc
tcp
message
TCP_HEADER.class 5KB
TCP_PACKET.class 3KB
TCP_SEGMENT.class 2KB
MSG_STREAM.class 1KB
tool
TCP_TOOL.class 3KB
app
SystemStart.class 913B
App_Sender.class 4KB
RunServer.class 428B
client
UDT_RetransTask.class 732B
TCP_Sender_ADT.class 2KB
TCP_Receiver_ADT.class 2KB
Client.class 2KB
ListenACK.class 3KB
UDT_Timer.class 293B
ListenPacket.class 3KB
server
Server.class 6KB
Server$1.class 669B
ForwardDelay.class 918B
WriteLogFile.class 3KB
TransLog.class 4KB
config
Constant.class 870B
SYS_INI.class 1KB
.classpath 366B
ackLog.txt 1.16MB
.settings
org.eclipse.jdt.core.prefs 587B
nowLog.txt 37KB
src
com
ouc
tcp
test
TCP_Receiver.java 2KB
SendWindow.java 5KB
ReceiveWindow.java 2KB
TestRun.java 192B
CheckSum.java 952B
Window.java 476B
TCP_Sender.java 2KB
LICENSE 1KB
bin
.gitignore 6B
.project 370B
images
1543475127852.png 169KB
1543483522574.png 10KB
1543484026183.png 8KB
1543492404019.png 40KB
1543492797552.png 49KB
1543658335759.png 71KB
1543492694460.png 39KB
1543658305389.png 63KB
1543492685358.png 39KB
1543483965620.png 10KB
1543492598614.png 12KB
1543495285994.png 30KB
1543481219964.png 34KB
1543481256508.png 33KB
1543479194075.png 151KB
1543493994705.png 35KB
1543483489261.png 9KB
1543492574543.png 24KB
1543481796291.png 28KB
1543492651420.png 25KB
1543494085825.png 9KB
1543478990033.png 180KB
1543483362057.png 8KB
1543658918055.png 12KB
1543651734072.png 53KB
1543492847348.png 48KB
1543481775825.png 13KB
ENCDA.tcp 1.24MB
README.md 32KB
RDT3.0
TCP_Receiver.java 2KB
TestRun.java 195B
CheckSum.java 998B
Window.java 566B
TCP_Sender.java 3KB
Log.txt 104KB
共 75 条
- 1
资源评论
神仙别闹
- 粉丝: 2674
- 资源: 7640
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功