📄 tcp.c
字号:
REMAINING = len;
SENT = 0;
if(conxn[nr].his_window > 1300) MIN_PACKET_SIZE = 1300; //获得每个TCP包的最小长度
else MIN_PACKET_SIZE = conxn[nr].his_window;
if(MIN_PACKET_SIZE ==0)
{
printf("his window is 0\n");
return 0;
}
while((REMAINING != 0) && (SENT != conxn[nr].his_window)) //
{
if(REMAINING > MIN_PACKET_SIZE) //这是判断是否已经传送数据的末尾
{
one_send_len = MIN_PACKET_SIZE; //
}
else one_send_len = REMAINING;
if(conxn[nr].his_window < SENT+one_send_len)
{
one_send_len = conxn[nr].his_window - SENT; //这里是发送剩余部分数据,因为已经到了对方窗口的极限
}
tcp_send_1_packet(buf+SENT,one_send_len,nr); //将这一包传送出去
REMAINING -= one_send_len;
SENT += one_send_len; //调整已经传送的数据的长度。
conxn[nr].timer = TCP_TIMEOUT;
conxn[nr].my_sequence += one_send_len; // 调整SEQ
}
return SENT; //返回已经传送的数据长度
}
//******************************************
// 找一个空闲的连接,并返回这个连接的号码.
//
UINT8 find_leisure_connection()
{
UINT8 i;
for(i=0;i<MAX_SOCKET_NUM;i++)
{
if(conxn[i].state == STATE_CLOSED) return i;
}
return NO_CONNECTION;
}
//*************************************************************
//
// 把收到的数据送往连接的接收缓冲区,并调整接收WINDOW的大小
// 接收缓冲是循环的
//*************************************************************
void conxn_save_data(UINT8 nr,UINT8 *SRC,UINT16 data_len)
{
UINT16 i;
for(i=0;i<data_len;i++)
{
conxn[nr].inbuf[conxn[nr].inbuf_tail] = *(SRC+i);
conxn[nr].inbuf_tail ++;
conxn[nr].inbuf_tail &= max_mm; //循环存放
}
if(conxn[nr].inbuf_tail > conxn[nr].inbuf_head)
{
conxn[nr].local_window = max_socket_in_len-conxn[nr].inbuf_tail+conxn[nr].inbuf_head; //调整窗口大小
}
else
{
conxn[nr].local_window = conxn[nr].inbuf_head-conxn[nr].inbuf_tail; //调整窗口大小
}
if(conxn[nr].local_window ==0)
{
// conxn[nr].local_window =0;
conxn[nr].flags |= FLG_WIN_ZERO;
}
}
//****************************************************************************
// 由应用程序调用
//
// 每隔一段时间调用一次,检查各连接是否有需要传送的内容。
//
// 内容包括:1、要主动发起连接
// 2、要主动结束连接
// 3、要复位连接
// 4、有数据要发送
// 5、有超时传送存在
//
//**************************************************************
void TCP_TICK()
{
UINT8 i;
UINT16 TEMP;
for(i=0;i<MAX_SOCKET_NUM;i++)
{
if(conxn[i].state != STATE_CLOSED &&conxn[i].state != STATE_LISTEN)
{
if(conxn[i].flags & FLG_WIN_ZERO ) //检查上次窗口是否为0,如果是0,则要发送ACK
{
if(conxn[i].local_window !=0)
{
tcp_send(FLG_ACK,20,i); //通知窗口大小
conxn[i].flags &= ~FLG_WIN_ZERO;
}
}
switch(conxn[i].flags &~FLG_WIN_ZERO) //这个FLAGS是由上层来置的,
{
case FLG_SYN: //发起连接
tcp_send(FLG_SYN,28,i); //将MTU通知对方
conxn[i].state = STATE_ACT_SYN_SENT;
conxn[i].flags = 0;
break;
case FLG_FIN: //要结束此连接
tcp_send(FLG_FIN|FLG_ACK,20,i);
conxn[i].state = STATE_FIN_WAIT_1;
conxn[i].flags = 0;
break;
case FLG_RST: //要复位此连接
tcp_send(FLG_RST,20,i);
conxn[i].state = STATE_CLOSED;
conxn[i].flags = 0;
break;
case FLG_PSH: //有数据要传送,并且上次传送的一大包数据对方已经全部收到,这个大包是由对方的窗口大小决定的
if(conxn[i].his_ack == conxn[i].my_sequence)
{
conxn[i].wait_ack_num = 0;
if(conxn[i].offset < conxn[i].outbuf_len)
{
if(conxn[i].state == STATE_ESTABLISHED)
{
TEMP = TCP_data_send(&(conxn[i].outbuf[conxn[i].offset]),conxn[i].outbuf_len-conxn[i].offset,i); //outbuf_len在收到确认后才清0,这是为了重传
//conxn[i].timer = TCP_TIMEOUT; //超时时间5秒,在时钟中断里减,直到0,如果收到了确认,则将TIMER设定为FF,不需要减了
conxn[i].offset += TEMP;
}
}
else
{
conxn[i].flags = 0; //表示已经传送完了,并也完全收到了ACK
conxn[i].outbuf_len = 0;
conxn[i].offset = 0;
}
}
else //还没有完全收到对方的ACK,此时要检查是否有需要重传的。
{
if(CHECK_RSEND(&conxn[i])) //重传3次就复位该连接
{
tcp_send(FLG_RST, 20, i);
Init_cn(&conxn[i]);
/*
conxn[i].flags = 0; //表示已经传送完了,并也完全收到了ACK
conxn[i].outbuf_len = 0;
conxn[i].offset = 0;
conxn[i].wait_ack_num = 0;
conxn[i].state=STATE_CLOSED;//关闭此连接
*/
}
}
break;
default: //检查是否有超时存在
if(conxn[i].inactivity==0) //这个计数器在每次收到任何来自对方的信息时都更新
{
tcp_send(FLG_RST,20,i); //在inactivity定义的时间内没有收到来自对方的任何反应,即表示此连接已无效
conxn[i].state = STATE_CLOSED;
conxn[i].flags = 0;
}
if((conxn[i].timer==0)&& conxn[i].repeat < 3) //这个定时器用于当发送的数据包在TIMER时间内没有收到应答,则要重新传输此数据
{ //此时有几种可能:1、SYN没收到 2、FIN没收到 3、FIN ACK没收到 4、数据没收到
switch(conxn[i].state)
{
case STATE_ACT_SYN_SENT: //说明是主动发送的SYN没有收到应答,则重新传递
{
conxn[i].my_sequence = conxn[i].old_sequence; //重串SYN包
conxn[i].repeat ++;
if(conxn[i].repeat == 4)
{
printf("repeat syn end\n");
conxn[i].state = STATE_CLOSED;
}
else
{
tcp_send(FLG_SYN,28,i);
printf("r send syn\n");
}
}
break;
case STATE_FIN_WAIT_1: //表示主动发送的FIN没有被收到
case STATE_LAST_ACK: //收到对方的FIN后,发回了FIN ACK可对方没有回答 ,重发FIN ACK
conxn[i].my_sequence = conxn[i].old_sequence;
conxn[i].repeat ++;
if(conxn[i].repeat == 4)
{
printf("repeat fin end\n");
conxn[i].state = STATE_CLOSED;
}
else
{
tcp_send(FLG_FIN|FLG_ACK,20,i);
printf("r send fin\n");
}
break;
case STATE_ESTABLISHED: //数据重传,这里不可能到,因为已经在上面处理了一次(CASE FLG_PSH)
printf("it is not here\n");
// {
// conxn[i].my_sequence = conxn[i].old_sequence;
// TCP_data_send(conxn[i].outbuf,conxn[i].outbuf_len,i);
// conxn[i].my_sequence += conxn[i].outbuf_len;
// }
break;
default:
printf("it is error\n");
break;
}
conxn[i].repeat++;
if(conxn[i].repeat==3) //重传3次
{
printf("repeat end\n");
conxn[i].state = STATE_CLOSED; //关闭连接。
conxn[i].repeat = 0;
}
}
break;
}
}
}
}
//***************************************************************
//传送1包TCP数据
void tcp_send_1_packet(UINT8 *buf, UINT16 len, UINT8 nr)
{
TCPLAYER xdata * tcp;
struct pseudotcp *TCP_chk;
UINT8 *outbuf;
outbuf = (UINT8 *)&IP_IN; //作为数据的唯一的传输通道
memcp(outbuf+40,buf,len);
// Fill in TCP segment header
tcp = (TCPLAYER *)(outbuf + 20);
TCP_chk = (struct pseudotcp *)(outbuf +8);
tcp->tcp_src = conxn[nr].local_port; //PORT;
tcp->tcp_dest = conxn[nr].port;
tcp->tcp_seq = conxn[nr].my_sequence;
tcp->tcp_ack = conxn[nr].his_sequence;
// Header is always 20 bytes long
tcp->flags = 0x5000 | FLG_ACK | FLG_PSH;
tcp->tcp_window = conxn[nr].local_window;
tcp->tcp_check = 0;
tcp->tcp_urgent = 0;
// Compute checksum including 12 bytes of pseudoheader
// Must pre-fill 2 items in ip header to do this
memcp(TCP_chk->source, conxn[nr].ipaddr,4);
memcp(TCP_chk->dest,MY_IP,4);
TCP_chk->z = 0;
TCP_chk->proto = 6;
TCP_chk->tcplen = 20+len;
// Sum source_ipaddr, dest_ipaddr, and entire TCP message
tcp->tcp_check = ~Check_sum(outbuf + 8, 32 + len);
// tcp->tcp_check = 0;
IP_Send(&outbuf[20], len+20, conxn[nr].ipaddr, IP_TCP_PROT, 120);
memcp((UINT8 *)&conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].TCP_HEAD,&outbuf[20],20); //存放的是TCP包头
conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].Repeat = 0;
conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].Timer = TCP_TIMEOUT;
conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].START_PTR = buf;
conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].LENGTH = len;
conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].State = 1; //wait for ack
conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].expect_ack = conxn[nr].my_sequence+len; //期待受到的ACK
conxn[nr].tcp_wait_ack[conxn[nr].wait_ack_num].R_ACK_NUM = 0;
conxn[nr].wait_ack_num++;
if(conxn[nr].wait_ack_num == max_tcp_wait_num) //等待ACK的TCP包超过设定的极限时,将只保留最后一个
{
printf("tcp wait ack full\n");
conxn[nr].wait_ack_num--;
}
}
//****************************************************************************************************
// 检查是否有需要重传的TCP包,如果有则重新传送那一包
// 重传的条件一个是对方不停回答同一个ACK超过3次,另一个是超时TCP_TIMEOUT
// 重传3次后就复位此连接。
// 返回:0表示不复位此连接 1是复位
UINT8 CHECK_RSEND(CONNECTION *CN)
{
UINT8 i;
UINT8 *outbuf;
struct pseudotcp *TCP_chk;
outbuf = (UINT8 *)&IP_IN;
if(CN->wait_ack_num ==0) return 0;
if(CN->S_R_ACK_NUM ==0) return 0; //没有需要重传的包,表示在
for(i=0;i<CN->wait_ack_num;i++)
{
if((CN->tcp_wait_ack[i].R_ACK_NUM ==3)||(CN->tcp_wait_ack[i].Timer ==0)) //找需要重传的数据包,这是由对方的不停的回同一个ACK或超时造成的
{
CN->tcp_wait_ack[i].TCP_HEAD.tcp_ack = CN->his_sequence; //要将我方的ACK变为最新的送回去
CN->tcp_wait_ack[i].TCP_HEAD.tcp_window = CN->local_window;
CN->tcp_wait_ack[i].TCP_HEAD.tcp_check = 0;
memcp(outbuf+20,(UINT8 *)&CN->tcp_wait_ack[i].TCP_HEAD,20); //先把TCP头拷贝过去,再拷贝数据
memcp(outbuf+40,CN->tcp_wait_ack[i].START_PTR,CN->tcp_wait_ack[i].LENGTH); //tcp data
TCP_chk = (struct pseudotcp *)(outbuf +8); //
memcp(TCP_chk->source, CN->ipaddr,4);
memcp(TCP_chk->dest,MY_IP,4);
TCP_chk->z = 0;
TCP_chk->proto = 6;
TCP_chk->tcplen = 20+CN->tcp_wait_ack[i].LENGTH;
CN->tcp_wait_ack[i].TCP_HEAD.tcp_check = ~Check_sum(outbuf + 8, 32 + CN->tcp_wait_ack[i].LENGTH);
memcp(outbuf+20,(UINT8 *)&CN->tcp_wait_ack[i].TCP_HEAD,20);
IP_Send(&outbuf[20], CN->tcp_wait_ack[i].LENGTH+20, CN->ipaddr, IP_TCP_PROT, 120);
CN->tcp_wait_ack[i].R_ACK_NUM = 0; //本包数据重传次数计数
CN->tcp_wait_ack[i].Timer = TCP_TIMEOUT;
CN->tcp_wait_ack[i].Repeat++;
if(CN->tcp_wait_ack[i].Repeat>3)
{
printf("repeat 3\n");
CN->repeat = 3; //重传次数超过了3次。应复位对方
return 1;
}
}
}
return 0;
}
//***********************************************************************
// 这个函数要在主程序的时钟中断里每隔0.5S调用一次,就是对SOCKET级的Timer和inactivity,以及在SOCKET的
// tcp_wait_ack中的timer做减一操作,而在TCP_TICK中处理重传问题
// 这样可以减少中断处理的开销
//***********************************************************************
void tcp_timer_scan()
{
UINT8 nr,i;
// Scan through all active connections
for (nr = 0; nr < MAX_SOCKET_NUM; nr++)
{
if ((conxn[nr].state != STATE_CLOSED) && (conxn[nr].state != STATE_LISTEN))//这两种状态都是原始态肯定没有超时
{
if (conxn[nr].timer) conxn[nr].timer--;
if (conxn[nr].inactivity) conxn[nr].inactivity--;
if(conxn[nr].state==STATE_ESTABLISHED && conxn[nr].flags == FLG_PSH)//说明是在数据传输过程中
{
for(i=0;i<conxn[nr].wait_ack_num;i++) //遍历所有的存储包
{
if(conxn[nr].tcp_wait_ack[i].State ==1) //只查没有收到ACK的包
{
if(conxn[nr].tcp_wait_ack[i].Timer!=0) conxn[nr].tcp_wait_ack[i].Timer--;
}
}
}
}
} //end for
}
//******************************************
// 对一个连接清零
void Init_cn( CONNECTION * CN)
{
UINT16 TT;
TT = CN->local_port;
memset((UINT8 *)CN, 0, sizeof(CONNECTION));
CN->local_port = TT; //
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -