在 TCP 通讯中,主要有建立连接、传输数据、关闭连接三个过程。建立连接和传输数据,前面已经写过了。关闭连接是和 FIN 有关的数据包,也可以发送 RST 数据包,强制关闭连接。
//函数功能:发送"FIN+ACK数据包"
void ENC28J60_Send_FIN_ACK_data_packet(struct ConnectTableType *pConnect)
{
u8 ret;
struct TCP_IPv4_Packet_Type *pTX;
u16 tmp,chksum;
int t;
ret=Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(pConnect);
//将"pConnect指向的连接参数"装载到MyNetworkInformation中
//主要是用来装载"连接参数",如:远程IP,远程MAC,远程端口,本地端口和连接表指针
if(ret==1)
{
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
memcpy(pTX->dest_MAC, MyNetworkInformation.Remote_MAC, 6);
//添加远程MAC地址
memcpy(pTX->src_MAC,MyNetworkInformation.Local_MAC, 6);
//添加本地MAC地址
pTX->type[0] = (u8)(UIP_ETHTYPE_IP4>>8);
pTX->type[1] = (u8)(UIP_ETHTYPE_IP4);
//设置太网帧的类型为0x0800,表示后面跟着的是IPV4数据包;
pTX->vhl = 0x45;//设置"IP版本和头部长度"
pTX->tos = 0;//设置"服务类型"
tmp = UIP_IPH_LEN + UIP_TCPH_LEN;
//"IP头部的大小20"+"TCP头部的大小20" ,得到tmp
#if UIP_CONF_IPV6
//For IPv6, the IP length field does not include the IPv6 IP header length.
pTX->len[0] = ((tmp - UIP_IPH_LEN) >> 8);
pTX->len[1] = ((tmp - UIP_IPH_LEN) & 0xff);
#else
pTX->len[0] = (tmp >> 8);
pTX->len[1] = (tmp & 0xff);
//设置IP头部中的"IP头部,TCP头部和可选项"的字节总数
#endif
++My_IPID;
pTX->ipid[0] = My_IPID >> 8;
pTX->ipid[1] = My_IPID & 0xff;//设置"标识"
pTX->ipoffset[0] =0;
pTX->ipoffset[1] = 0;
//设置"标志字段和片偏移字段",0x0000表示不分包
pTX->ttl = UIP_TTL;//修改IP头部中的"生存时间值"
pTX->protocol = UIP_PROTO_TCP;//修改"IP头部中的协议"为6
memcpy(pTX->Send_IP,MyNetworkInformation.Local_IP,4);
//设置"发送方ip地址"
memcpy(pTX->Receive_IP,MyNetworkInformation.Remote_IP,4);
//设置"接收方IP地址"
pTX->ipchksum[0] = 0;pTX->ipchksum[1] = 0;
chksum=~(IPv4_checksum(&TCP_TX_buf[UIP_LLH_LEN]));
pTX->ipchksum[0] = (u8)(chksum>>8);
pTX->ipchksum[1] = (u8)(chksum);
//设置"IPV4检验和"
memcpy(pTX->Send_Port, MyNetworkInformation.Local_Port, 4);
//设置"发送方端口"
memcpy(pTX->Receive_Port, MyNetworkInformation.Remote_Port, 4);
//设置"接收方端口
//使用接收到的确认号作为"TCP头部中的序列号"
pTX->seqno[0] = pConnect->Send_next[0];
pTX->seqno[1] = pConnect->Send_next[1];
pTX->seqno[2] = pConnect->Send_next[2];
pTX->seqno[3] = pConnect->Send_next[3];
//修改"TCP头部中的确认号"
//因为这里不是应答远程设备,所以不用将"接收到的序列号+用户数据长度"作为确认号
pTX->ackno[0] = pConnect->Receive_next[0];
pTX->ackno[1] = pConnect->Receive_next[1];
pTX->ackno[2] = pConnect->Receive_next[2];
pTX->ackno[3] = pConnect->Receive_next[3];
t=pTX->len[0];t=(u16)(t<<8);
t=t|pTX->len[1];
t=t-UIP_TCPH_LEN - UIP_IPH_LEN;
if(t<=0)t=1;
pConnect->len = t;
pTX->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
//TCP数据包无optdata[],BUF->tcpoffset=0x50
//UIP_TCPH_LEN=20,设置"TCP头部的长度"为5*4=20
pTX->flags = (TCP_FIN | TCP_ACK);
//TCP头部的标志位为"FIN+ACK"
pTX->wnd[0] = ((UIP_TCP_MSS) >> 8);//修改"TCP头部中的窗口大小"
pTX->wnd[1] = ((UIP_TCP_MSS) & 0xff);//修改"TCP头部中的窗口大小"
pTX->urgp[0] = 0;
pTX->urgp[1] = 0;//修改TCP头部中的"紧急指针"
TCP_TX_Length = UIP_LLH_LEN + UIP_IPH_LEN + UIP_TCPH_LEN;
//"以太网头部的大小14"+"IP头部的大小20"+"TCP头部的大小20",得到TCP_TX_Length
//计算TCP头部校验和
pTX->tcpchksum[0] = 0;pTX->tcpchksum[1] = 0;
chksum=~(TCPv4_header_checksume());
//计算TCP头部的校验和
pTX->tcpchksum[0] = (u8)(chksum>>8);
pTX->tcpchksum[1] = (u8)(chksum);
ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);
#if TCP_Debug == 1
Print_MyConnectTable();
#endif
}
}
//函数功能:告诉对方断开连接
u8 ENC28J60_Disconnect(struct ConnectTableType *pConnect)
{
u8 loop,tmp,ret;
u16 cnt;
pConnect->TCPStateFlag=TCP_CLOSING;
loop=2;ret=0;
while(loop)
{
ENC28J60_Send_FIN_ACK_data_packet(pConnect);//断开连接步骤1
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
loop--;
cnt=UIP_TTL*4;
while(cnt)
{
delay_ms(1);
cnt--;
tmp=ENC28J60_Receive_Data_From_Ethernet_Network();
if(tmp==TCP_ACK_RX_Flag)
{//断开连接步骤2,接收到"ACK数据包"
ret=1;
cnt=0;
loop=0;
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
#endif
}
}
}
cnt=0;ret=0;
if(tmp==TCP_ACK_RX_Flag) cnt=UIP_TTL*4;
while(cnt)
{
cnt--;
delay_ms(1);
tmp=ENC28J60_Receive_Data_From_Ethernet_Network();
if(tmp==TCP_FIN_ACK_RX_Flag)
{//断开连接步骤3,接收到"FIN+ACK数据包"
ENC28J60_Send_ACK_data_packet(pConnect);
//发送ACK应答,表示自己进入了关闭等待(CLOSE-WAIT)状态
//断开连接步骤4,发送"ACK数据包"
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
ret=1;
cnt=0;
loop=0;
#if TCP_Debug == 1
Print_MyConnectTable();
#endif
pConnect->TCPStateFlag=TCP_CLOSED;
}
}
return(ret);
}
//函数功能:告诉对方断开连接
u8 ENC28J60_Disconnect_Reply(struct ConnectTableType *pConnect)
{
u8 loop,cnt,tmp,ret;
loop=2;ret=0;
while(loop)
{
ENC28J60_Send_FIN_ACK_data_packet(pConnect);
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
loop--;
cnt=UIP_TTL*2;
while(cnt)
{
delay_ms(1);
cnt--;
tmp=ENC28J60_Receive_Data_From_Ethernet_Network();
if(tmp==TCP_ACK_RX_Flag)
{
ret=1;
cnt=0;
loop=0;
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_MyConnectTable();
#endif
pConnect->TCPStateFlag=TCP_CLOSING;
}
}
}
return(ret);
}
//函数功能:接收来自计算机的TCP请求/TCP应答
u8 TCPIP4_Work(void)
{
struct TCP_IPv4_Packet_Type *pRX;
u16 opcode;
u8 ch1,ret;
struct ConnectTableType *pConnect;
ch1=0;ret=0;
if( Check_Receive_TCPv4_header_checksume() ) ch1=1;
//"接收TCPIP数据包之TCP头部校验和"正确,返回1
if(ch1==1)//接收到TCPIP数据包
{
pRX=(struct TCP_IPv4_Packet_Type *)&TCP_RX_buf[0];
pConnect=MyNetworkInformation.pConnect;
// Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
opcode=(u16)(pRX->flags & TCP_CTL);
printf("\r\nopcode=0x%02X\r\n",opcode);
if( opcode== (TCP_SYN | TCP_ACK) )//接收到"syn+ack应答"
{
Parse_the_TCP_MSS_option(pConnect);
//解析TCP MSS选项,获取对方的接收缓存有大。Parse the TCP MSS option, if present.
memcpy(pConnect->Remote_IP,pRX->Send_IP, 4);//保存远程IP
memcpy(pConnect->Remote_Port,pRX->Send_Port, 2);//保存远程端口
memcpy(pConnect->Local_Port,pRX->Receive_Port, 2);//保存本地端口
pConnect->TCPStateFlag = TCP_SYN_RECEIVED;//系统进入ESTABLISHED状态
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
pConnect->Time=0;
pConnect->ConnectFlag=1;//客户端建立连接
ret=1;
}
if( opcode== TCP_ACK )//接收到"ACK应答"
{
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
pConnect->Time=0;
ret=2;
}
if( opcode== (TCP_PSH | TCP_ACK) )
{//接收到"PSH+ACK应答",说明对方发送用户数据给我
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
ENC28J60_Send_ACK_data_packet(pConnect);//发送ACK应答
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
pConnect->Time=0;
ret=3;
}
if( opcode== (TCP_FIN | TCP_ACK) )//远程设备要求关闭"连接"
{
if(pConnect->TCPStateFlag==TCP_CLOSING)
{//本地主动关闭
ret=4;
}
else
{//远程关闭
pConnect->TCPStateFlag=TCP_CLOSING; //接收到"关闭连接"
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
ENC28J60_Send_ACK_data_packet(pConnect);
//发送ACK应答,表示自己进入了关闭等待(CLOSE-WAIT)状态
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
ENC28J60_Disconnect_Reply(pConnect);
pConnect->TCPStateFlag=TCP_CLOSED; //同意"关闭连接"
pConnect->Time=0;
ret=5;
}
}
}
return(ret);
}
//函数功能:发送"RST"数据包,用来断开与TCP服务器的连接
/*
发送"RST数据包"后,发送方会立即关闭连接,而不需要等待对方的确认。接收方收到RST包后,也会立即关闭连接。
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
0x45 0x00 0x00 0x28 0x00 0x03 0x00 0x00 0x40 0x06 0xF6 0xAD
0xC0 0xA8 0x01 0x11
0xC0 0xA8 0x01 0xBE
0x06 0x10 0x13 0x00 0x00 0xA0 0xB2 0x06 K8iP 0x04 0x00 0x00 0xBE 6 0x00 0x00
*/
void ENC28J60_Send_RST_data_packet(struct ConnectTableType *pConnect)
{
u8 ret;
struct TCP_IPv4_Packet_Type *pTX;
u16 tmp,chksum;
int t;
ret=Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(pConnect);
//将"pConnect指向的连接参数"装载到MyNetworkInformation中
//主要是用来装载"连接参数",如:远程IP,远程MAC,远程端口,本地端口和连接表指针
if(ret==1)
{
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
memcpy(pTX->dest_MAC, MyNetworkInformation.Remote_MAC, 6);
//添加远程MAC地址
memcpy(pTX->src_MAC,MyNetworkInformation.Local_MAC, 6);
//添加本地MAC地址
pTX->type[0] = (u8)(UIP_ETHTYPE_IP4>>8);
pTX->type[1] = (u8)(UIP_ETHTYPE_IP4);
//设置太网帧的类型为0x0800,表示后面跟着的是IPV4数据包;
pTX->vhl = 0x45;//设置"IP版本和头部长度"
pTX->tos = 0;//设置"服务类型"
tmp = UIP_IPH_LEN + UIP_TCPH_LEN;
//"IP头部的大小20"+"TCP头部的大小20"",得到len=40
#if UIP_CONF_IPV6
//For IPv6, the IP length field does not include the IPv6 IP header length.
pTX->len[0] = ((tmp - UIP_IPH_LEN) >> 8);
pTX->len[1] = ((tmp - UIP_IPH_LEN) & 0xff);
#else
pTX->len[0] = (tmp >> 8);
pTX->len[1] = (tmp & 0xff);
//设置IP头部中的"IP头部,TCP头部和可选项"的字节总数
#endif
++My_IPID;
pTX->ipid[0] = My_IPID >> 8;
pTX->ipid[1] = My_IPID & 0xff;//设置"标识"
pTX->ipoffset[0] =0;
pTX->ipoffset[1] = 0;
//设置"标志字段和片偏移字段",0x0000表示不分包
pTX->ttl = UIP_TTL;//修改IP头部中的"生存时间值"
pTX->protocol = UIP_PROTO_TCP;//修改"IP头部中的协议"为6
memcpy(pTX->Send_IP,MyNetworkInformation.Local_IP,4);
//设置"发送方ip地址"
memcpy(pTX->Receive_IP,MyNetworkInformation.Remote_IP,4);
//设置"接收方IP地址"
pTX->ipchksum[0] = 0;pTX->ipchksum[1] = 0;
chksum=~(IPv4_checksum(&TCP_TX_buf[UIP_LLH_LEN]));
pTX->ipchksum[0] = (u8)(chksum>>8);
pTX->ipchksum[1] = (u8)(chksum);
//设置"IPV4检验和"
memcpy(pTX->Send_Port, MyNetworkInformation.Local_Port, 4);
//设置"发送方端口"
memcpy(pTX->Receive_Port, MyNetworkInformation.Remote_Port, 4);
//设置"接收方端口
//使用接收到的确认号作为"TCP头部中的序列号"
pTX->seqno[0] = pConnect->Send_next[0];
pTX->seqno[1] = pConnect->Send_next[1];
pTX->seqno[2] = pConnect->Send_next[2];
pTX->seqno[3] = pConnect->Send_next[3];
//修改"TCP头部中的确认号"
//因为这里不是应答远程设备,所以不用将"接收到的序列号+用户数据长度"作为确认号
pTX->ackno[0] = pConnect->Receive_next[0];
pTX->ackno[1] = pConnect->Receive_next[1];
pTX->ackno[2] = pConnect->Receive_next[2];
pTX->ackno[3] = pConnect->Receive_next[3];
t=pTX->len[0];t=(u16)(t<<8);
t=t|pTX->len[1];
t=t-UIP_TCPH_LEN - UIP_IPH_LEN;
if(t<=0)t=1;
pConnect->len = t;
pTX->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
//TCP数据包无optdata[],BUF->tcpoffset=0x50
//UIP_TCPH_LEN=20,设置"TCP头部的长度"为5*4=20
pTX->flags = TCP_RST;
//TCP头部的标志位为"RST"
//设置"零窗口机制",使远程主机停止发送数据。
pTX->wnd[0] = 0;//修改"TCP头部中的窗口大小"
pTX->wnd[1] = 0;//修改"TCP头部中的窗口大小"
pTX->urgp[0] = 0;
pTX->urgp[1] = 0;//修改TCP头部中的"紧急指针"
pTX->tcpchksum[0] = 0;pTX->tcpchksum[1] = 0;
chksum=~(TCPv4_header_checksume());
//计算TCP头部的校验和
pTX->tcpchksum[0] = (u8)(chksum>>8);
pTX->tcpchksum[1] = (u8)(chksum);
TCP_TX_Length=UIP_LLH_LEN+UIP_IPH_LEN + UIP_TCPH_LEN;
//"以太网头部的大小14"+"IP头部的大小20"+"TCP头部的大小20",得到len=54
ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);
memset( pConnect,0,sizeof(struct ConnectTableType) );
//清除该连接信息
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
Print_MyConnectTable();
#endif
}
}
main.c
#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "delay.h"
#include "USART1.h"
#include "IWDG.h"
#include "ENC28J60_Variable.h"
#include "Timer4.h"
#include "enc28j60.h"
#include "ENC28J60_Interface.h"
#include "ARP.h"
#include "ICMP.h"
#include "TCP.h"
//#include "Ethernet_Network_Receive.h"
//PLL配置: PLLCLK = HSE * 6 = 72 MHz,2025年11月8日修改
u8 Main_cnt;
const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
int main(void)
{
u8 Remote_ip[4];
HSE_SetSysClock(RCC_PLLMul_6); // 设置系统时钟为12MHz * 6 = 72MHz
// SystemInit();//在使用IAP时,该语句需要屏蔽
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
USART1_Serial_Interface_Enable(115200);
SysRstSrcRecord();//系统复位记录
delay_ms(1000);
Network_Init();
// Test_ARP();
Remote_ip[0]=192;Remote_ip[1]=168;Remote_ip[2]=1;Remote_ip[3]=190;//远程IP地址,如:"192.168.1.190"
// ENC28J60_Send_PING_Command(Remote_ip);
// //ENC28J60发送PING命令
Create_New_Connect_Parameter(&MyConnectTable[0],Remote_ip,5000);
//创建连接参数,为建立TCP连接做准备
TCP_Client_Connect_To_Server(&MyConnectTable[0]);
////使用指定的连接表,连接到远程服务器
// Create_New_Connect_Parameter(&MyConnectTable[1],Remote_ip,5000);
// //创建连接参数,为建立TCP连接做准备
// TCP_Client_Connect_To_Server(&MyConnectTable[1]);
// ////使用指定的连接表,连接到远程服务器
Print_MyConnectTable();
TCP_Client_Send_Data_To_Server(&MyConnectTable[0],"ABC",3);
Print_MyConnectTable();
TCP_Client_Send_Data_To_Server(&MyConnectTable[0],"EFG",3);
Print_MyConnectTable();
delay_ms(5000);
ENC28J60_Disconnect(&MyConnectTable[0]);
// ENC28J60_Send_RST_data_packet(&MyConnectTable[0]);
while(1)
{
ENC28J60_Receive_Data_From_Ethernet_Network();
if(timer_expired(&ARP_Timer))
{
timer_reset(&ARP_Timer);
ARP_Table_Update_Timer();
//10秒执行一次,如果"远程设备IP地址不为0,且建立时间超过20分钟",则将其IP地址设置为0
}
if(timer_expired(&TCP_Timer))
{
timer_reset(&TCP_Timer);//50*10=500毫秒
MyConnectTable_Update_Timer();
//0.5秒执行一次,如果"远程设备IP地址不为0,且建立时间超过2分钟",则将其IP地址设置为0
}
if( MyNetworkInformation.pConnect->TCPStateFlag == TCP_CLOSED )
{
delay_ms(5000);
TCP_Client_Connect_To_Server(&MyConnectTable[0]);
////使用指定的连接表,连接到远程服务器
}
}
}
测试结果:

TCP.h
#ifndef __TCP_H
#define __TCP_H
#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "ENC28J60_Variable.h"
#define TCP_Debug 1
extern u8 iss[4];//发送的序列号,The iss variable is used for the TCP initial sequence number.
#define TCP_OPT_END 0 //TCP 选项列表的结尾,End of TCP options list
#define TCP_OPT_NOOP 1 //不执行操作",TCP选项,"No-operation" TCP option
#define TCP_OPT_MSS 2 //最大段大小的字节总数为2,Maximum segment size TCP option
#define TCP_OPT_MSS_LEN 4 //TCP选项中MSS选项的长度。 Length of TCP MSS option.
extern void Create_New_Connect_Parameter(struct ConnectTableType *pConnect,u8 *pRemote_IP,u16 tmpRemote_Port);
extern void TCP_Client_Connect_To_Server(struct ConnectTableType *pConnect);
extern void ENC28J60_Send_RST_data_packet(struct ConnectTableType *pConnect);
extern u8 TCP_Client_Send_Data_To_Server(struct ConnectTableType *pConnect,u8 *pData, u16 len);
extern u8 ENC28J60_Disconnect(struct ConnectTableType *pConnect);
extern u8 TCPIP4_Work(void);
#endif
TCP.c
#include "TCP.h"
#include "string.h" //使能strcpy(),strlen(),memset(),NULL
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "stdlib.h" //atoi(),atof(),rand()
#include "delay.h"
#include "ENC28J60_Variable.h"
#include "enc28j60.h"
#include "ENC28J60_Interface.h"
#include "ARP.h"
u8 iss[4];//发送的序列号,The iss variable is used for the TCP initial sequence number.
u8 uip_acc32[4];//uip_acc32[]是以"大端存储方式"工作的
//u8 uip_flags;
//uip_flags的值定义如下:
#define UIP_ACKDATA 1 //接收到应答标志
#define UIP_NEWDATA 2 //收到新数据
#define UIP_REXMIT 4 //告诉应用程序重新传输上次发送的数据。
#define UIP_POLL 8 //应用程序是否有要发送的数据。
#define UIP_CLOSE 16
#define UIP_ABORT 32
//远程主机已中止该连接,因此该连接已消失。或者应用程序表示它想要中止连接。
#define UIP_CONNECTED 64 //已成功建立主动连接。
#define UIP_TIMEDOUT 128 //由于重传次数过多,连接已中断。
void uip_add32(u8 *op32, u16 op16);
void uip_add_Receive_next(struct ConnectTableType *p,u16 n);
void Create_New_Connect_Parameter(struct ConnectTableType *pConnect,u8 *pRemote_IP,u16 tmpRemote_Port);
void Parse_the_TCP_MSS_option(struct ConnectTableType *p);
void ENC28J60_Send_TCP_SYN(void);
void TCP_Client_Connect_To_Server(struct ConnectTableType *pConnect);
u8 Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(struct ConnectTableType *pConnect);
void ENC28J60_Send_ACK_data_packet(struct ConnectTableType *pConnect);
void ENC28J60_Send_RST_data_packet(struct ConnectTableType *pConnect);
void ENC28J60_Send_PSH_data_packet(struct ConnectTableType *pConnect,u8 *pData, u16 len);
u8 TCP_Client_Send_Data_To_Server(struct ConnectTableType *pConnect,u8 *pData, u16 len);
void Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(
struct ConnectTableType *pConnect,struct TCP_IPv4_Packet_Type *pRX);
void ENC28J60_Send_FIN_ACK_data_packet(struct ConnectTableType *pConnect);
u8 ENC28J60_Disconnect(struct ConnectTableType *pConnect);
u8 TCPIP4_Work(void);
//函数功能:
//将op32[]和op16相加,结果保存在uip_acc32[]
//uip_acc32[]是"大端存储方式"的数据
//op32[]是"大端存储方式"的数据
//op16是"小端存储方式"的数据
void uip_add32(u8 *op32, u16 op16)
{
uip_acc32[3] = op32[3] + (op16 & 0xff);//先计算最低8位
uip_acc32[2] = op32[2] + (op16 >> 8);//接着计算次低8位
uip_acc32[1] = op32[1];
uip_acc32[0] = op32[0];
if( uip_acc32[2] < (op16 >> 8) )//"次低8位相加"有进位
{
++uip_acc32[1];
if(uip_acc32[1] == 0)
{//"次高8位和次低8位的进位相加"有进位
++uip_acc32[0];
}
}
if( uip_acc32[3] < (op16 & 0xff) )//"最低8位相加"有进位
{
++uip_acc32[2];
if(uip_acc32[2] == 0)//"次低8位相加"有进位
{
++uip_acc32[1];
if(uip_acc32[1] == 0)
{//"次高8位和次低8位的进位相加"有进位
++uip_acc32[0];
}
}
}
}
//函数功能:修改"下一个期望接收到的序列号"
//p->Receive_next[]"大端存储方式"的数据和n"小端存储方式"的数据,相加,结果保存在p->Receive_next[]
void uip_add_Receive_next(struct ConnectTableType *p,u16 n)
{
/////将p->Receive_next[]和n相加,结果保存在uip_acc32[]////
uip_acc32[3] = p->Receive_next[3] + (n & 0xff);//先计算最低8位
uip_acc32[2] = p->Receive_next[2] + (n >> 8);//接着计算次低8位
uip_acc32[1] = p->Receive_next[1];
uip_acc32[0] = p->Receive_next[0];
if( uip_acc32[2] < (n >> 8) )//"次低8位相加"有进位
{
++uip_acc32[1];
if(uip_acc32[1] == 0)
{//"次高8位和次低8位的进位相加"有进位
++uip_acc32[0];
}
}
if( uip_acc32[3] < (n & 0xff) )//"最低8位相加"有进位
{
++uip_acc32[2];
if(uip_acc32[2] == 0)//"次低8位相加"有进位
{
++uip_acc32[1];
if(uip_acc32[1] == 0)
{//"次高8位和次低8位的进位相加"有进位
++uip_acc32[0];
}
}
}
// uip_add32(p->Receive_next, n);
//将p->Receive_next[]和n相加,结果保存在uip_acc32[]
p->Receive_next[0] = uip_acc32[0];//更新p->Receive_next[0]
p->Receive_next[1] = uip_acc32[1];//更新p->Receive_next[1]
p->Receive_next[2] = uip_acc32[2];//更新p->Receive_next[2]
p->Receive_next[3] = uip_acc32[3];//更新p->Receive_next[3]
}
//函数功能:创建"新连接参数",为"发送SYN数据包,连接到TCP服务器",作准备。
void Create_New_Connect_Parameter(struct ConnectTableType *pConnect,u8 *pRemote_IP,u16 tmpRemote_Port)
{
u8 i;
struct ARPTableType *p1;
u8 j,ch;
struct ConnectTableType *p2;
u16 tmpLocal_port;
u8 loop,cnt,ret;
ret=Check_Remote_MAC_In_ARPTable_Array(pRemote_IP);
//如果"ARP表"里没有pRemote_IP指向的IP地址,则返回0;否则返回1
if(ret==0) loop=2;//若没有远程MAC地址,则发送ARP请求
else//ARP表里已有这个IP地址对应的MAC地址
{
ret=ARP_REPLY_RX_Flag;
loop=0;
}
while(loop)
{
ENC28J60_Send_ARP_REQUEST();
loop--;
cnt=UIP_TTL;
while(cnt)
{
delay_ms(1);
cnt--;
ret=ENC28J60_Receive_Data_From_Ethernet_Network();
if(ret==ARP_REPLY_RX_Flag)
{
cnt=0;
loop=0;
break;
}
}
}
MyNetworkInformation.pConnect=NULL;
if(ret==ARP_REPLY_RX_Flag)
{
///////设置MyNetworkInformation.Remote_IP[]/////////
memcpy(MyNetworkInformation.Remote_IP, pRemote_IP, 4);
//保存"新连接"的远程IP地址
///////设置MyNetworkInformation.Remote_MAC[]/////////
for(i=0; i<ARPTable_SIZE;i++)
{//设置合适的本地端口
p1 = &ARPTable[i];
if( memcmp(p1->Remote_IP,MyNetworkInformation.Remote_IP,4)==0 )
{//发现远程IP地址
memcpy(MyNetworkInformation.Remote_MAC, p1->Remote_MAC, 6);
//保存"新连接"的远程MAC地址
p1->Time=0;//因为准备使用这个远程MAC地址,则将ARP表更新计数器清零。
break;
}
}
///////设置MyNetworkInformation.Remote_Port[]/////////
MyNetworkInformation.Remote_Port[0]=(u8)(tmpRemote_Port>>8);
MyNetworkInformation.Remote_Port[1]=(u8)(tmpRemote_Port);
///////设置MyNetworkInformation.Local_Port[]/////////
tmpLocal_port=Min_Local_Port;
for(i=0; i<My_ConnectTable_SIZE;i++)
{//查找未用的本地端口
for(j=0; j<My_ConnectTable_SIZE;j++)
{ //设置合适的本地端口
p2 = &MyConnectTable[j];
ch=0;
if( p2->Local_Port[0] == (u8)(tmpLocal_port>>8) ) ch++;
if( p2->Local_Port[1] == (u8)(tmpLocal_port) ) ch++;
if(ch==2)
{//发现端口被占用了
tmpLocal_port++;
//保存"新连接"的本地端口
if(tmpLocal_port<Min_Local_Port)
{
tmpLocal_port=Min_Local_Port;
}
break;
}
}
if(j==My_ConnectTable_SIZE) break;
}
MyNetworkInformation.Local_Port[0]=(u8)(tmpLocal_port>>8);
MyNetworkInformation.Local_Port[1]=(u8)(tmpLocal_port);
//保存本地端口
///////设置MyNetworkInformation.pConnect/////////
MyNetworkInformation.pConnect=pConnect;
}
}
//函数功能:解析MSS选项,获取发送方接收缓存有大;
//Parse the TCP MSS option, if present.
void Parse_the_TCP_MSS_option(struct ConnectTableType *p)
{
u16 loop;
u16 tmp16,t;
u8 c,opt;
struct TCP_IPv4_Packet_Type *pRX;
pRX=(struct TCP_IPv4_Packet_Type *)&TCP_RX_buf[0];
if((pRX->tcpoffset & 0xf0) > 0x50)//"TCP头部的长度"大于20,表示有"可选项optdata[]"
{//(0x50>>4)*4=20,即TCP头部的长度为20个字节;
loop=( (pRX->tcpoffset >> 4) - 5 ) << 2;
//计算"可选项optdata[]"有多少个字节
//比如:TCP_RX_buf[55]=0x02,TCP_RX_buf[56]=0x04,TCP_RX_buf[57]=0x05,TCP_RX_buf[58]=0xB4,
//TCP_RX_buf[59]=0x01,TCP_RX_buf[60]=0x01,TCP_RX_buf[61]=0x04,TCP_RX_buf[62]=0x02
for(c = 0; c < loop;)
{
opt = TCP_RX_buf[UIP_OptdataIndex + c];
//UIP_OptdataIndex=54
if(opt == TCP_OPT_END)//读到"TCP选项列表的结尾
{
/* End of options. */
break;
}
else if(opt == TCP_OPT_NOOP)//TCP_OPT_NOOP=1不执行操作
{
++c; /* NOP option. */
}
else if(opt == TCP_OPT_MSS && TCP_RX_buf[UIP_OptdataIndex + 1 + c] == TCP_OPT_MSS_LEN)
{//比如:TCP_RX_buf[55]=0x02,TCP_RX_buf[56]=0x04,TCP_RX_buf[57]=0x05,TCP_RX_buf[58]=0xB4
//"接收到的最大段大小"的字节总数为TCP_OPT_MSS=2:TCP选项中MSS选项的长度为TCP_OPT_MSS_LEN=4
tmp16 = (TCP_RX_buf[UIP_OptdataIndex + 2 + c] << 8) | TCP_RX_buf[UIP_OptdataIndex + 3 + c];
//计算"接收到的最大段大小"
t=tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16;
p->initialMSS[0] = (u8)(t>>8);
p->initialMSS[1] = (u8)(t);
//如果"接收到的最大段大小"大于"本地的TCP最大段大小",则使用"本地的TCP最大段大小"
//如果"接收到的最大段大小"小于或等于"本地的TCP最大段大小",则使用"接收到的最大段大小"
//"本地的TCP最大段大小"为1514-14-40-40,计算机是1460
p->MSS[0]=p->initialMSS[0];p->MSS[1]=p->initialMSS[1];
//记录"远程接收窗口"大小
break;
}
else
{
/* 其他所有选项均设有长度字段,便于我们直接跳过。
All other options have a length field, so that we easily can skip past them. */
if(TCP_RX_buf[UIP_OptdataIndex + 1 + c] == 0)
{
/*若长度字段为零,则表示选项格式异常,系统将不再处理。
If the length field is zero, the options are malformed and we don't process them further. */
break;
}
c += TCP_RX_buf[UIP_OptdataIndex + 1 + c];
}
}
}
}
//函数功能:发送"SYN数据包",为的是连接到TCP服务器
/*
1、发送"SYN数据包":
以太网头部:
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
IP头部:
0x45 0x00 0x00 0x2C 0x00 0x01 0x00 0x00 0x40 0x06 0xF6 0xAB
0xC0 0xA8 0x01 0x11 0xC0 0xA8 0x01 0xBE
TCP头部:
0x03 0xE8 0x13 0x88 0x00 0x00 0xA0 0xB1 0x00 0x00 0x00 0x00
0x60 0x02 0x05 0xB4 0x58 0x31 0x00 0x00 0x02 0x04 0x05 0xB4
分析得到序列号为"0x00 0x00 0xA0 0xB1"
分析得到确认号为"0x00 0x00 0x00 0x00"
2、接收"SYN+ACK数据包"
以太网头部:
0x00 0x08 0xDC 0x11 0x11 0x02 0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x08 0x00
IP头部:
0x45 0x00 0x00 0x2C 0x41 0xE4 0x40 0x00 0x80 0x06 0x34 0xC8
0xC0 0xA8 0x01 0xBE 0xC0 0xA8 0x01 0x11
TCP头部:
0x13 0x88 0x03 0xE8 0x1C 0xBA 0xCE 0xCD 0x00 0x00 0xA0 0xB2
0x60 0x12 0xFA 0xF0 0x75 0x5B 0x00 0x00 0x02 0x04 0x05 0xB4 0x00 0x00
分析得到序列号为"0x1C 0xBA 0xCE 0xCD"
分析得到确认号为"0x00 0x00 0xA0 0xB2"
3、发送"ACK数据包"
以太网头部:
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
IP头部:
0x45 0x00 0x00 0x28 0x00 0x02 0x00 0x00 0x40 0x06 0xF6 0xAE
0xC0 0xA8 0x01 0x11 0xC0 0xA8 0x01 0xBE
TCP头部:
0x03 0xE8 0x13 0x88 0x00 0x00 0xA0 0xB2 0x1C 0xBA 0xCE 0xCE
0x50 0x10 0x05 0xB4 0x82 U 0x00 0x00
分析得到序列号为"0x00 0x00 0xA0 0xB2"
分析得到确认号为"0x1C 0xBA 0xCE 0xCE"
4、发送"PSH+ACK数据包",用户数据内容为ABC
以太网头部:
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
IP头部:
0x45 0x00 0x00 0x2B 0x00 0x03 0x00 0x00 0x40 0x06 0xF6 0xAA
0xC0 0xA8 0x01 0x11 0xC0 0xA8 0x01 0xBE
TCP头部:
0x03 0xE8 0x13 0x88 0x00 0x00 0xA0 0xB2 0x1C 0xBA 0xCE 0xCE
0x50 0x18 0x05 0xB4 0xFE 0x07 0x00 0x00 ABC
分析得到序列号为"0x00 0x00 0xA0 0xB2"
分析得到确认号为"0x1C 0xBA 0xCE 0xCE"
*/
void ENC28J60_Send_TCP_SYN(void)
{
struct TCP_IPv4_Packet_Type *pTX;
u16 chksum;
u16 tmp;
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
/////////以太网头部//////////////
memcpy(pTX->dest_MAC, MyNetworkInformation.Remote_MAC, 6);
//设置"目的MAC地址"
memcpy(pTX->src_MAC, MyNetworkInformation.Local_MAC, 6);
//设置"源MAC地址"为ENC28J60的MAC地址
pTX->type[0] = (u8)(UIP_ETHTYPE_IP4>>8);
pTX->type[1] = (u8)(UIP_ETHTYPE_IP4);
//设置太网帧的类型为0x0800,表示后面跟着的是IPV4数据包;
pTX->vhl = 0x45;//设置"IP版本和头部长度"
pTX->tos = 0;//设置"//服务类型"
tmp = UIP_IPH_LEN + UIP_TCPH_LEN + TCP_OPT_MSS_LEN;
//"IP头部的大小20"+"TCP头部的大小20"+"可选项大小4",得到len=44
#if UIP_CONF_IPV6
//For IPv6, the IP length field does not include the IPv6 IP header length.
pTX->len[0] = ((tmp - UIP_IPH_LEN) >> 8);
pTX->len[1] = ((tmp - UIP_IPH_LEN) & 0xff);
#else
pTX->len[0] = (tmp >> 8);
pTX->len[1] = (tmp & 0xff);
//设置IP头部中的"IP头部,TCP头部和可选项"的字节总数
#endif
++My_IPID;
pTX->ipid[0] = My_IPID >> 8;
pTX->ipid[1] = My_IPID & 0xff;//设置"标识"
pTX->ipoffset[0] =0;
pTX->ipoffset[1] = 0;
//设置"标志字段和片偏移字段",0x0000表示不分包
pTX->ttl = UIP_TTL;//修改IP头部中的"生存时间值"
pTX->protocol = UIP_PROTO_TCP;//修改"IP头部中的协议"为6
memcpy(pTX->Send_IP,MyNetworkInformation.Local_IP,4);
//设置"发送方ip地址"
memcpy(pTX->Receive_IP,MyNetworkInformation.Remote_IP,4);
//设置"接收方IP地址"
pTX->ipchksum[0] = 0;pTX->ipchksum[1] = 0;
chksum=~(IPv4_checksum(&TCP_TX_buf[UIP_LLH_LEN]));
pTX->ipchksum[0] = (u8)(chksum>>8);
pTX->ipchksum[1] = (u8)(chksum);
//设置"IPV4检验和"
memcpy(pTX->Send_Port, MyNetworkInformation.Local_Port, 4);
//设置"发送方端口"
memcpy(pTX->Receive_Port, MyNetworkInformation.Remote_Port, 4);
//设置"接收方端口"
//修改"TCP头部中的序列号"
tmp=rand();
tmp++;
pTX->seqno[0] = (u8)(tmp>>24);//记忆ENC28J60发送的序列号
pTX->seqno[1] = (u8)(tmp>>16);
pTX->seqno[2] = (u8)(tmp>>8);
pTX->seqno[3] = (u8)(tmp&0xFF);
//修改"TCP头部中的确认号"
pTX->ackno[0] = 0;
pTX->ackno[1] = 0;
pTX->ackno[2] = 0;
pTX->ackno[3] = 0;
pTX->tcpoffset = ( (UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4 ) << 4;
//计算"TCP头部的长度",TCP数据包有optdata[],pTX->tcpoffset=0x60
pTX->flags = TCP_SYN;//TCP客户端主动发送TCP_SYN请求时,TCP头部的标志位为"TCP_SYN"
//设置我的接收窗口大小
pTX->wnd[0] = ((UIP_TCP_MSS) >> 8);//修改"TCP头部中的窗口大小"
pTX->wnd[1] = ((UIP_TCP_MSS) & 0xff);//修改"TCP头部中的窗口大小"
pTX->urgp[0] = 0;
pTX->urgp[1] = 0;//修改TCP头部中的"紧急指针"
pTX->optdata[0] = TCP_OPT_MSS;//最大段字节总数为2
pTX->optdata[1] = TCP_OPT_MSS_LEN;//TCP选项中MSS选项的长度
pTX->optdata[2] = (UIP_TCP_MSS) / 256;//最大段高8数值,TCP最大段大小为(1500-14-40)
pTX->optdata[3] = (UIP_TCP_MSS) & 255;//最大段低8数值,TCP最大段大小为(1500-14-40)
pTX->tcpchksum[0] = 0;pTX->tcpchksum[1] = 0;
chksum=~(TCPv4_header_checksume());
//计算TCP头部的校验和
pTX->tcpchksum[0] = (u8)(chksum>>8);
pTX->tcpchksum[1] = (u8)(chksum);
TCP_TX_Length=UIP_LLH_LEN+UIP_IPH_LEN + UIP_TCPH_LEN + TCP_OPT_MSS_LEN;
//"以太网头部的大小14"+"IP头部的大小20"+"TCP头部的大小20"+"可选项大小4",得到len=58
ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);
}
//函数功能:使用新连接参数,连接到远程服务器
void TCP_Client_Connect_To_Server(struct ConnectTableType *pConnect)
{
u8 loop,cnt,ret;
u16 tmpLocal_port;
u8 i,j,ch;
struct ConnectTableType *p2;
tmpLocal_port=HEX16(MyNetworkInformation.Local_Port);
loop=1;
while(loop)
{
ENC28J60_Send_TCP_SYN();
pConnect->TCPStateFlag=TCP_SYN_SENT;
cnt=UIP_TTL;
while(cnt)
{
delay_ms(1);
cnt--;
ret=ENC28J60_Receive_Data_From_Ethernet_Network();
if(ret==TCP_SYN_ACK_RX_Flag)
{
cnt=0;
loop=0;
}
}
if(ret!=TCP_SYN_ACK_RX_Flag)//原端口可能被占用
{
tmpLocal_port++;//本地端口加1,因为原端口被占用
for(i=0; i<My_ConnectTable_SIZE;i++)
{//查找未用的本地端口
for(j=0; j<My_ConnectTable_SIZE;j++)
{ //设置合适的本地端口
p2 = &MyConnectTable[j];
ch=0;
if( p2->Local_Port[0] == (u8)(tmpLocal_port>>8) ) ch++;
if( p2->Local_Port[1] == (u8)(tmpLocal_port) ) ch++;
if(ch==2)
{//发现端口被占用了
tmpLocal_port++;
//保存"新连接"的本地端口
if(tmpLocal_port<Min_Local_Port)
{
tmpLocal_port=Min_Local_Port;
}
break;
}
}
if(j==My_ConnectTable_SIZE) break;
}
MyNetworkInformation.Local_Port[0]=(u8)(tmpLocal_port>>8);
MyNetworkInformation.Local_Port[1]=(u8)(tmpLocal_port);
//保存本地端口
printf("\r\nloop\r\n");
}
}
if(ret==TCP_SYN_ACK_RX_Flag)
{
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_MyConnectTable();
#endif
ENC28J60_Send_ACK_data_packet(pConnect);//发送ACK应答
pConnect->TCPStateFlag=TCP_ESTABLISHED;
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
}
}
//函数功能:将"pConnect指向的连接参数"装载到MyNetworkInformation中
//主要是用来装载"连接参数",如:远程IP,远程MAC,远程端口,本地端口和连接表指针
//注意:在TCP建立连接后,才可以调用该函数
u8 Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(struct ConnectTableType *pConnect)
{
u8 i,ret;
struct ARPTableType *p1;
ret=0;
for(i=0; i<ARPTable_SIZE;i++)
{//设置合适的本地端口
p1 = &ARPTable[i];
if( memcmp(p1->Remote_IP,pConnect->Remote_IP,4)==0 )
{//发现远程IP地址
memcpy(MyNetworkInformation.Remote_MAC, p1->Remote_MAC, 6);
//保存"新连接"的远程MAC地址
memcpy(MyNetworkInformation.Remote_IP, pConnect->Remote_IP, 4);
memcpy(MyNetworkInformation.Remote_Port, pConnect->Remote_Port,2);
memcpy(MyNetworkInformation.Local_Port, pConnect->Local_Port,2);
MyNetworkInformation.pConnect=pConnect;
pConnect->Time=0;//因为准备使用这个连接,则将连接表更新计数器清零。
p1->Time=0;//因为准备使用这个远程MAC地址,则将ARP表更新计数器清零。
ret=1;//ARPTable[]有所给的IP地址
break;
}
}
return(ret);
}
//装载发送的序号和确认号,并计算用户数据长度
void Load_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(
struct ConnectTableType *pConnect,struct TCP_IPv4_Packet_Type *pTX)
{
int t;
//使用接收到的确认号作为"TCP头部中的序列号"
pTX->seqno[0] = pConnect->Send_next[0];
pTX->seqno[1] = pConnect->Send_next[1];
pTX->seqno[2] = pConnect->Send_next[2];
pTX->seqno[3] = pConnect->Send_next[3];
//修改"TCP头部中的确认号"
uip_add_Receive_next(pConnect,pConnect->len);
//使用"接收到的序列号+用户数据长度"作为确认号
pTX->ackno[0] = pConnect->Receive_next[0];
pTX->ackno[1] = pConnect->Receive_next[1];
pTX->ackno[2] = pConnect->Receive_next[2];
pTX->ackno[3] = pConnect->Receive_next[3];
//计算"将要发送的用户数据长度"
t=pTX->len[0];t=(u16)(t<<8);
t=t|pTX->len[1];
t=t-UIP_TCPH_LEN - UIP_IPH_LEN;
if(t<=0)t=1;
pConnect->len = t;
}
//函数功能:发送"ACK应答数据包"
/*
发送"SYN"
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
0x45 0x00 0x00 0x2C 0x00 0x01 0x00 0x00 0x40 0x06 0xF6 0xAB
发送方的IP:0xC0 0xA8 0x01 0x11
接收方的IP:0xC0 0xA8 0x01 0xBE
发送方端口和接收方端口:0x03 0xE8 0x13 0x88
发送方的序列号:0x00 0x00 0xA0 0xB1
发送方期望收到的序列号:0x00 0x00 0x00 0x00
0x60 0x02 0x05 0xB4 0x56 0x31 0x00 0x00
0x02 0x04 0x05 0xB4
接收"SYN+ACK"
0x00 0x08 0xDC 0x11 0x11 0x02 0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x08 0x00
0x45 0x00 0x00 0x2C 0x93 0x6B 0x40 0x00 0x80 0x06 0xE3 0x40
发送方的IP:0xC0 0xA8 0x01 0xBE
接收方的IP:0xC0 0xA8 0x01 0x11
发送方端口和接收方端口:0x13 0x88 0x03 0xE8
发送方的序列号:0x83 0xAF 0x3E 0xDF
发送方期望收到的序列号:0x00 0x00 0xA0 0xB2
0x60 0x12 0xFA 0xF0 0x9E 0x54 0x00 0x00
0x02 0x04 0x05 0xB4
0x00 0x00
发送"ACK"
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
0x45 0x00 0x00 0x28 0x00 0x02 0x00 0x00 0x40 0x06 0xF6 0xAE
发送方的IP:0xC0 0xA8 0x01 0x11
接收方的IP:0xC0 0xA8 0x01 0xBE
发送方端口和接收方端口:0x03 0xE8 0x13 0x88
发送方的序列号:0x00 0x00 0xA0 0xB2
发送方期望收到的序列号:0x83 0xAF 0x3E 0xE0
0x50 0x10 0x05 0xB4 0xAB 0x4E 0x00 0x00
*/
void ENC28J60_Send_ACK_data_packet(struct ConnectTableType *pConnect)
{
u8 ret;
struct TCP_IPv4_Packet_Type *pTX;
u16 tmp;
u16 chksum;
int t;
ret=Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(pConnect);
//将"pConnect指向的连接参数"装载到MyNetworkInformation中
//主要是用来装载"连接参数",如:远程IP,远程MAC,远程端口,本地端口和连接表指针
if(ret)
{
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
memcpy(pTX->dest_MAC, MyNetworkInformation.Remote_MAC, 6);
//添加远程MAC地址
memcpy(pTX->src_MAC, MyNetworkInformation.Local_MAC, 6);
//添加本地MAC地址
pTX->type[0] = (u8)(UIP_ETHTYPE_IP4>>8);
pTX->type[1] = (u8)(UIP_ETHTYPE_IP4);
//设置太网帧的类型为0x0800,表示后面跟着的是IPV4数据包;
pTX->vhl = 0x45;//设置"IP版本和头部长度"
pTX->tos = 0;//设置"//服务类型"
tmp = UIP_IPH_LEN + UIP_TCPH_LEN;
//"IP头部的大小20"+"TCP头部的大小20"",得到len=40
#if UIP_CONF_IPV6
//For IPv6, the IP length field does not include the IPv6 IP header length.
pTX->len[0] = ((tmp - UIP_IPH_LEN) >> 8);
pTX->len[1] = ((tmp - UIP_IPH_LEN) & 0xff);
#else
pTX->len[0] = (tmp >> 8);
pTX->len[1] = (tmp & 0xff);
//设置IP头部中的"IP头部,TCP头部和可选项"的字节总数
#endif
++My_IPID;
pTX->ipid[0] = My_IPID >> 8;
pTX->ipid[1] = My_IPID & 0xff;//设置"标识"
pTX->ipoffset[0] =0;
pTX->ipoffset[1] = 0;
//设置"标志字段和片偏移字段",0x0000表示不分包
pTX->ttl = UIP_TTL;//修改IP头部中的"生存时间值"
pTX->protocol = UIP_PROTO_TCP;//修改"IP头部中的协议"为6
memcpy(pTX->Send_IP,MyNetworkInformation.Local_IP,4);
//设置"发送方ip地址"
memcpy(pTX->Receive_IP,MyNetworkInformation.Remote_IP,4);
//设置"接收方IP地址"
pTX->ipchksum[0] = 0;pTX->ipchksum[1] = 0;
chksum=~(IPv4_checksum(&TCP_TX_buf[UIP_LLH_LEN]));
pTX->ipchksum[0] = (u8)(chksum>>8);
pTX->ipchksum[1] = (u8)(chksum);
//设置"IPV4检验和"
memcpy(pTX->Send_Port, MyNetworkInformation.Local_Port, 4);
//设置"发送方端口"
memcpy(pTX->Receive_Port, MyNetworkInformation.Remote_Port, 4);
//设置"接收方端口"
//使用接收到的确认号作为"TCP头部中的序列号"
pTX->seqno[0] = pConnect->Send_next[0];
pTX->seqno[1] = pConnect->Send_next[1];
pTX->seqno[2] = pConnect->Send_next[2];
pTX->seqno[3] = pConnect->Send_next[3];
//修改"TCP头部中的确认号"
uip_add_Receive_next(pConnect,pConnect->len);
//应答远程设备,需要将"接收到的序列号+用户数据长度"作为确认号
pTX->ackno[0] = pConnect->Receive_next[0];
pTX->ackno[1] = pConnect->Receive_next[1];
pTX->ackno[2] = pConnect->Receive_next[2];
pTX->ackno[3] = pConnect->Receive_next[3];
t=pTX->len[0];t=(u16)(t<<8);
t=t|pTX->len[1];
t=t-UIP_TCPH_LEN - UIP_IPH_LEN;
if(t<=0)t=1;
pConnect->len = t;
pTX->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
//TCP数据包无optdata[],BUF->tcpoffset=0x50
//UIP_TCPH_LEN=20,设置"TCP头部的长度"为5*4=20
pTX->flags = TCP_ACK;
//TCP头部的标志位为"ACK"
//设置我的接收窗口大小
if(pConnect->TCPStateFlag & TCP_STOPPED)
{//设置零窗口机制,使远程主机停止发送数据。
//we advertise a zero window so that the remote host will stop sending data.
pTX->wnd[0] = 0;//修改"TCP头部中的窗口大小"
pTX->wnd[1] = 0;//修改"TCP头部中的窗口大小"
}
else
{
pTX->wnd[0] = ((UIP_TCP_MSS) >> 8);//修改"TCP头部中的窗口大小"
pTX->wnd[1] = ((UIP_TCP_MSS) & 0xff);//修改"TCP头部中的窗口大小"
}
pTX->urgp[0] = 0;
pTX->urgp[1] = 0;//修改TCP头部中的"紧急指针"
pTX->tcpchksum[0] = 0;pTX->tcpchksum[1] = 0;
chksum=~(TCPv4_header_checksume());
//计算TCP头部的校验和
pTX->tcpchksum[0] = (u8)(chksum>>8);
pTX->tcpchksum[1] = (u8)(chksum);
TCP_TX_Length=UIP_LLH_LEN+UIP_IPH_LEN + UIP_TCPH_LEN;
//"以太网头部的大小14"+"IP头部的大小20"+"TCP头部的大小20",得到len=54
ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);
}
}
//函数功能:发送"RST"数据包,用来断开与TCP服务器的连接
/*
发送"RST数据包"后,发送方会立即关闭连接,而不需要等待对方的确认。接收方收到RST包后,也会立即关闭连接。
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
0x45 0x00 0x00 0x28 0x00 0x03 0x00 0x00 0x40 0x06 0xF6 0xAD
0xC0 0xA8 0x01 0x11
0xC0 0xA8 0x01 0xBE
0x06 0x10 0x13 0x00 0x00 0xA0 0xB2 0x06 K8iP 0x04 0x00 0x00 0xBE 6 0x00 0x00
*/
void ENC28J60_Send_RST_data_packet(struct ConnectTableType *pConnect)
{
u8 ret;
struct TCP_IPv4_Packet_Type *pTX;
u16 tmp,chksum;
int t;
ret=Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(pConnect);
//将"pConnect指向的连接参数"装载到MyNetworkInformation中
//主要是用来装载"连接参数",如:远程IP,远程MAC,远程端口,本地端口和连接表指针
if(ret==1)
{
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
memcpy(pTX->dest_MAC, MyNetworkInformation.Remote_MAC, 6);
//添加远程MAC地址
memcpy(pTX->src_MAC,MyNetworkInformation.Local_MAC, 6);
//添加本地MAC地址
pTX->type[0] = (u8)(UIP_ETHTYPE_IP4>>8);
pTX->type[1] = (u8)(UIP_ETHTYPE_IP4);
//设置太网帧的类型为0x0800,表示后面跟着的是IPV4数据包;
pTX->vhl = 0x45;//设置"IP版本和头部长度"
pTX->tos = 0;//设置"服务类型"
tmp = UIP_IPH_LEN + UIP_TCPH_LEN;
//"IP头部的大小20"+"TCP头部的大小20"",得到len=40
#if UIP_CONF_IPV6
//For IPv6, the IP length field does not include the IPv6 IP header length.
pTX->len[0] = ((tmp - UIP_IPH_LEN) >> 8);
pTX->len[1] = ((tmp - UIP_IPH_LEN) & 0xff);
#else
pTX->len[0] = (tmp >> 8);
pTX->len[1] = (tmp & 0xff);
//设置IP头部中的"IP头部,TCP头部和可选项"的字节总数
#endif
++My_IPID;
pTX->ipid[0] = My_IPID >> 8;
pTX->ipid[1] = My_IPID & 0xff;//设置"标识"
pTX->ipoffset[0] =0;
pTX->ipoffset[1] = 0;
//设置"标志字段和片偏移字段",0x0000表示不分包
pTX->ttl = UIP_TTL;//修改IP头部中的"生存时间值"
pTX->protocol = UIP_PROTO_TCP;//修改"IP头部中的协议"为6
memcpy(pTX->Send_IP,MyNetworkInformation.Local_IP,4);
//设置"发送方ip地址"
memcpy(pTX->Receive_IP,MyNetworkInformation.Remote_IP,4);
//设置"接收方IP地址"
pTX->ipchksum[0] = 0;pTX->ipchksum[1] = 0;
chksum=~(IPv4_checksum(&TCP_TX_buf[UIP_LLH_LEN]));
pTX->ipchksum[0] = (u8)(chksum>>8);
pTX->ipchksum[1] = (u8)(chksum);
//设置"IPV4检验和"
memcpy(pTX->Send_Port, MyNetworkInformation.Local_Port, 4);
//设置"发送方端口"
memcpy(pTX->Receive_Port, MyNetworkInformation.Remote_Port, 4);
//设置"接收方端口
//使用接收到的确认号作为"TCP头部中的序列号"
pTX->seqno[0] = pConnect->Send_next[0];
pTX->seqno[1] = pConnect->Send_next[1];
pTX->seqno[2] = pConnect->Send_next[2];
pTX->seqno[3] = pConnect->Send_next[3];
//修改"TCP头部中的确认号"
//因为这里不是应答远程设备,所以不用将"接收到的序列号+用户数据长度"作为确认号
pTX->ackno[0] = pConnect->Receive_next[0];
pTX->ackno[1] = pConnect->Receive_next[1];
pTX->ackno[2] = pConnect->Receive_next[2];
pTX->ackno[3] = pConnect->Receive_next[3];
t=pTX->len[0];t=(u16)(t<<8);
t=t|pTX->len[1];
t=t-UIP_TCPH_LEN - UIP_IPH_LEN;
if(t<=0)t=1;
pConnect->len = t;
pTX->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
//TCP数据包无optdata[],BUF->tcpoffset=0x50
//UIP_TCPH_LEN=20,设置"TCP头部的长度"为5*4=20
pTX->flags = TCP_RST;
//TCP头部的标志位为"RST"
//设置"零窗口机制",使远程主机停止发送数据。
pTX->wnd[0] = 0;//修改"TCP头部中的窗口大小"
pTX->wnd[1] = 0;//修改"TCP头部中的窗口大小"
pTX->urgp[0] = 0;
pTX->urgp[1] = 0;//修改TCP头部中的"紧急指针"
pTX->tcpchksum[0] = 0;pTX->tcpchksum[1] = 0;
chksum=~(TCPv4_header_checksume());
//计算TCP头部的校验和
pTX->tcpchksum[0] = (u8)(chksum>>8);
pTX->tcpchksum[1] = (u8)(chksum);
TCP_TX_Length=UIP_LLH_LEN+UIP_IPH_LEN + UIP_TCPH_LEN;
//"以太网头部的大小14"+"IP头部的大小20"+"TCP头部的大小20",得到len=54
ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);
memset( pConnect,0,sizeof(struct ConnectTableType) );
//清除该连接信息
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
Print_MyConnectTable();
#endif
}
}
//函数功能:发送PSH数据包,用户数据是以pData为首地址的前len个字节
/*
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
0x45 0x00 0x00 0x2B 0x00 0x05 0x00 0x00 0x40 0x06 0xF6 0xA8
0xC0 0xA8 0x01 0x11
0xC0 0xA8 0x01 0xBE
0x09 0xC4 0x13 0x88
0x00 0x00 0xA0 0xB2 0x14 0xE2 0x2F 0xDE
0x50 0x18 0x05 0xB4 0x9E 0xF4 0x00 0x00
ABC
*/
void ENC28J60_Send_PSH_data_packet(struct ConnectTableType *pConnect,u8 *pData, u16 len)
{
u8 ret;
struct TCP_IPv4_Packet_Type *pTX;
u16 tmp,chksum;
int t;
ret=Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(pConnect);
//将"pConnect指向的连接参数"装载到MyNetworkInformation中
//主要是用来装载"连接参数",如:远程IP,远程MAC,远程端口,本地端口和连接表指针
if(ret==1)
{
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
memcpy(pTX->dest_MAC, MyNetworkInformation.Remote_MAC, 6);
//添加远程MAC地址
memcpy(pTX->src_MAC,MyNetworkInformation.Local_MAC, 6);
//添加本地MAC地址
pTX->type[0] = (u8)(UIP_ETHTYPE_IP4>>8);
pTX->type[1] = (u8)(UIP_ETHTYPE_IP4);
//设置太网帧的类型为0x0800,表示后面跟着的是IPV4数据包;
pTX->vhl = 0x45;//设置"IP版本和头部长度"
pTX->tos = 0;//设置"服务类型"
tmp = UIP_IPH_LEN + UIP_TCPH_LEN + len;
//"IP头部的大小20"+"TCP头部的大小20"+"用户数据长度len" ,得到tmp
#if UIP_CONF_IPV6
//For IPv6, the IP length field does not include the IPv6 IP header length.
pTX->len[0] = ((tmp - UIP_IPH_LEN) >> 8);
pTX->len[1] = ((tmp - UIP_IPH_LEN) & 0xff);
#else
pTX->len[0] = (tmp >> 8);
pTX->len[1] = (tmp & 0xff);
//设置IP头部中的"IP头部,TCP头部和可选项"的字节总数
#endif
++My_IPID;
pTX->ipid[0] = My_IPID >> 8;
pTX->ipid[1] = My_IPID & 0xff;//设置"标识"
pTX->ipoffset[0] =0;
pTX->ipoffset[1] = 0;
//设置"标志字段和片偏移字段",0x0000表示不分包
pTX->ttl = UIP_TTL;//修改IP头部中的"生存时间值"
pTX->protocol = UIP_PROTO_TCP;//修改"IP头部中的协议"为6
memcpy(pTX->Send_IP,MyNetworkInformation.Local_IP,4);
//设置"发送方ip地址"
memcpy(pTX->Receive_IP,MyNetworkInformation.Remote_IP,4);
//设置"接收方IP地址"
pTX->ipchksum[0] = 0;pTX->ipchksum[1] = 0;
chksum=~(IPv4_checksum(&TCP_TX_buf[UIP_LLH_LEN]));
pTX->ipchksum[0] = (u8)(chksum>>8);
pTX->ipchksum[1] = (u8)(chksum);
//设置"IPV4检验和"
memcpy(pTX->Send_Port, MyNetworkInformation.Local_Port, 4);
//设置"发送方端口"
memcpy(pTX->Receive_Port, MyNetworkInformation.Remote_Port, 4);
//设置"接收方端口
//使用接收到的确认号作为"TCP头部中的序列号"
pTX->seqno[0] = pConnect->Send_next[0];
pTX->seqno[1] = pConnect->Send_next[1];
pTX->seqno[2] = pConnect->Send_next[2];
pTX->seqno[3] = pConnect->Send_next[3];
//修改"TCP头部中的确认号"
//因为这里不是应答远程设备,所以不用将"接收到的序列号+用户数据长度"作为确认号
pTX->ackno[0] = pConnect->Receive_next[0];
pTX->ackno[1] = pConnect->Receive_next[1];
pTX->ackno[2] = pConnect->Receive_next[2];
pTX->ackno[3] = pConnect->Receive_next[3];
t=pTX->len[0];t=(u16)(t<<8);
t=t|pTX->len[1];
t=t-UIP_TCPH_LEN - UIP_IPH_LEN;
if(t<=0)t=1;
pConnect->len = t;
pTX->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
//TCP数据包无optdata[],BUF->tcpoffset=0x50
//UIP_TCPH_LEN=20,设置"TCP头部的长度"为5*4=20
pTX->flags = (TCP_PSH | TCP_ACK);
//TCP头部的标志位为"PSH+ACK"
pTX->wnd[0] = ((UIP_TCP_MSS) >> 8);//修改"TCP头部中的窗口大小"
pTX->wnd[1] = ((UIP_TCP_MSS) & 0xff);//修改"TCP头部中的窗口大小"
pTX->urgp[0] = 0;
pTX->urgp[1] = 0;//修改TCP头部中的"紧急指针"
//添加用户数据
tmp = UIP_LLH_LEN + UIP_IPH_LEN + UIP_TCPH_LEN;
TCP_TX_Length = UIP_LLH_LEN + UIP_IPH_LEN + UIP_TCPH_LEN + len;
//"以太网头部的大小14"+"IP头部的大小20"+"TCP头部的大小20",得到TCP_TX_Length
for(;tmp<TCP_TX_Length;tmp++)
{
TCP_TX_buf[tmp]=*pData;
pData++;
}
//计算TCP头部校验和
pTX->tcpchksum[0] = 0;pTX->tcpchksum[1] = 0;
chksum=~(TCPv4_header_checksume());
//计算TCP头部的校验和
pTX->tcpchksum[0] = (u8)(chksum>>8);
pTX->tcpchksum[1] = (u8)(chksum);
ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);
}
}
/*
发送用户数据
0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x00
0x45 0x00 0x00 0x2B 0x00 0x03 0x00 0x00 0x40 0x06 0xF6 0xAA
0xC0 0xA8 0x01 0x11
0xC0 0xA8 0x01 0xBE
0x03 0xE8 0x13 0x88
0x00 0x00 0xA0 0xB2 0xB1 0xD2 0x47 0xC9
0x50 0x18 0x05 0xB4 0xCF 0xF4 0x00 0x00 ABC
接收来自计算机的应答
0x00 0x08 0xDC 0x11 0x11 0x02 0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x08 0x00
0x45 0x00 0x00 0x28 0xCE 0xA8 0x40 0x00 0x80 0x06 0xA8 0x07
0xC0 0xA8 0x01 0xBE
0xC0 0xA8 0x01 0x11
0x13 0x88 0x03 0xE8
0xB1 0xD2 0x67 0xC9 0x00 0x00 0xA0 0xB5
0x50 0x10 0xFA 0xED 0x5F 0x05 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
*/
//函数功能:将首地址为pData的前len个字节发送给指定连接的设备
u8 TCP_Client_Send_Data_To_Server(struct ConnectTableType *pConnect,u8 *pData, u16 len)
{
u8 loop,cnt,tmp,ret;
loop=2;ret=0;
while(loop)
{
ENC28J60_Send_PSH_data_packet(pConnect,pData,len);
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
loop--;
cnt=UIP_TTL;
while(cnt)
{
delay_ms(1);
cnt--;
tmp=ENC28J60_Receive_Data_From_Ethernet_Network();
if(tmp==TCP_ACK_RX_Flag)
{
ret=1;
cnt=0;
loop=0;
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_MyConnectTable();
#endif
}
}
}
return(ret);
}
//更新序号和确认号,并计算接收到的用户数据长度
void Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(
struct ConnectTableType *pConnect,struct TCP_IPv4_Packet_Type *pRX)
{
int t;
u8 iplen,tcplen;
u16 len;
pConnect->Receive_next[0] = pRX->seqno[0];//保存"收到的序列号"
pConnect->Receive_next[1] = pRX->seqno[1];
pConnect->Receive_next[2] = pRX->seqno[2];
pConnect->Receive_next[3] = pRX->seqno[3];
pConnect->Send_next[0]=pRX->ackno[0];
pConnect->Send_next[1]=pRX->ackno[1];
pConnect->Send_next[2]=pRX->ackno[2];
pConnect->Send_next[3]=pRX->ackno[3];
//在后面发送ACK数据包时,用作序列号
//IPV4头部长度(Header Length):低4位为0x05,表示IPv4头部长度,单位为32位字
//即IPV4头部为:5 * 4 = 20 字节。
iplen=pRX->vhl;
iplen=(u8)(iplen&0x0f)*4;
//表示TCP头部的长度,即(0x70>>4)*4=28;
tcplen=pRX->tcpoffset;
tcplen=(u8)(tcplen>>4)*4;
t=pRX->len[0];t=(u16)(t<<8);
t=t|pRX->len[1];
//t为"IP头部长度+TCP头部长度+用户数据长度"
t=t-iplen-tcplen;//用户数据长度
if(t<=0)//无用户数据
{
t=1;
pConnect->pUserData=NULL;
}
else//有用户数据
{
len=iplen+tcplen+UIP_LLH_LEN;
//len为"以太网头部长度+IP头部长度+TCP头部长度"
pConnect->pUserData=&TCP_RX_buf[len];
//保存用户数据的首地址
Print_Receive_Package(pConnect->pUserData,t);
}
pConnect->len = t;//计算用户数据长度
}
//函数功能:发送"FIN+ACK数据包"
void ENC28J60_Send_FIN_ACK_data_packet(struct ConnectTableType *pConnect)
{
u8 ret;
struct TCP_IPv4_Packet_Type *pTX;
u16 tmp,chksum;
int t;
ret=Load_ConnectParameter_To_MyNetworkInformation_After_TCP_Established(pConnect);
//将"pConnect指向的连接参数"装载到MyNetworkInformation中
//主要是用来装载"连接参数",如:远程IP,远程MAC,远程端口,本地端口和连接表指针
if(ret==1)
{
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
memcpy(pTX->dest_MAC, MyNetworkInformation.Remote_MAC, 6);
//添加远程MAC地址
memcpy(pTX->src_MAC,MyNetworkInformation.Local_MAC, 6);
//添加本地MAC地址
pTX->type[0] = (u8)(UIP_ETHTYPE_IP4>>8);
pTX->type[1] = (u8)(UIP_ETHTYPE_IP4);
//设置太网帧的类型为0x0800,表示后面跟着的是IPV4数据包;
pTX->vhl = 0x45;//设置"IP版本和头部长度"
pTX->tos = 0;//设置"服务类型"
tmp = UIP_IPH_LEN + UIP_TCPH_LEN;
//"IP头部的大小20"+"TCP头部的大小20" ,得到tmp
#if UIP_CONF_IPV6
//For IPv6, the IP length field does not include the IPv6 IP header length.
pTX->len[0] = ((tmp - UIP_IPH_LEN) >> 8);
pTX->len[1] = ((tmp - UIP_IPH_LEN) & 0xff);
#else
pTX->len[0] = (tmp >> 8);
pTX->len[1] = (tmp & 0xff);
//设置IP头部中的"IP头部,TCP头部和可选项"的字节总数
#endif
++My_IPID;
pTX->ipid[0] = My_IPID >> 8;
pTX->ipid[1] = My_IPID & 0xff;//设置"标识"
pTX->ipoffset[0] =0;
pTX->ipoffset[1] = 0;
//设置"标志字段和片偏移字段",0x0000表示不分包
pTX->ttl = UIP_TTL;//修改IP头部中的"生存时间值"
pTX->protocol = UIP_PROTO_TCP;//修改"IP头部中的协议"为6
memcpy(pTX->Send_IP,MyNetworkInformation.Local_IP,4);
//设置"发送方ip地址"
memcpy(pTX->Receive_IP,MyNetworkInformation.Remote_IP,4);
//设置"接收方IP地址"
pTX->ipchksum[0] = 0;pTX->ipchksum[1] = 0;
chksum=~(IPv4_checksum(&TCP_TX_buf[UIP_LLH_LEN]));
pTX->ipchksum[0] = (u8)(chksum>>8);
pTX->ipchksum[1] = (u8)(chksum);
//设置"IPV4检验和"
memcpy(pTX->Send_Port, MyNetworkInformation.Local_Port, 4);
//设置"发送方端口"
memcpy(pTX->Receive_Port, MyNetworkInformation.Remote_Port, 4);
//设置"接收方端口
//使用接收到的确认号作为"TCP头部中的序列号"
pTX->seqno[0] = pConnect->Send_next[0];
pTX->seqno[1] = pConnect->Send_next[1];
pTX->seqno[2] = pConnect->Send_next[2];
pTX->seqno[3] = pConnect->Send_next[3];
//修改"TCP头部中的确认号"
//因为这里不是应答远程设备,所以不用将"接收到的序列号+用户数据长度"作为确认号
pTX->ackno[0] = pConnect->Receive_next[0];
pTX->ackno[1] = pConnect->Receive_next[1];
pTX->ackno[2] = pConnect->Receive_next[2];
pTX->ackno[3] = pConnect->Receive_next[3];
t=pTX->len[0];t=(u16)(t<<8);
t=t|pTX->len[1];
t=t-UIP_TCPH_LEN - UIP_IPH_LEN;
if(t<=0)t=1;
pConnect->len = t;
pTX->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
//TCP数据包无optdata[],BUF->tcpoffset=0x50
//UIP_TCPH_LEN=20,设置"TCP头部的长度"为5*4=20
pTX->flags = (TCP_FIN | TCP_ACK);
//TCP头部的标志位为"FIN+ACK"
pTX->wnd[0] = ((UIP_TCP_MSS) >> 8);//修改"TCP头部中的窗口大小"
pTX->wnd[1] = ((UIP_TCP_MSS) & 0xff);//修改"TCP头部中的窗口大小"
pTX->urgp[0] = 0;
pTX->urgp[1] = 0;//修改TCP头部中的"紧急指针"
//添加用户数据
TCP_TX_Length = UIP_LLH_LEN + UIP_IPH_LEN + UIP_TCPH_LEN;
//"以太网头部的大小14"+"IP头部的大小20"+"TCP头部的大小20",得到TCP_TX_Length
//计算TCP头部校验和
pTX->tcpchksum[0] = 0;pTX->tcpchksum[1] = 0;
chksum=~(TCPv4_header_checksume());
//计算TCP头部的校验和
pTX->tcpchksum[0] = (u8)(chksum>>8);
pTX->tcpchksum[1] = (u8)(chksum);
ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);
#if TCP_Debug == 1
Print_MyConnectTable();
#endif
}
}
//函数功能:告诉对方断开连接
u8 ENC28J60_Disconnect(struct ConnectTableType *pConnect)
{
u8 loop,tmp,ret;
u16 cnt;
pConnect->TCPStateFlag=TCP_CLOSING;
loop=2;ret=0;
while(loop)
{
ENC28J60_Send_FIN_ACK_data_packet(pConnect);//断开连接步骤1
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
loop--;
cnt=UIP_TTL*4;
while(cnt)
{
delay_ms(1);
cnt--;
tmp=ENC28J60_Receive_Data_From_Ethernet_Network();
if(tmp==TCP_ACK_RX_Flag)
{//断开连接步骤2,接收到"ACK数据包"
ret=1;
cnt=0;
loop=0;
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
#endif
}
}
}
cnt=0;ret=0;
if(tmp==TCP_ACK_RX_Flag) cnt=UIP_TTL*4;
while(cnt)
{
cnt--;
delay_ms(1);
tmp=ENC28J60_Receive_Data_From_Ethernet_Network();
if(tmp==TCP_FIN_ACK_RX_Flag)
{//断开连接步骤3,接收到"FIN+ACK数据包"
ENC28J60_Send_ACK_data_packet(pConnect);
//发送ACK应答,表示自己进入了关闭等待(CLOSE-WAIT)状态
//断开连接步骤4,发送"ACK数据包"
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
ret=1;
cnt=0;
loop=0;
#if TCP_Debug == 1
Print_MyConnectTable();
#endif
pConnect->TCPStateFlag=TCP_CLOSED;
}
}
return(ret);
}
//函数功能:告诉对方断开连接
u8 ENC28J60_Disconnect_Reply(struct ConnectTableType *pConnect)
{
u8 loop,cnt,tmp,ret;
loop=2;ret=0;
while(loop)
{
ENC28J60_Send_FIN_ACK_data_packet(pConnect);
#if TCP_Debug == 1
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
loop--;
cnt=UIP_TTL*2;
while(cnt)
{
delay_ms(1);
cnt--;
tmp=ENC28J60_Receive_Data_From_Ethernet_Network();
if(tmp==TCP_ACK_RX_Flag)
{
ret=1;
cnt=0;
loop=0;
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_MyConnectTable();
#endif
pConnect->TCPStateFlag=TCP_CLOSING;
}
}
}
return(ret);
}
//函数功能:接收来自计算机的TCP请求/TCP应答
u8 TCPIP4_Work(void)
{
struct TCP_IPv4_Packet_Type *pRX;
u16 opcode;
u8 ch1,ret;
struct ConnectTableType *pConnect;
ch1=0;ret=0;
if( Check_Receive_TCPv4_header_checksume() ) ch1=1;
//"接收TCPIP数据包之TCP头部校验和"正确,返回1
if(ch1==1)//接收到TCPIP数据包
{
pRX=(struct TCP_IPv4_Packet_Type *)&TCP_RX_buf[0];
pConnect=MyNetworkInformation.pConnect;
// Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
opcode=(u16)(pRX->flags & TCP_CTL);
printf("\r\nopcode=0x%02X\r\n",opcode);
if( opcode== (TCP_SYN | TCP_ACK) )//接收到"syn+ack应答"
{
Parse_the_TCP_MSS_option(pConnect);
//解析TCP MSS选项,获取对方的接收缓存有大。Parse the TCP MSS option, if present.
memcpy(pConnect->Remote_IP,pRX->Send_IP, 4);//保存远程IP
memcpy(pConnect->Remote_Port,pRX->Send_Port, 2);//保存远程端口
memcpy(pConnect->Local_Port,pRX->Receive_Port, 2);//保存本地端口
pConnect->TCPStateFlag = TCP_SYN_RECEIVED;//系统进入ESTABLISHED状态
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
pConnect->Time=0;
pConnect->ConnectFlag=1;//客户端建立连接
ret=1;
}
if( opcode== TCP_ACK )//接收到"ACK应答"
{
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
pConnect->Time=0;
ret=2;
}
if( opcode== (TCP_PSH | TCP_ACK) )
{//接收到"PSH+ACK应答",说明对方发送用户数据给我
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
ENC28J60_Send_ACK_data_packet(pConnect);//发送ACK应答
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
pConnect->Time=0;
ret=3;
}
if( opcode== (TCP_FIN | TCP_ACK) )//远程设备要求关闭"连接"
{
if(pConnect->TCPStateFlag==TCP_CLOSING)
{//本地主动关闭
ret=4;
}
else
{//远程关闭
pConnect->TCPStateFlag=TCP_CLOSING; //接收到"关闭连接"
Update_SequenceNumber_AcknowledgeNumber_And_Calculate_UserDataLength(pConnect,pRX);
ENC28J60_Send_ACK_data_packet(pConnect);
//发送ACK应答,表示自己进入了关闭等待(CLOSE-WAIT)状态
#if TCP_Debug == 1
Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);
Print_Send_Package(TCP_TX_buf,TCP_TX_Length);
#endif
ENC28J60_Disconnect_Reply(pConnect);
pConnect->TCPStateFlag=TCP_CLOSED; //同意"关闭连接"
pConnect->Time=0;
ret=5;
}
}
}
return(ret);
}
ENC28J60_Interface.c
#include "ENC28J60_Interface.h"
#include "string.h" //使能strcpy(),strlen(),memset(),NULL
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "enc28j60.h"
#include "Timer4.h"
#include "ARP.h"
#include "ICMP.h"
#include "TCP.h"
#include "delay.h"
u8 Network_Init(void);
u16 chksum(u16 sum, const u8 *data, u16 len);
u16 IPv4_checksum(u8 *tmpIPDataPointer);
u8 Check_the_ipV4_header_checksume(u8 *tmpIPDataPointer);
u16 ICMP4_TX_chksum(void);
u16 ICMP4_RX_chksum(void);
u8 Check_the_ICMP4_header_checksume(void);
u16 TCPv4_header_checksume(void);
u8 Check_Receive_TCPv4_header_checksume(void);
void Print_Send_Package(unsigned char* buf,u16 len);
void Print_Receive_Package(unsigned char* buf,u16 len);
void Print_MyConnectTable(void);
void Print_ARP_Table(void);
void ARP_Table_Update_Timer(void);
void MyConnectTable_Update_Timer(void);
u8 Check_Remote_MAC_In_ARPTable_Array(u8 *pRemote_IP);
u8 ENC28J60_Receive_Data_From_Ethernet_Network(void);
//配置网卡硬件,并设置MAC地址
//返回值:0,正常;1,失败;
u8 Network_Init(void)
{
u8 res=0;
//本地IP地址,如:"192.168.1.17"
MyNetworkInformation.Local_IP[0] = 192;
MyNetworkInformation.Local_IP[1] = 168;
MyNetworkInformation.Local_IP[2] = 1;
MyNetworkInformation.Local_IP[3] = 17;
//本地子网掩码: 255.255.255.0
MyNetworkInformation.Subnet_Mask[0] = 255;
MyNetworkInformation.Subnet_Mask[1] = 255;
MyNetworkInformation.Subnet_Mask[2] = 255;
MyNetworkInformation.Subnet_Mask[3] = 0;
//本地网关:192.168.1.1,其实就是你路由器的IP地址
MyNetworkInformation.Router_IP[0] = 192;
MyNetworkInformation.Router_IP[1] = 168;
MyNetworkInformation.Router_IP[2] = 1;
MyNetworkInformation.Router_IP[3] = 1;
//本地MAC地址:0x00 0x08 0xDC 0x11 0x11 0x02
MyNetworkInformation.Local_MAC[0] = 0x00;
MyNetworkInformation.Local_MAC[1] = 0x08;
MyNetworkInformation.Local_MAC[2] = 0xDC;
MyNetworkInformation.Local_MAC[3] = 0x11;
MyNetworkInformation.Local_MAC[4] = 0x11;
MyNetworkInformation.Local_MAC[5] = 0x02;
MyNetworkInformation.Local_Port[0]=(u8)(Min_Local_Port>>8);
MyNetworkInformation.Local_Port[1]=(u8)(Min_Local_Port);
//初始化本地端口值,假定本地端口为Min_Local_Port;
printf("IP:%d.%d.%d.%d\n",\
MyNetworkInformation.Local_IP[0],\
MyNetworkInformation.Local_IP[1],\
MyNetworkInformation.Local_IP[2],\
MyNetworkInformation.Local_IP[3]);
printf("Mask:%d.%d.%d.%d\n",\
MyNetworkInformation.Subnet_Mask[0],\
MyNetworkInformation.Subnet_Mask[1],\
MyNetworkInformation.Subnet_Mask[2],\
MyNetworkInformation.Subnet_Mask[3]);
printf("Gate:%d.%d.%d.%d\n",\
MyNetworkInformation.Router_IP[0],\
MyNetworkInformation.Router_IP[1],\
MyNetworkInformation.Router_IP[2],\
MyNetworkInformation.Router_IP[3]);
printf("Local_MAC:%02x-%02x-%02x-%02x-%02x-%02x\n",\
MyNetworkInformation.Local_MAC[0],\
MyNetworkInformation.Local_MAC[1],\
MyNetworkInformation.Local_MAC[2],\
MyNetworkInformation.Local_MAC[3],\
MyNetworkInformation.Local_MAC[4],\
MyNetworkInformation.Local_MAC[5]);
res=ENC28J60_Init((u8*)MyNetworkInformation.Local_MAC);//初始化ENC28J60
ENC28J60_PHY_Write(PHLCON,0x0476);
//PHLCON:PHY模块LED控制寄存器
//指示灯状态:0x476 is PHLCON LEDA(绿)=links status, LEDB(红)=receive/transmit
TIM4_Interrupt_Initializtion(10000,72);//当arr=10000,psc=72时,则为10ms,误差为1us;
timer_set(&TCP_Timer,50);//50*10=500毫秒
timer_set(&ARP_Timer,1000);//1000*10=10000毫秒=10秒
memset( ARPTable, 0, sizeof(ARPTable) );
//清除ARP表
memset( MyConnectTable, 0, sizeof(MyConnectTable) );
//清除连接表
return res;
}
//函数功能:将的data[]中的前len个字节按照"双字节进行累加和校验"
//data为"待校验数据缓存"的起始地址
//len为待校验数据的字节总数
u16 chksum(u16 sum, const u8 *data, u16 len)
{
u16 t;
const u8 *dataptr;
const u8 *last_byte;
dataptr = data; //指向"待校验数据缓存"的起始地址
last_byte = data + len - 1; //指向"待校验数据缓存"的结束地址
while(dataptr < last_byte)//至少有两个字节才能执行此while循环
{
t = (dataptr[0] << 8) + dataptr[1];//生成"大端存储方式"的16位整型数据
sum += t;//计算"累加和"
if(sum < t)//sum越过0x0000,发生进位
{
sum++;//若有进位,则加1
}
dataptr += 2;//修改指针,为下次计算"累加和"做准备
}
if(dataptr == last_byte)//剩余一个字节
{
t = (dataptr[0] << 8) + 0;//生成"大端存储方式"的16位整型数据
sum += t;//计算"累加和"
if(sum < t)//sum越过0x0000,发生进位
{
sum++;//若有进位,则加1
}
}
return sum;//返回sum
}
//函数功能:将首地址为&uip_buf[14]的缓存中的前20个字节按照"双字节进行累加和校验";
//IPV4头部(IPv4 header),占20个字节
u16 IPv4_checksum(u8 *tmpIPDataPointer)
{
u16 sum;
sum = chksum(0, tmpIPDataPointer, UIP_IPH_LEN);
//将"大端存储方式"的tmpIPDataPointer[]中的前20个字节按照"双字节进行累加和校验"
//定义UIP_LLH_LEN=14,IP头的大小为UIP_IPH_LEN=20,IP头部/IPV4头部(IPv4 header),占20个字节
return (sum == 0) ? 0xffff : sum;
//sum按照小端存储方式返回
}
//函数功能:计算"ICMP4头数据"的校验和
u16 ICMP4_TX_chksum(void)
{
u16 upper_layer_len;
u16 sum;
struct ICMP_Packet_Type *pTX;
pTX=(struct ICMP_Packet_Type *)&TCP_TX_buf[0];
upper_layer_len = ((u16)(pTX->len[0]) << 8) + pTX->len[1];
upper_layer_len = upper_layer_len - UIP_IPH_LEN;
//上层长度upper_layer_len = ICMP报文总长度 - IP头的大小20
sum=0;
sum = chksum(sum, &TCP_TX_buf[UIP_IPH_LEN + UIP_LLH_LEN],upper_layer_len);
//&TCP_TX_buf[34]为TCP头部在TCP_TX_buf[]中的首地址
return (sum == 0) ? 0xffff : sum;
//sum按照小端存储方式返回
}
//函数功能:计算"ICMP4头数据"的校验和
u16 ICMP4_RX_chksum(void)
{
u16 upper_layer_len;
u16 sum;
struct ICMP_Packet_Type *pRX;
pRX=(struct ICMP_Packet_Type *)&TCP_RX_buf[0];
upper_layer_len = ((u16)(pRX->len[0]) << 8) + pRX->len[1];
upper_layer_len = upper_layer_len - UIP_IPH_LEN;
//上层长度upper_layer_len = ICMP报文总长度 - IP头的大小20
sum=0;
sum = chksum(sum, &TCP_RX_buf[UIP_IPH_LEN + UIP_LLH_LEN],upper_layer_len);
//&TCP_RX_buf[35]为TCP头部在TCP_RX_buf[]中的首地址
return (sum == 0) ? 0xffff : sum;
//sum按照小端存储方式返回
}
//函数功能:如果接收到的"ICMP4头部校验和"正确,返回1
u8 Check_the_ICMP4_header_checksume(void)
{
u8 ret;
u16 sum;
u16 upper_layer_len;
struct ICMP_Packet_Type *pRX;
pRX=(struct ICMP_Packet_Type *)&TCP_RX_buf[0];
upper_layer_len = ((u16)(pRX->len[0]) << 8) + pRX->len[1];
upper_layer_len = upper_layer_len - UIP_IPH_LEN;
//上层长度upper_layer_len = ICMP报文总长度 - IP头的大小20
sum=0;
sum = chksum(sum, &TCP_RX_buf[UIP_IPH_LEN + UIP_LLH_LEN],upper_layer_len);
//&TCP_RX_buf[35]为TCP头部在TCP_RX_buf[]中的首地址
/*累加"TCP头和数据"时需要考虑加法进位。 Sum TCP header and data. */
sum = (sum == 0) ? 0xffff : sum;
//sum按照小端存储方式返回
ret=0;
if(sum == 0xffff)//"ICMP4头部校验和"正确
{
ret=1;//"ICMP4头部校验和"正确,返回1
return(ret);
}
return(ret);
}
//函数功能:如果接收到的"IPV4头部校验和"正确,返回1
u8 Check_the_ipV4_header_checksume(u8 *tmpIPDataPointer)
{
u8 ret;
u16 sum;
ret=0;
/* Compute and check the IP header checksum. */
sum=IPv4_checksum(tmpIPDataPointer);
//计算"IP头部/IPV4头部(IPv4 header)"20个字节的校验和
if(sum == 0xffff)//"IP头部校验和"正确
{
ret=1;//"IP头部校验和"正确,返回1
return(ret);
}
return(ret);
}
//函数功能:计算TCP头部的校验和
u16 TCPv4_header_checksume(void)
{
u16 upper_layer_len;
u16 sum;
struct TCP_IPv4_Packet_Type *pTX;
pTX=(struct TCP_IPv4_Packet_Type *)&TCP_TX_buf[0];
#if UIP_CONF_IPV6
upper_layer_len = (((u16_t)(pTX->len[0]) << 8) + pTX->len[1]);
#else /* UIP_CONF_IPV6 */
upper_layer_len = (((u16)(pTX->len[0]) << 8) + pTX->len[1]) - UIP_IPH_LEN;
//上层长度upper_layer_len = 总长度 - IP头的大小
#endif /* UIP_CONF_IPV6 */
sum = upper_layer_len + UIP_PROTO_TCP;
//累加"上层长度和IP协议"时,无需考虑进位。
//IP protocol and length fields. This addition cannot carry.
sum = chksum(sum, (u8 *)&pTX->Send_IP[0], 8);
//发送方IP和接收方IP累加时考虑进位。Sum IP source and destination addresses.
sum = chksum(sum, &TCP_TX_buf[UIP_IPH_LEN + UIP_LLH_LEN],upper_layer_len);
//&TCP_TX_buf[34]为TCP头部在TCP_TX_buf[]中的首地址
/*累加"TCP头和数据"时需要考虑加法进位。 Sum TCP header and data. */
sum= (sum == 0) ? 0xffff : sum;
return (sum);
//按照"TCP协议头"计算校验和
}
//函数功能:如果接收到的"TCP头部校验和"正确,返回1
u8 Check_Receive_TCPv4_header_checksume(void)
{
u8 ret;
u16 upper_layer_len;
u16 sum;
struct TCP_IPv4_Packet_Type *pTCPIP4_HeaderRX;
pTCPIP4_HeaderRX=(struct TCP_IPv4_Packet_Type *)&TCP_RX_buf[0];
#if UIP_CONF_IPV6
upper_layer_len = (((u16_t)(pTCPIP4_HeaderRX->len[0]) << 8) + pTCPIP4_HeaderRX->len[1]);
#else /* UIP_CONF_IPV6 */
upper_layer_len = (((u16)(pTCPIP4_HeaderRX->len[0]) << 8) + pTCPIP4_HeaderRX->len[1]) - UIP_IPH_LEN;
//上层长度upper_layer_len = IP报文总长度 - IP头的大小
#endif /* UIP_CONF_IPV6 */
sum = upper_layer_len + UIP_PROTO_TCP;
/*累加"上层长度和IP协议"时,无需考虑进位。
IP protocol and length fields. This addition cannot carry. */
sum = chksum(sum, (u8 *)&pTCPIP4_HeaderRX->Send_IP[0], 8);
//发送方IP和接收方IP需要考虑累加时考虑进位。Sum IP source and destination addresses.
sum = chksum(sum, &TCP_RX_buf[UIP_IPH_LEN + UIP_LLH_LEN],upper_layer_len);
//&TCP_RX_buf[34]为TCP头部在TCP_RX_buf[]中的首地址
/*累加"TCP头和数据"时需要考虑加法进位。 Sum TCP header and data. */
sum= (sum == 0) ? 0xffff : HTONS(sum);
ret=0;
if(sum == 0xffff)//"TCP头部校验和"正确
{
ret=1;//"TCP头部校验和"正确,返回1
return(ret);
}
return(ret);
}
//函数功能:打印发送数据包;
void Print_Send_Package(unsigned char* buf,u16 len)
{
u16 i;
u8 temp;
printf("\r\nSend_Len=%u\r\n",len);
for(i=0;i<len;i++)
{
temp=0;
if( ( (buf[i]==0x0D)||(buf[i]==0x0A) ) )
{
printf("%c",buf[i]);
temp=1;
}
if(temp==0)
{
if( ( (buf[i]>0x20)&&(buf[i]<='~') ) ) printf("%c",buf[i]);
else
{
printf(" 0x%02X",buf[i]);
printf(" ");
}
}
}
printf("\r\n");//将"\r\n"发送到调试串口,由PC显示;
}
//函数功能:打印接收数据包;
void Print_Receive_Package(unsigned char* buf,u16 len)
{
u16 i;
u8 temp;
if(len) printf("\r\nReceive_Len=%u\r\n",len);
for(i=0;i<len;i++)
{
temp=0;
if( ( (buf[i]==0x0D)||(buf[i]==0x0A) ) )
{
printf("%c",buf[i]);
temp=1;
}
if(temp==0)
{
if( ( (buf[i]>0x20)&&(buf[i]<='~') ) ) printf("%c",buf[i]);
else
{
printf(" 0x%02X",buf[i]);
printf(" ");
}
}
}
if(len) printf("\r\n");//将"\r\n"发送到调试串口,由PC显示;
}
//函数功能:打印"连接表MyConnectTable[]"
void Print_MyConnectTable(void)
{
u8 i;
struct ConnectTableType *p;
printf("\r\n");
for(i=0;i<My_ConnectTable_SIZE;i++)
{
p = &MyConnectTable[i];
printf("%u, ip=%u:%u:%u:%u ",
i,p->Remote_IP[0],p->Remote_IP[1],
p->Remote_IP[2],p->Remote_IP[3]
);
printf("lport=%u, rport=%u ",
HEX16(p->Local_Port),HEX16(p->Remote_Port)
);
printf("Receive_next=%02X_%02X_%02X_%02X ",
p->Receive_next[0],p->Receive_next[1],
p->Receive_next[2],p->Receive_next[3]
);
printf("Send_next=%02X_%02X_%02X_%02X ",
p->Send_next[0],p->Send_next[1],
p->Send_next[2],p->Send_next[3]
);
printf("DataLength=%u ",p->len);
printf("mss=%u ",HEX16(p->MSS) );
printf("initialmss=%u ",HEX16(p->initialMSS) );
printf("ConnectFlag=%u ",p->ConnectFlag);
printf("TCPStateFlag=%u ",p->TCPStateFlag);
printf("TCPTime=%u\r\n",p->Time);
}
}
//函数功能:打印"ARP表ARPTable[]"
void Print_ARP_Table(void)
{
u8 i;
struct ARPTableType *pARP_Table;
printf("\r\n");
for(i=0;i<ARPTable_SIZE;i++)
{
pARP_Table = &ARPTable[i];
printf("arp_table[%u].ip=%u:%u:%u:%u ",
i,pARP_Table->Remote_IP[0],pARP_Table->Remote_IP[1],
pARP_Table->Remote_IP[2],pARP_Table->Remote_IP[3]
);
printf("arp_table[%u].mac=%02X:%02X:%02X:%02X:%02X:%02X ",
i,pARP_Table->Remote_MAC[0],pARP_Table->Remote_MAC[1],
pARP_Table->Remote_MAC[2],pARP_Table->Remote_MAC[3],
pARP_Table->Remote_MAC[4],pARP_Table->Remote_MAC[5]
);
printf("arp_table[%u].time=%u\r\n",i,pARP_Table->Time);
}
}
//函数功能:
//如果"ARP表"中的"某个IP地址"在20分钟内未更新,则将"该条目"设置为0
//注意:ARP_Table_Update_Timer()每10秒执行一次
#define ARP_Table_UpdateTime 120
void ARP_Table_Update_Timer(void)
{
u8 i,ch;
struct ARPTableType *pARP_Table;
u16 tmp;
for(i = 0; i < ARPTable_SIZE; ++i)
{
pARP_Table = &ARPTable[i];//获取首地址
ch=0;
tmp=pARP_Table->Remote_IP[0];
tmp=tmp<<8;
tmp=tmp|pARP_Table->Remote_IP[1];
if(tmp)ch=1;
tmp=pARP_Table->Remote_IP[2];
tmp=tmp<<8;
tmp=tmp|pARP_Table->Remote_IP[3];
if(tmp)ch=1;
if(ch==1) pARP_Table->Time++;
else pARP_Table->Time=0;
if(pARP_Table->Time >= ARP_Table_UpdateTime)ch++;
//发现ARP表中有一个"IP地址和MAC地址"建立时间,长期没有更新,因此将这个IP地址设置为0
if( ch==2)
{//远程设备IP地址不为0,且建立时间超过120*10=1200秒=20分钟
memset( pARP_Table,0,sizeof(struct ARPTableType) );
}
}
#if ENC28J60_Interface_Debug == 1
Print_ARP_Table();
Print_MyConnectTable();
#endif
}
//函数功能:
//如果"连接表"中的"某个IP地址"在2分钟内未更新,则将"该条目"设置为0
//0.5秒执行一次
#define MyConnectTable_UpdateTime 240
void MyConnectTable_Update_Timer(void)
{
u8 i,ch;
struct ConnectTableType *pMyConnectTable;
u16 tmp;
for(i = 0; i < My_ConnectTable_SIZE; ++i)
{
pMyConnectTable = &MyConnectTable[i];//获取首地址
ch=0;
tmp=pMyConnectTable->Remote_IP[0];
tmp=tmp<<8;
tmp=tmp|pMyConnectTable->Remote_IP[1];
if(tmp)ch=1;
tmp=pMyConnectTable->Remote_IP[2];
tmp=tmp<<8;
tmp=tmp|pMyConnectTable->Remote_IP[3];
if(tmp)ch=1;
if(ch==1) pMyConnectTable->Time++;
else pMyConnectTable->Time=0;
if(pMyConnectTable->Time >= MyConnectTable_UpdateTime)ch++;
//发现ARP表中有一个"IP地址和MAC地址"建立时间,长期没有更新,因此将这个IP地址设置为0
if( ch==2)
{//远程设备IP地址不为0,且建立时间超过120*10=1200秒=20分钟
pMyConnectTable->TCPStateFlag=TCP_CLOSED;
ENC28J60_Send_RST_data_packet(pMyConnectTable);
//超时断开连接
memset( pMyConnectTable,0,sizeof(struct ConnectTableType) );
}
}
}
//函数功能:
//如果"ARP表"里没有pRemote_IP指向的IP地址,则返回0;否则返回1
u8 Check_Remote_MAC_In_ARPTable_Array(u8 *pRemote_IP)
{
u8 i,ret;
struct ARPTableType *p1;
memset(MyNetworkInformation.Remote_MAC, 0xFF, 6);
//先假定"接收方的MAC地址"为"0xFF,0xFF,0xFF,0xFF,0xFF,0xFF"
ret=0;
for(i=0; i<ARPTable_SIZE;i++)
{//设置合适的本地端口
p1 = &ARPTable[i];
if( memcmp(p1->Remote_IP,pRemote_IP,4)==0 )
{//发现远程IP地址
memcpy(MyNetworkInformation.Remote_MAC, p1->Remote_MAC, 6);
//更新"接收方的MAC地址"
p1->Time=0;//因为准备使用这个远程MAC地址,则将ARP表更新计数器清零。
ret=1;//ARPTable[]有所给的IP地址
break;
}
}
memcpy(MyNetworkInformation.Remote_IP, pRemote_IP, 4);
//更新"接收方的IP地址"
return(ret);
}
//函数功能:
//从以太网接收应答或数据
u8 ENC28J60_Receive_Data_From_Ethernet_Network(void)
{
u16 type;
u8 ret,ch,t;
struct IPv4_Packet_Type *pRX;
ret=0;
TCP_RX_Length=ENC28J60_Packet_Receive(UIP_BUFSIZE,TCP_RX_buf);
//读ENC28J60的接收缓存
if(TCP_RX_Length > UIP_LLH_LEN )
{//接收到的数据长度大于"以太网头部"的长度
pRX=(struct IPv4_Packet_Type *)&TCP_RX_buf[0];
type=HEX16(pRX->type);
if(type== UIP_ETHTYPE_IP4 )
{
ch=0;
if( TCP_RX_Length>sizeof(struct IPv4_Packet_Type) ) ch++;
if( pRX->Receive_IP[0]==MyNetworkInformation.Local_IP[0] ) ch++;
if( pRX->Receive_IP[1]==MyNetworkInformation.Local_IP[1] ) ch++;
if( pRX->Receive_IP[2]==MyNetworkInformation.Local_IP[2] ) ch++;
if( pRX->Receive_IP[3]==MyNetworkInformation.Local_IP[3] ) ch++;
if( Check_the_ipV4_header_checksume(&TCP_RX_buf[UIP_LLH_LEN]) ) ch++;
//"IPV4头部校验和"正确,返回1
if(ch==6)
{
t=ICMP_Work();
if(t==1)ret=ICMP_REPLY_RX_Flag;
t=TCPIP4_Work();
if(t==1)ret=TCP_SYN_ACK_RX_Flag;
if(t==2)ret=TCP_ACK_RX_Flag;
if(t==4)ret=TCP_FIN_ACK_RX_Flag;
}
}
else if( type==UIP_ETHTYPE_ARP)
{
t=ARP_Work();
if(t==1)ret=ARP_REPLY_RX_Flag;
}
else
{
}
}
return(ret);
}
ENC28J60_Interface.h
#ifndef __ENC28J60_Interface_H
#define __ENC28J60_Interface_H
#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "ENC28J60_Variable.h"
#define ENC28J60_Interface_Debug 1
#define Min_Local_Port 1000 //本地端口最小值为1000
#define UIP_LITTLE_ENDIAN 3412 //小端存储方式
#define UIP_BIG_ENDIAN 1234 //大端存储方式
#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN
#if UIP_BYTE_ORDER == UIP_BIG_ENDIAN
#define HTONS(n) (n)
#else
#define HTONS(n) (u16)( ( ( (u16) (n)) << 8 ) | ( ( (u16) (n) ) >> 8 ) )
//小端存储方式将val的高8位值和低8位值交换
#endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */
#define HEX16(d) (u16)( ( (d[0]<<8 )&0xFF00 )|( d[1]&0xFF ) )
#define ARP_REPLY_RX_Flag 1
#define ICMP_REPLY_RX_Flag 2
#define TCP_SYN_ACK_RX_Flag 3
#define TCP_ACK_RX_Flag 4
#define TCP_FIN_ACK_RX_Flag 5
extern u8 Network_Init(void);
extern u16 chksum(u16 sum, const u8 *data, u16 len);
extern u16 IPv4_checksum(u8 *tmpIPDataPointer);
extern u8 Check_the_ipV4_header_checksume(u8 *tmpIPDataPointer);
extern u16 ICMP4_TX_chksum(void);
extern u16 ICMP4_RX_chksum(void);
extern u8 Check_the_ICMP4_header_checksume(void);
extern u16 TCPv4_header_checksume(void);
extern u8 Check_Receive_TCPv4_header_checksume(void);
extern void Print_Send_Package(unsigned char* buf,u16 len);
extern void Print_Receive_Package(unsigned char* buf,u16 len);
extern void Print_MyConnectTable(void);
extern void Print_ARP_Table(void);
extern void ARP_Table_Update_Timer(void);
extern void MyConnectTable_Update_Timer(void);
extern u8 Check_Remote_MAC_In_ARPTable_Array(u8 *pRemote_IP);
extern u8 ENC28J60_Receive_Data_From_Ethernet_Network(void);
#endif
至此,TCPIP作为客户端,已经学习完了。