⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp.c

📁 完整的TCP/IP源代码,绝对好用
💻 C
📖 第 1 页 / 共 2 页
字号:
#include "C:\wql\tcpipsocket\target.h"
#include "C:\wql\tcpipsocket\tcp.h"
#include "C:\wql\tcpipsocket\func.h"
#include "C:\wql\tcpipsocket\ip.h"
#include <string.h>
#include <stdio.h>


UINT16 sender_tcpport;
UINT16 receiver_tcpport;

UINT8 sender_ipaddr[4];
extern UINT8 MY_IP[4];
extern IP_frame IP_IN;
UINT32 initial_sequence_nr;

CONNECTION xdata conxn[MAX_SOCKET_NUM];

UINT8 xdata opt[10] = {0x02, 0x04, 0x05, 0xB4,0x01, 0x01,0x04, 0x02};//最大片长0x05b4(1460)

//**************************************************************
void conxn_save_data(UINT8 nr,UINT8 *SRC,UINT16 data_len);
//*******************************************************************
// SRC_PORT本地端口号
// hdr_len TCP头长度
// nr 连接的号码
// flags TCP的标志字

void tcp_send(UINT16 flags, UINT16 hdr_len,UINT8 nr)
{
   //UINT16  result;
   UINT8  outbuf[50];
   TCPLAYER   *tcp;
   UINT8 dest[4];
   
   tcp = (TCPLAYER *)&outbuf[12];
   // If no connection, then message is probably a reset
   // message which goes back to the sender
   // Otherwise, use information from the connection.
   
   if (nr == NO_CONNECTION)
   {
      tcp->tcp_dest = sender_tcpport;
      tcp->tcp_src  = receiver_tcpport;
      tcp->tcp_seq = 0;
      tcp->tcp_ack = 0;
      memcpy(dest,sender_ipaddr,4);
   }
   else  //if (nr < NO_CONNECTION)
   {
      // This message is to connected port
      tcp->tcp_src = conxn[nr].local_port; //SRC_PORT;
      tcp->tcp_dest = conxn[nr].port;
      tcp->tcp_seq = conxn[nr].my_sequence;
      tcp->tcp_ack = conxn[nr].his_sequence;
      memcpy(dest, conxn[nr].ipaddr,4);
   }
 
   // Total segment length = header length
      
   // Insert header len
   tcp->flags = (hdr_len << 10) | flags;
   tcp->tcp_window = conxn[nr].local_window;
   tcp->tcp_check = 0;
   tcp->tcp_urgent = 0;
   
   // Sending SYN with header options
   if (hdr_len == 28)
   {
      memcpy(&outbuf[32],opt, 8);
   }   
   // Compute checksum including 12 bytes of pseudoheader
	// Must pre-fill 2 items in ip header to do this
   memcpy(outbuf,dest,4);
   memcpy(&outbuf[4],MY_IP,4);
   outbuf[8] = 0;   
   outbuf[9] = 6;   //proto is tcp
   outbuf[10] = 0;
   outbuf[11] = hdr_len;
   
	// Sum source_ipaddr, dest_ipaddr, and entire TCP message 
	tcp->tcp_check = ~Check_sum(outbuf , 12 + hdr_len);
//	tcp->tcp_check = 0;

 	IP_Send(&outbuf[12], hdr_len,dest, IP_TCP_PROT, 120);
	if(flags != FLG_ACK)
	{
	conxn[nr].old_sequence = conxn[nr].my_sequence;
	conxn[nr].my_sequence++;
    conxn[nr].timer = TCP_TIMEOUT;
	}
    else
       conxn[nr].timer = 0xff; //TCP_TIMEOUT;
}


//--------------------------------------------------------------------
void init_tcp(void)
{
   memset((UINT8 *)&conxn[0], 0, sizeof(conxn));
   initial_sequence_nr = 1;
}
//------------------------------------------------------------------------
// inbuf  -  a ip packet pointer
// 这个函数是接收一个TCP数据包,找到一个新的连接,或已有的连接,完成连接的状态迁移
// 并将TCP包的数据送到连接的输入缓冲区里,便于应用层操作。
// 另外当发送时出现丢包的现象,则对方会不停的发回上次的确认号,因此这个函数也可以根据
// 这个现象进行数据的冲传。
//*************************************************************************
void TCP_Interpret(UINT8 *inbuf, struct pseudotcp *tcp_chk)
{
      TCPLAYER  *tcp;
      IPLAYER       *ip;
      UINT16         sum,data_len;
      UINT8      TEMP[12];      
      UINT8       nr,i,header_len;
        
	//  IP headers = 20 bytes      
      tcp = (TCPLAYER *)(inbuf + 20);
      ip = (IPLAYER  *)inbuf ;
   
	// If the checksum is zero it means that the sender did not compute
	// it and we should not try to check it.
	if (tcp->tcp_check != 0)
	{
		memcp(TEMP,inbuf+8,12);
		memcp(inbuf+8,(UINT8 *)tcp_chk,12);
		sum = Check_sum(inbuf + 8, ip->ip_tlen - 8);  //the length contain pseudoheader
		memcp(inbuf+8,TEMP,12); //reload the old ip packet
		
	//	if (sum != 0xFFFF)   return;

	}
     // Capture sender's IP address and port number
      memcp(sender_ipaddr, ip->ip_src,4);
      sender_tcpport = tcp->tcp_src;	//这里得到两个端口号是为了发送NO CONNECTION准备的.
      receiver_tcpport = tcp->tcp_dest;

      header_len =  (tcp->flags & 0xF000) >> 10; //tcp header length
      data_len = ip->ip_tlen  - header_len - 20; //tcp data length

      // look up same connection already ok?
      for (i=0; i < MAX_SOCKET_NUM; i++)
      {
	    if(conxn[i].state != STATE_CLOSED)
		{
      	   if (tcp->tcp_dest == conxn[i].local_port)  //通过判断端口号来识别是否有连接等待此数据包.
      	   {
        	 nr = i;
             break;
           }
		}
       }
   // If i = MAX_SOCKET_NUM,that means we are not connected.
   // 发复位命令给对方并返回.
       if (i == MAX_SOCKET_NUM)
       {
	      tcp_send(FLG_RST,20,NO_CONNECTION);
		  return;
       } 
       
      // By now we should have a connection number in range of 0-MAX_SOCKET_NUM

      //if received a reset flag then clear connection and return
      if (tcp->flags & FLG_RST)
      {
	     printf("received rst\n");
		 //Init_cn(&conxn[nr]);
 	     conxn[nr].state = STATE_CLOSED; //这里还不能将状态置为STATE_CLOSED,因为有可能是STATE_LISTEN
         return;                         //解决的方法是在上层的轮询中,当置于LISTEN状态的变为了CLOSED,则将其变回LISTEN
      }

	  else if ((tcp->flags & (FLG_ACK |FLG_SYN )) == 0) return; //no ack then return.

	  conxn[nr].inactivity = INACTIVITY_TIME; //收到数据就更新.
 
      switch (conxn[nr].state)
      {
       	case STATE_CLOSED:
             //这里是不可能到的.
             break;
       	case STATE_LISTEN:
       		if ((tcp->flags & FLG_SYN) && ((tcp->flags & FLG_ACK) == 0)) //only syn
     		{
       			memcp(conxn[nr].ipaddr, ip->ip_src,4);	//此处建立了一个SOCKET连接
       			conxn[nr].port = tcp->tcp_src;
       			conxn[nr].his_sequence = 1 + tcp->tcp_seq;
       			conxn[nr].his_ack = tcp->tcp_ack;
				conxn[nr].local_port = tcp->tcp_dest;
                conxn[nr].my_sequence = initial_sequence_nr;
                conxn[nr].his_window = tcp->tcp_window;  
                initial_sequence_nr += 64000L;
                tcp_send(FLG_SYN | FLG_ACK, 28, nr); //send syn ack
                conxn[nr].state = STATE_SYN_RCVD;
       		}
       		else 
       		{
              tcp_send(FLG_RST, 20, NO_CONNECTION);   
   			}
       		break;
       		
       	case STATE_SYN_RCVD:
       		// He may already be sending me data - should process it
       		conxn[nr].his_sequence += data_len;
       		conxn[nr].his_ack = tcp->tcp_ack;
       		if (tcp->flags & FLG_FIN)
       		{
              conxn[nr].his_sequence++;
              tcp_send(FLG_ACK, 20,nr);
		      conxn[nr].state = STATE_CLOSE_WAIT;
              tcp_send(FLG_FIN | FLG_ACK, 20, nr);
             
              conxn[nr].state = STATE_LAST_ACK;
		               // Entered LAST ACK state
       		}
		    else if (conxn[nr].his_ack == conxn[nr].my_sequence) //ack is for me?
		    {
		      conxn[nr].state = STATE_ESTABLISHED; //建立了被动连接
			  conxn[nr].his_window = tcp->tcp_window;  
              conxn[nr].timer = 0xff; //TCP_TIMEOUT; 
		    }
		    break;
		      
		 case STATE_ESTABLISHED: //在这里连接可能是由被动建立的也可能由主动建立的,因此要考虑周全

		 	conxn[nr].his_ack = tcp->tcp_ack;
 		    conxn[nr].his_window = tcp->tcp_window;  
            //if(conxn[nr].his_window != 0)

            if (tcp->flags & FLG_FIN)
           	{
              conxn[nr].his_sequence++;
              tcp_send(FLG_ACK, 20, nr);
              tcp_send(FLG_FIN | FLG_ACK, 20, nr);
              conxn[nr].state = STATE_LAST_ACK;
			  break;
			}
			
		    if (data_len != 0)
			{
			  if(tcp->tcp_seq == conxn[nr].his_sequence) //如果对方的序号不对,则只向其发送上次的seq的ack
			  {
			    if((data_len ==1)&& (conxn[nr].local_window==0))
				{
				  printf("data invalid\n");

				  break;
			    }
			    conxn_save_data(nr,inbuf+header_len+20,data_len);//把收到的数据存放到SOCKET的接收缓存中并调整窗口大小
	       	    conxn[nr].his_sequence += data_len; //调整
			  }
	       	  tcp_send(FLG_ACK, 20, nr); 		// Send ACK
			  
			}
			if(conxn[nr].his_ack == conxn[nr].my_sequence)  //peer ack is my sequence
			{
			  conxn[nr].wait_ack_num = 0; //将等待ACK的TCP包的数量清0,表示每个包都收到了ACK
			  conxn[nr].S_R_ACK_NUM  = 0;
 	          conxn[nr].timer = 0xff; //TCP_TIMEOUT;

    	      //conxn[nr].inactivity = INACTIVITY_TIME;	//前面已经更新了
			}
            else if(conxn[nr].his_ack < conxn[nr].my_sequence) //是中间过程的ACK,要调整等待TCP包的标志
           	{
			  {
			    UINT8 i;
				for(i=0;i<conxn[nr].wait_ack_num;i++)
				{
				  if(conxn[nr].tcp_wait_ack[i].State == 1)  //看这一包收到ACK了吗
				  {
				    if(conxn[nr].his_ack < conxn[nr].tcp_wait_ack[i].expect_ack) 
					{
					  if(conxn[nr].his_ack == conxn[nr].tcp_wait_ack[i].TCP_HEAD.tcp_seq) //这里是当连续收到同一个ACK时,表示要重传这一包后面的一包。
					  												//这个判断可能是多余的,因为如果不等的话,就全乱了
					  {
					    conxn[nr].tcp_wait_ack[i].R_ACK_NUM++;  //上一包接收到ACK的次数,说明本包是否需要重传
						if(conxn[nr].tcp_wait_ack[i].R_ACK_NUM ==3) 
					    {
						   printf("require repeat\n");
					       conxn[nr].tcp_wait_ack[i].R_ACK_NUM = 3;
					       conxn[nr].S_R_ACK_NUM = 3; //表示有需要重传的包
					    }
					  }
					  break; //已经调整完毕,跳出FOR循环

					}
					conxn[nr].tcp_wait_ack[i].State = 0;  //置本包已经成功收到ACK标志。
					conxn[nr].tcp_wait_ack[i].R_ACK_NUM = 0;
				  }
				}
			  }
           	}
			//else  printf("his ack too big\n"); //这种情况是不可能出现的。
            break;

         case STATE_CLOSE_WAIT:
              // With this code, should not get here
      
            break;
      
         case STATE_LAST_ACK:  //这里是当被动连接收到一个FIN并发送了FIN ACK后,又收到了ACK,则将连接关闭
              conxn[nr].his_ack = tcp->tcp_ack;
              if (tcp->tcp_ack == conxn[nr].my_sequence)
              {
			    printf("received last ack\n");
                conxn[nr].state = STATE_CLOSED;  
           	  }
              break;
                          
          case STATE_FIN_WAIT_1:   //我方已经先发出了FIN命令,此时如果又收到对方的FIN则可以关闭连接了
   		      conxn[nr].his_sequence += data_len;
              conxn[nr].his_ack = tcp->tcp_ack;
              if (tcp->flags & FLG_FIN) 
              {
                conxn[nr].his_sequence++;
                tcp_send(FLG_ACK, 20, nr);
         
                if (tcp->tcp_ack == conxn[nr].my_sequence)
                {
                  conxn[nr].state = STATE_CLOSED;
                }
                else conxn[nr].state = STATE_CLOSING;
              }
              else if (tcp->tcp_ack == conxn[nr].my_sequence) //如果没有收到FIN,而只是ACK
              {
                conxn[nr].state = STATE_FIN_WAIT_2;
              }
              break;
                            
         case STATE_FIN_WAIT_2:  //我方发送了FIN但是对方还没有回FIN,还继续发送数据,则要处理他
		      conxn[nr].his_sequence += data_len;
              conxn[nr].his_ack = tcp->tcp_ack;
              if (tcp->flags & FLG_FIN)
              {
                conxn[nr].his_sequence++; // For his FIN flag
                tcp_send(FLG_ACK, 20, nr);
                conxn[nr].state = STATE_CLOSED;
              }
              break;
                       
         case STATE_CLOSING:  //我先送FIN可对方回答的ACK不是我的,才进入这个状态,此时只等待ACK
              conxn[nr].his_ack = tcp->tcp_ack;
            		
	          if (tcp->tcp_ack == conxn[nr].my_sequence)
	          {
                 conxn[nr].state = STATE_CLOSED;
	          }
	          break;
	                   
		 case STATE_ACT_SYN_SENT: //active to establish the socket 已经发出了SYN命令,等待对方的SYN ACK
		      conxn[nr].his_ack = tcp->tcp_ack;
		      if((conxn[nr].his_ack == conxn[nr].my_sequence) &&(tcp->flags & FLG_SYN))
			  {
      	   		conxn[nr].his_sequence = 1 + tcp->tcp_seq;
				conxn[nr].his_window = tcp->tcp_window;
		 		tcp_send(FLG_ACK, 20, nr);
		 		conxn[nr].state = STATE_ESTABLISHED;   //tcp socket established
 		 	  }
			  else
			  {
	            tcp_send(FLG_RST, 20, nr);
				conxn[nr].state = STATE_CLOSED;
			  }
	          break;   

          default:
              break;
   	}
   
}

//------------------------------------------------------------------------
//
// buf      -- tcp data buffer存放TCP数据的缓冲区
// len      -- TCP 数据长度
// nr       -- connection number
// 返回实际传送的长度。
//------------------------------------------------------------------------

// 向第NR个连接发送长度为LEN的数据,数据存放在OUTBUF中,OUTBUF是一个IP型的PACKET。

UINT16 TCP_data_send(UINT8  *buf, UINT16 len, UINT8 nr)
{
   UINT16 MIN_PACKET_SIZE;
   UINT16 REMAINING; //剩余的
   UINT16 SENT;  //已经传送的数据长度
   UINT16 one_send_len;

   if(len==0) return 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -