本节我们在上一节基础上进一步完成TCP协议的收发机制。上一节我们已经实现了向服务器方发送一个字符,本节我们要实现连续发送多个字符,并且能正常接收数据功能,完成了这些功能后,我们就可以基于此去开发其他构建在TCP之上的其他协议。
为了保证数据能正确的连续收发,本节的设计思路是使用一个队列将发送的数据存储起来,然后将数据包发送,只有等待收到对方回发的ack后,我们才将数据从队列中删除,如果数据包一直没有收到ack回应,我们就启动一个timer,自动将队列中的数据包进行发送,如果发送给定次数后还没有成功,那么就通知数据发送层发送失败,接下来我们看看相应代码设计。
class SendPacketWrapper {
//将发送的数据包封装起来存储在队列中
private byte[] packet_to_send;
private int seq_num;
private int ack_num;
private int send_count = 0;
public SendPacketWrapper(byte[] packet, int seq_num) {
this.packet_to_send = packet;
this.seq_num = seq_num;
this.ack_num = seq_num + packet.length;
}
public byte[] get_packet() {
return this.packet_to_send;
}
public int get_seq_num() {
return this.seq_num;
}
public int get_ack_num() {
return this.ack_num;
}
public void increase_send_count() {
this.send_count++;
}
public int get_send_count() {
return this.send_count;
}
}
class SendPacketTask extends TimerTask {
private TCPThreeHandShakes task_handler;
public SendPacketTask(TCPThreeHandShakes handler) {
this.task_handler = handler;
}
@Override
public void run() {
this.task_handler.sendPacketInList();
}
}
第一个类用于负责把发送的数据封装起来,他记录了数据的缓冲区,以及发送时对应的seq号,这样当数据包需要重发时就可以再次使用这个数值进行发送,同时也记录了应对的ack号,这样当对方返回ack值时,我们才能检验该数据包是否已经被对方接收。
接下来我们在类TCPThreeHandShakes中添加相应变量和代码:
public class TCPThreeHandShakes extends Application{
。。。。
private int tcp_state = CONNECTION_IDLE;
private static int PACKET_SEND_TIMES = 3; //连续发生3次不成功则失败
private Timer send_timer = new Timer(); //定时将发送队列中的数据包进行发送
private int packet_resend_time = 2000; //每过一秒就发送队列中存储的数据包
private SendPacketTask resend_packet_task = null;
//每次发送数据包时先将它存储在队列中,发送出去收到ack后再将它从队列中去除
private ArrayList<SendPacketWrapper> send_packet_