static int
tcp_ack_check (void)
{
TcpT_Seg_Fields* fd_ptr;
char str0 [128];
TcpT_Seq old_snd_una;
TcpT_Size acked_bytes;
double current_time;
double next_timeout_time;
FIN (tcp_ack_check ());
current_time = op_sim_time ();
if (!(ev_ptr->flags & TCPC_FLAG_ACK))
{
if (tcp_trace_active)
op_prg_odb_print_major ("TCP received non-ACK segment; aborting connection.", OPC_NIL);
FRET (0);
}
op_pk_nfd_access (ev_ptr->pk_ptr, "fields", &fd_ptr);
if (fd_ptr == OPC_NIL) //如果fd_ptr是空指针,表示没有获得头部信息
tcp_conn_error ("Unable to get the header fields from the received packet.", OPC_NIL, OPC_NIL);
//缓存当前分组的ack序号
seg_ack = fd_ptr->ack_num; //seg_ack表示ack序列号
if (tcb_ptr->tcp_conn_stat_ptr != OPC_NIL &&
op_stat_valid (tcb_ptr->tcp_conn_stat_ptr->rcv_seg_ack_no_stathandle) == OPC_TRUE)
{
op_stat_write (tcb_ptr->tcp_conn_stat_ptr->rcv_seg_ack_no_stathandle, seg_ack);
}
//存储当前snd_una的值
old_snd_una = snd_una; //将当前snd_una的值存进old_snd_una中
//处理ECN相关的信息
tcp_ecn_processing (); //ECN处理函数
//在它接收之前丢失了一个数据包。所以新数据达到,但累积ACK不能提前
if (tcp_seq_lt (seg_ack, snd_una)) //如果ack序列号小于第一个未确认序列号
{
//只要没有进入快速重传,ack重置计数器就一直计数
if (dup_ack_cnt < tcp_parameter_ptr->fr_dup_ack_thresh) //如果重复ack的数量小于快速重传重复ack的阈值,那么重复ack计数器清零
dup_ack_cnt = 0;
if (tcp_trace_active || tcp_extns_trace_active)
op_prg_odb_print_major ("TCP received an old duplicate ACK; ignoring.", OPC_NIL);
//检查输入分组是否包含内容
if (seg_len > 0)
{
//尽管分组是无序的,接收数据。然而,不处理其他内容
FRET (1); //如果有内容,返回1
}
else
{
FRET (0);
}
}
//检查是否这个分组复制了最近接收的ack
else if (seg_ack == snd_una) //如果接收到的ack序列号=第一个未确认分组的序列号
{
if ((seg_len != 0) && (conn_supports_ts == TCPC_OPTION_STATUS_ENABLED)) //并且连接支持时间戳
{
tcp_ts_info_process (ev_ptr->pk_ptr); //处理时间戳信息函数
}
//重复ack是否包含任何新的数据活着窗口更新?
if ((seg_len != 0) || (fd_ptr->rcv_win << snd_scale != snd_wnd))
{
if ((tcp_trace_active || tcp_extns_trace_active) && dup_ack_cnt != 0)
{
op_prg_odb_print_major ("TCP received a duplicate ACK containing new data or a window update.", OPC_NIL);
}
//处理sack数据
tcp_sack_processing (ev_ptr->pk_ptr);
if (dup_ack_cnt < tcp_parameter_ptr->fr_dup_ack_thresh) //重复ack计数小于阈值,说明还未进入快速重传
{
dup_ack_cnt = 0;
}
}
else
{
if (tcp_seq_gt (snd_max, snd_una)) //如果发送分组的最大序列号大于第一个未确认序列号,说明分组已经丢失
{
dup_ack_cnt++;
if (tcp_trace_active || tcp_extns_trace_active)
{
sprintf (str0, "TCP received consecutive duplicate ACK number %d.", dup_ack_cnt);
op_prg_odb_print_major (str0, OPC_NIL);
}
tcp_sack_processing (ev_ptr->pk_ptr);
//进入快速重传
tcp_frfr_processing ();
FRET (1);
}
else
{
if (tcp_trace_active || tcp_extns_trace_active)
{
op_prg_odb_print_major
("TCP received a duplicate ACK, but there is no outstanding data.", OPC_NIL);
}
FRET (0);
}
}
}
//检查未发送数据的确认
else if (tcp_seq_gt (seg_ack, MAX (snd_nxt, snd_max))) //如果ack序列号大于下一个预期序列号或者发送分组的最大序列号
{
if (tcp_trace_active)
op_prg_odb_print_major ("TCP received ACK of data not yet sent; sending ACK.", OPC_NIL);
tcp_ack_unsent_log_write ();
tcp_ack_schedule ();
FRET (0);
}
//处理最新接收到的确认
if (tcp_seq_gt (seg_ack, snd_una)) //如果ack序列号大于未确认的序列号
{
acked_bytes = seg_ack - snd_una;
//如果ack确认了一些数据,将他们从未确认的缓冲期清除
tcp_acked_bytes_processing ();
if ((tcp_flavor == TcpC_Flavor_New_Reno) && tcp_seq_lt (snd_una,snd_recover)) //快速恢复还未结束
{
tcp_new_reno_retransmit ();
}
//设置时间戳
if (conn_supports_ts == TCPC_OPTION_STATUS_DISABLED)
{
tcp_rtt_measurements_update (rtt_base_time);
}
else
{
tcp_ts_info_process (ev_ptr->pk_ptr);
}
tcp_sack_processing (ev_ptr->pk_ptr);
if (SACK_PERMITTED && (op_prg_list_size (scoreboard_ptr->entries) != 0))
{
tcp_scoreboard_update_newack (seg_ack, old_snd_una);
//更新选择性确认数据的数量
if (tcb_ptr->tcp_conn_stat_ptr != OPC_NIL &&
op_stat_valid (tcb_ptr->tcp_conn_stat_ptr->sacked_data_stathandle) == OPC_TRUE)
{
op_stat_write (tcb_ptr->tcp_conn_stat_ptr->sacked_data_stathandle, (double) tcp_sack_number_sacked_bytes_find ());
}//将tcp-sack-number-sacked-bytes-find的值写给sacked-data-stanthandle统计量
}
//更新拥塞窗口
tcp_cwnd_update (acked_bytes);
//因为ack序列号大于snd-una序列号,所以他一定是确认序列号的第一个ack
dup_ack_cnt = 0;
//重传定时器重置
tcp_retrans_timer_reset ();
}
//如果接收到当前分组,更新远程接收窗口
/*************************************************************/
if (tcp_seq_lt (snd_wl1, seg_seq) || (snd_wl1 == seg_seq && tcp_seq_le (snd_wl2, seg_ack)))
//如果上一次窗口更新的分组序列号小于当前分组序列号,或者,
//上一次窗口更新的序列号等于当前序列号以及上一次窗口更新时ack序列号小于等于当前ack序列号
{
tcp_send_window_update (seg_seq, seg_ack, fd_ptr->rcv_win);
//记录通知接收窗口的统计量
if (tcb_ptr->tcp_conn_stat_ptr != OPC_NIL &&
op_stat_valid (tcb_ptr->tcp_conn_stat_ptr->remote_rcv_win_stathandle) == OPC_TRUE) //远程接收窗口统计句柄为true
{
op_stat_write (tcb_ptr->tcp_conn_stat_ptr->remote_rcv_win_stathandle, snd_wnd); //snd-wnd:远程接收窗口的大小
}
//取消持久定时器
if (op_ev_valid (persist_evh) && op_ev_pending (persist_evh)) //持久定时器有效 && 持久定时器在事件列表中等待调度
if (op_ev_cancel (persist_evh) == OPC_COMPCODE_FAILURE) //取消持久定时器的调度失败
tcp_conn_warn ("Unable to cancel retransmission timeout.", //提示无法取消重传定时溢出,可能会发生杂散重传
"Spurious retransmission may take place.", OPC_NIL);
}
//如果当前没有未确认的数据,但是远程接收窗口大小为0
//设置重传超时,所以我们能修剪远程接收窗口大小
if (snd_una == snd_max && snd_wnd == 0) //如果未确认序列号=最大序列号,且接收窗口0
{
if (!op_ev_valid (persist_evh) || !op_ev_pending (persist_evh))//持久定时器无效且不能调度
{
//计算下一个超时时间
next_timeout_time = Tcp_Slowtimo_Next_Timeout_Time_Obtain (current_time, persist_timeout, timer_gran);
persist_evh = op_intrpt_schedule_call (next_timeout_time, 0, tcp_timeout_persist, OPC_NI
tcp_ack_check.rar_opnet tcp_tcp协议ack机制在opnet中实现
版权申诉
5星 · 超过95%的资源 38 浏览量
2022-09-24
22:24:51
上传
评论
收藏 3KB RAR 举报
钱亚锋
- 粉丝: 86
- 资源: 1万+
最新资源
- MyBatis动态SQL是一种强大的特性,它允许我们在SQL语句中根据条件动态地添加或删除某些部分,从而实现更加灵活和高效的数据
- MyBatis动态SQL是一种强大的特性,它允许我们在SQL语句中根据条件动态地添加或删除某些部分,从而实现更加灵活和高效的数据
- MyBatis动态SQL是一种强大的特性,它允许我们在SQL语句中根据条件动态地添加或删除某些部分,从而实现更加灵活和高效的数据
- 关于mybatis的一些相关资源
- 关于mybatist的一些相关资源
- uni-app实战社区交友类app开发&带视频教程
- mybatis动态sql的一些相关资源
- 隐马尔可夫模型在期货市场的应用_曾琦裕 (1).caj
- 极域解控和极域反控!!!
- 安卓android-serialport-api 串口demo源代码.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈