📄 tcp.c
字号:
//-----------------------------------------------------------------------------
// Net TCP.C
//
// This module handles TCP segments
// Refer to RFC 793, 896, 1122, 1323, 2018, 2581
//
// A "connection" is a unique combination of 4 items: His IP address,
// his port number, my IP address, and my port number.
//
// Note that a SYN and a FIN count as a byte of data, but a RST does
// not count. Neither do any of the other flags.
// See "TCP/IP Illustrated, Volume 1" Sect 17.3 for info on flags
//-----------------------------------------------------------------------------
#include <string.h>
#include <stdlib.h>
#include <ctype.h> // toupper
#include "main.h" // toupper
#include "udp.h" // toupper
#include "rs232.h" // toupper
#include "tcp.h" // toupper
#include "ip.h" // toupper
#include "ARP.h" // toupper
UWORK8 idata just_closed; // Keeps track of when a conxn closed
// These structures keep track of connection information
Str_CONNECTION xdata StrConnection_buf[2]; //设置可以同时接收5组IP地址的TCP数据
UWORK32 initial_sequence_nr; //序列号
UWORK8 TcpStateFlag; //TCP的状态标志位,是在各个过程中的状态
char xdata text[];
// Options: MSS (4 bytes), NOPS (2 bytes), Selective ACK (2 bytes)
UWORK8 code options_buf[10] = {0x02, 0x04, 0x05, 0xB4, 0x01, 0x01, 0x04, 0x02};
UWORK8 code optionsRe_buf[4] = {0x02, 0x04, 0x05, 0x78}; //建立连接的TCP选项
UWORK8 HttpSendFlag; //TCP有发送的数据
//------------------------------------------------------------------------
// Initialize variables declared in this module
//
//------------------------------------------------------------------------
/*********************************************************************
函数名: void init_tcp(void)
功能: 初始化TCP的接收缓存器
输入: None
输出: None
返回: None
日期: 2004/02/04
*********************************************************************/
void init_tcp(void)
{
memset(StrConnection_buf, 0, sizeof(StrConnection_buf));
just_closed = FALSE;
initial_sequence_nr = 1;
}
//------------------------------------------------------------------------
// This runs every 0.5 seconds. If the connection has had no activity
// it initiates closing the connection.
//
//------------------------------------------------------------------------
/*********************************************************************
函数名: void tcp_inactivity(void)
功能: 判断TCP的状态
输入: None
输出: None
返回: None
日期: 2004/02/04
This runs every 0.5 seconds. If the connection has had no activity
// it initiates closing the connection.
*********************************************************************/
//------------------------------------------------------------------------
// This runs every 0.5 seconds. If the connection has had no activity
// it initiates closing the connection.
//
//------------------------------------------------------------------------
/*
void tcp_inactivity(void)
{
UWORK8 idata nr;
// Look for active connections in the established state
for (nr = 0; nr < 2; nr++)
{
if ((StrConnection_buf[nr].ipaddr != 0) && (StrConnection_buf[nr].state == STATE_ESTABLISHED) && (StrConnection_buf[nr].inactivity))
{
// Decrement the timer and see if it hit 0
StrConnection_buf[nr].inactivity--;
if (StrConnection_buf[nr].inactivity == 0)
{
tcp_ReBack((FLG_ACK | FLG_FIN), 20, nr);
StrConnection_buf[nr].My_SERIESNUM++; // For my FIN
StrConnection_buf[nr].state = STATE_FIN_WAIT_1;
// if (debug) serial_send("TCP: Entered FIN_WAIT_1 state\r");
}
}
}
}
*/
//------------------------------------------------------------------------
// This runs every 0.5 seconds. If the other end has not ACK'd
// everyting we have sent, it re-sends it. To save RAM space, we
// regenerate a segment rather than keeping a bunch of segments
// hanging around eating up RAM. A connection should not be in an
// opening or closing state when this timer expires, so we simply
// send a reset.
//
// If a connection is in the ESTABLISHED state when the timer expires
// then we have just sent a web page so re-send the page
//------------------------------------------------------------------------
/*
void tcp_retransmit(void)
{
UWORK8 idata retries = 0;
UWORK8 idata nr;
// Scan through all active connections
for (nr = 0; nr < 2; nr++)
{
if ((StrConnection_buf[nr].ipaddr != 0) && (StrConnection_buf[nr].timer))
{
// Decrement the timer and see if it hit 0
StrConnection_buf[nr].timer--;
if (StrConnection_buf[nr].timer == 0)
{
// Socket just timed out. If we are not in ESTABLISHED state
// something is amiss so send reset and close connection
if (StrConnection_buf[nr].state != STATE_ESTABLISHED)
{
// Send reset and close connection
// if (debug) serial_send("TCP: Timeout, sending reset\r");
tcp_ReBack(FLG_RST, 20, nr);
StrConnection_buf[nr].ipaddr = 0;
return;
}
else
{
// Socket is in ESTABLISHED state. First make sure his
// ack number is not bogus.
if (StrConnection_buf[nr].his_TRUECODE > StrConnection_buf[nr].My_SERIESNUM)
{
// Send reset and close connection
// if (debug) serial_send("TCP: Timeout, sending reset\r");
tcp_ReBack(FLG_RST, 20, nr);
StrConnection_buf[nr].ipaddr = 0;
return;
}
// We always increment our sequence number immediately
// after sending, so the ack number from the other end
// should be equal to our sequence number. If it is less,
// it means he lost some of our data.
if (StrConnection_buf[nr].his_TRUECODE < StrConnection_buf[nr].My_SERIESNUM)
{
retries++;
if (retries <= 2)
{
// The only thing we send is a web page, and it looks
// like other end did not get it, so resend
// but do not increase my sequence number
// if (debug) serial_send("TCP: Timeout, resending data\r");
// http_server(StrConnection_buf[nr].query, 0, nr, 1);
StrConnection_buf[nr].inactivity = INACTIVITY_TIME;
}
else
{
// if (debug) serial_send("TCP: Giving up, sending reset\r");
// Send reset and close connection
tcp_ReBack(FLG_RST, 20, nr);
StrConnection_buf[nr].ipaddr = 0;
}
}
}
}
}
}
}
*/
/*********************************************************************
函数名: void IpReceive(void)
功能: Ip处理,因为是接收一帧处理一帧,不用返回成功失败
输入: 接收的帧是Ip帧
输出: 处理IP分组
返回: None
日期: 2004/12/20
*********************************************************************/
UWORK16 cksum(UWORK8 xdata *check,UWORK16 length) //计算校验和
{
UWORK32 sum=0;
UWORK16 i;
UWORK16 xdata *ptr;
ptr=(UWORK16 xdata *)check;
for (i=0;i<(length)/2;i++)
{
sum+=*ptr++;
}
if (length&0x01)//表示长度为单数
{
sum=sum+((*ptr)&0xff00);
}
sum=(sum&0xffff)+((sum>>16)&0xffff);//高16位和低16位相加
if(sum&0xffff0000)
{//表示有进位
sum++;
}
return ( (UWORK16)((sum)&0xffff));
// return ( (UWORK16)(~((sum)&0xffff)));
}
/*********************************************************************
函数名: void tcp_ReBack(UWORK16 flags, UWORK16 hdr_len, UWORK8 nr)
功能: 发送TCP
输入: flags: TCP的标志位, hdr_len: TCP的长度,nr:是接收的次数
输出: None
返回: None
日期: 2004/02/04
*********************************************************************/
void tcp_ReBack(UWORK16 flags, UWORK16 TCPHead_len, UWORK8 nr)
{
ST_TCP_FORMAT *strTcp;
strTcp = (ST_TCP_FORMAT *)&NetSend_buf[20];
strTcp->usSourcePort = SourcePort; //源端口
strTcp->usDesPort = StrConnection_buf[nr].usDesPort;
strTcp->ucSERIESNUM = StrConnection_buf[nr].My_SERIESNUM; //序号为0
strTcp->ucTRUECODE = StrConnection_buf[nr].His_SERIESNUM; //确认号为0
memcpy(&IPDestAddress_buf[0],(UWORK8 *)&StrConnection_buf[nr].ipaddr, 4); //发送的目标IP地址在TCP接收中已附值
strTcp->ucMOVENUM = (TCPHead_len << 10) | flags; //记算TCP的头长度及相关的标志位
strTcp->ucWINDOWBIG = 1024; //设置窗口大小
strTcp->ucTCPCHECK = 0; //先设置校验位为0
strTcp->ucMUSGPOINT = 0; //紧急指针为0
if (nr == NO_CONNECTION)
{
strTcp->ucSERIESNUM = 0;
strTcp->ucTRUECODE = 0;
}
if (TCPHead_len == 28) //如果发送的字节为28,那么多余的部分为TCP的很选项
{
memcpy(&strTcp->options[0], options_buf, 8); //把默认的TCP选择附值过去
}
//下面是给IP打包头
gstIphead.ucVerAndLen = 0x45; //版本号和长度,各占4位
gstIphead.ucDs = 0; //区分服务
gstIphead.usTotalLen = IP_HEAD_LEN + TCPHead_len; //头加后面的数据
gstIphead.usID = ++LocalIpID;
gstIphead.usSegOffset = 0;
gstIphead.ucTTL = 0; // max hops
gstIphead.ucprotocol = TCP;
gstIphead.usCheckSum = TCPHead_len; //在计算TCP的校验位时,TCP的长度要算两次,此时是借IP的校验位来做第二个TCP的长度
memcpy(&gstIphead.ucDestIP[0],&IPDestAddress_buf[0],IP_LEN); // 目的IP
memcpy(&gstIphead.ucSourceIP[0],&IPLocalAddress_buf[0],IP_LEN); // 源IP
memcpy(&NetSend_buf[0],&gstIphead,20); //把IP的头传给发送缓存区
strTcp->ucTCPCHECK = 0;
strTcp->ucTCPCHECK = CheckSum((UWORK16 *)&NetSend_buf[8],TCPHead_len + 12); // 20 = 12个字节伪头 + 8个字节UDP头
gstIphead.ucTTL = 0x40; //校验和计算完毕,重赋TTL值
memcpy(&MAC_Remote_buf[0],&gstaRevEthernet.ucaSourceNodID[0],MAC_LEN); // 目的MAC
IpSend();
StrConnection_buf[nr].timer = TCP_TIMEOUT;
}
/*********************************************************************
函数名: void SeriesToTcp(UWORK8 *pucAddr,UWORK16 usLen)
功能: 串口数据向TCP发送
输入: None
输出: None
返回: None
日期: 2004/02/04
*********************************************************************/
void tcp_Send(UWORK16 flags, UWORK16 TCPHead_len, UWORK8 nr, UWORK8 *TData, UWORK16 TLen)
{
ST_TCP_FORMAT *strTcp;
UWORK8 *TempD;
TempD = TData;
strTcp = (ST_TCP_FORMAT *)&NetSend_buf[20];
strTcp->usSourcePort = SourcePort; //源端口
strTcp->usDesPort = StrConnection_buf[nr].usDesPort; //目标端口,在接收TCP时已附值
strTcp->ucSERIESNUM = StrConnection_buf[nr].My_SERIESNUM; //序号为0
strTcp->ucTRUECODE = StrConnection_buf[nr].His_SERIESNUM; //确认号为0
memcpy(&IPDestAddress_buf[0],(UWORK8 *)&StrConnection_buf[nr].ipaddr, 4); //发送的目标IP地址在TCP接收中已附值
strTcp->ucMOVENUM = (TCPHead_len << 10) | flags; //记算TCP的头长度及相关的标志位
strTcp->ucWINDOWBIG = 1024; //设置窗口大小
strTcp->ucTCPCHECK = 0; //先设置校验位为0
strTcp->ucMUSGPOINT = 0; //紧急指针为0
if (TCPHead_len == 24) //建立连接是的TCP的选项
{
memcpy(&strTcp->options[0], optionsRe_buf, 4); //把默认的TCP选择附值过去
}
else if (TCPHead_len == 28) //如果发送的字节为28,那么多余的部分为TCP的很选项
{
memcpy(&strTcp->options[0], options_buf, 8); //把默认的TCP选择附值过去
}
memcpy((UWORK8 *)&NetSend_buf[(20 + TCPHead_len)], TData, TLen); //把发送的数据传到发送缓存中
//下面是给IP打包头
gstIphead.ucVerAndLen = 0x45; //版本号和长度,各占4位
gstIphead.ucDs = 0; //区分服务
gstIphead.usTotalLen = IP_HEAD_LEN + TCPHead_len + TLen; //头加后面的数据
gstIphead.usID = ++LocalIpID;
gstIphead.usSegOffset = 0;
gstIphead.ucTTL = 0; // max hops
gstIphead.ucprotocol = TCP;
gstIphead.usCheckSum = TCPHead_len + TLen; //在计算TCP的校验位时,TCP的长度要算两次,此时是借IP的校验位来做第二个TCP的长度
memcpy(&gstIphead.ucDestIP[0],&IPDestAddress_buf[0],IP_LEN); // 目的IP
memcpy(&gstIphead.ucSourceIP[0],&IPLocalAddress_buf[0],IP_LEN); // 源IP
memcpy(&NetSend_buf[0],&gstIphead,20); //把IP的头传给发送缓存区
strTcp->ucTCPCHECK = 0;
strTcp->ucTCPCHECK = CheckSum((UWORK16 *)&NetSend_buf[8],TCPHead_len + TLen + 12); // 20 = 12个字节伪头 + 8个字节UDP头
gstIphead.ucTTL = 0x40; //校验和计算完毕,重赋TTL值
memcpy(&MAC_Remote_buf[0],&gstaRevEthernet.ucaSourceNodID[0],MAC_LEN); // 目的MAC
IpSend();
StrConnection_buf[nr].timer = TCP_TIMEOUT; // (Re)start TCP retransmit timer
}
/*********************************************************************
函数名: void SeriesToTcp(UWORK8 *pucAddr,UWORK16 usLen)
功能: 串口数据向TCP发送
输入: None
输出: None
返回: None
日期: 2004/02/04
*********************************************************************/
void TcpFromSeries(UWORK8 *pucAddr,UWORK16 usLen)
{
UWORK8 p;
UWORK8 *p1, *p2;
p1 = pucAddr;
p2 = pucAddr;
for(p = 0; p < 2; p++) //发现在一次没有连接,则一个IP为0
{
if((StrConnection_buf[p].ipaddr != 0) && (StrConnection_buf[p].state == STATE_ESTABLISHED)) //如果IP地址不是0,就表示有相关的连接
{
tcp_Send(FLG_PSH | FLG_ACK, 20, p, p1, usLen);
StrConnection_buf[p].SLen = usLen;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -