SmartOS/TinyIP/Tcp.cpp

304 lines
7.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Tcp.h"
bool Callback(TinyIP* tip, void* param);
TcpSocket::TcpSocket(TinyIP* tip) : Socket(tip)
{
Type = IP_TCP;
Port = 0;
RemoteIP = 0;
RemotePort = 0;
LocalIP = 0;
LocalPort = 0;
seqnum = 0xa;
Status = Closed;
}
bool TcpSocket::Process(MemoryStream* ms)
{
TCP_HEADER* tcp = (TCP_HEADER*)ms->Current();
if(!ms->Seek(tcp->Length << 2)) return false;
Header = tcp;
uint len = ms->Remain();
ushort port = __REV16(tcp->DestPort);
ushort remotePort = __REV16(tcp->SrcPort);
// 仅处理本连接的IP和端口
if(Port != 0 && port != Port) return false;
if(RemotePort != 0 && remotePort != RemotePort) return false;
if(RemoteIP != 0 && Tip->RemoteIP != RemoteIP) return false;
// 不能修改主监听Socket的端口否则可能导致收不到后续连接数据
//Port = port;
//RemotePort = remotePort;
//
//Tip->Port = port;
//Tip->RemotePort = remotePort;
// 第一次同步应答
if (tcp->Flags & TCP_FLAGS_SYN && !(tcp->Flags & TCP_FLAGS_ACK)) // SYN连接请求标志位为1表示发起连接的请求数据包
{
if(OnAccepted)
OnAccepted(this, tcp, tcp->Next(), len);
else
{
#if NET_DEBUG
debug_printf("Tcp Accept "); // 打印发送方的ip
TinyIP::ShowIP(Tip->RemoteIP);
debug_printf("\r\n");
#endif
}
//第二次同步应答
Head(tcp, 1, true, false);
// 需要用到MSS所以采用4个字节的可选段
//Send(tcp, 4, TCP_FLAGS_SYN | TCP_FLAGS_ACK);
// 注意tcp->Size()包括头部的扩展数据这里不用单独填4
Send(tcp, 0, TCP_FLAGS_SYN | TCP_FLAGS_ACK);
return true;
}
// 第三次同步应答,三次应答后方可传输数据
if (tcp->Flags & TCP_FLAGS_ACK) // ACK确认标志位为1表示此数据包为应答数据包
{
// 无数据返回ACK
if (len == 0)
{
if (tcp->Flags & (TCP_FLAGS_FIN | TCP_FLAGS_RST)) //FIN结束连接请求标志位。为1表示是结束连接的请求数据包
{
Head(tcp, 1, false, true);
Send(tcp, 0, TCP_FLAGS_ACK);
}
return true;
}
if(OnReceived)
{
// 返回值指示是否向对方发送数据包
bool rs = OnReceived(this, tcp, tcp->Next(), len);
if(!rs)
{
// 发送ACK通知已收到
Head(tcp, 1, false, true);
Send(tcp, 0, TCP_FLAGS_ACK);
return true;
}
}
else
{
#if NET_DEBUG
debug_printf("Tcp Data(%d) From ", len);
TinyIP::ShowIP(RemoteIP);
debug_printf(" : ");
Sys.ShowString(tcp->Next(), len);
debug_printf("\r\n");
#endif
}
// 发送ACK通知已收到
Head(tcp, len, false, true);
//Send(buf, 0, TCP_FLAGS_ACK);
//TcpSend(buf, len);
// 响应Ack和发送数据一步到位
Send(tcp, len, TCP_FLAGS_ACK | TCP_FLAGS_PUSH);
}
else if(tcp->Flags & (TCP_FLAGS_FIN | TCP_FLAGS_RST))
{
if(OnDisconnected) OnDisconnected(this, tcp, tcp->Next(), len);
// RST是对方紧急关闭这里啥都不干
if(tcp->Flags & TCP_FLAGS_FIN)
{
Head(tcp, 1, false, true);
//Close(tcp, 0);
Send(tcp, 0, TCP_FLAGS_ACK | TCP_FLAGS_PUSH | TCP_FLAGS_FIN);
}
}
return true;
}
void TcpSocket::Send(TCP_HEADER* tcp, uint len, byte flags)
{
tcp->SrcPort = __REV16(Port);
tcp->DestPort = __REV16(RemotePort);
tcp->Flags = flags;
if(tcp->Length < sizeof(TCP_HEADER) / 4) tcp->Length = sizeof(TCP_HEADER) / 4;
// 必须在校验码之前设置,因为计算校验码需要地址
Tip->RemoteIP = RemoteIP;
// 网络序是大端
tcp->Checksum = 0;
tcp->Checksum = __REV16(Tip->CheckSum((byte*)tcp, tcp->Size() + len, 2));
debug_printf("SendTcp: Flags=0x%02x, len=%d(0x%x) %d => %d \r\n", flags, tcp->Length, tcp->Length, __REV16(tcp->SrcPort), __REV16(tcp->DestPort));
// 注意tcp->Size()包括头部的扩展数据
Tip->SendIP(IP_TCP, (byte*)tcp, tcp->Size() + len);
}
void TcpSocket::Head(TCP_HEADER* tcp, uint ackNum, bool mss, bool opSeq)
{
/*
第一次握手主机A发送位码为SYN1随机产生Seq=1234567的数据包到服务器主机B由SYN=1知道A要求建立联机
第二次握手主机B收到请求后要确认联机信息向A发送Ack=(主机A的Seq+1)SYN=1Ack=1随机产生Seq=7654321的包
第三次握手主机A收到后检查Ack是否正确即第一次发送的Seq+1以及位码Ack是否为1
若正确主机A会再发送Ack=(主机B的Seq+1)Ack=1主机B收到后确认Seq值与Ack=1则连接建立成功。
完成三次握手主机A与主机B开始传送数据。
*/
//TCP_HEADER* tcp = Header;
int ack = tcp->Ack;
tcp->Ack = __REV(__REV(tcp->Seq) + ackNum);
if (!opSeq)
{
// 我们仅仅递增第二个字节这将允许我们以256或者512字节来发包
tcp->Seq = __REV(seqnum << 8);
// step the inititial seq num by something we will not use
// during this tcp session:
seqnum += 2;
/*tcp->Seq = __REV(seqnum);
seqnum++;*/
//tcp->Seq = 0;
}else
{
tcp->Seq = ack;
}
tcp->Length = sizeof(TCP_HEADER) / 4;
// 头部后面可能有可选数据Length决定头部总长度4的倍数
if (mss)
{
uint* p = (uint*)tcp->Next();
// 使用可选域设置 MSS 到 1460:0x5b4
p[0] = __REV(0x020405b4);
p[1] = __REV(0x01030302);
p[2] = __REV(0x01010402);
tcp->Length += 3;
}
}
TCP_HEADER* TcpSocket::Create()
{
return (TCP_HEADER*)(Tip->Buffer + sizeof(ETH_HEADER) + sizeof(IP_HEADER));
}
void TcpSocket::Ack(uint len)
{
TCP_HEADER* tcp = Create();
tcp->Init(true);
Send(tcp, len, TCP_FLAGS_ACK | TCP_FLAGS_PUSH);
}
void TcpSocket::Close()
{
debug_printf("Tcp::Close ");
Tip->ShowIP(RemoteIP);
debug_printf(":%d \r\n", RemotePort);
TCP_HEADER* tcp = Create();
tcp->Init(true);
Send(tcp, 0, TCP_FLAGS_ACK | TCP_FLAGS_PUSH | TCP_FLAGS_FIN);
}
void TcpSocket::Send(byte* buf, uint len)
{
debug_printf("Tcp::Send ");
Tip->ShowIP(RemoteIP);
debug_printf(":%d buf=0x%08x len=%d ...... \r\n", RemotePort, buf, len);
TCP_HEADER* tcp = Create();
tcp->Init(true);
byte* end = Tip->Buffer + Tip->BufferSize;
if(buf < tcp->Next() || buf >= end)
{
// 复制数据,确保数据不会溢出
uint len2 = Tip->BufferSize - tcp->Offset() - tcp->Size();
assert_param(len <= len2);
memcpy(tcp->Next(), buf, len);
}
Send(tcp, len, TCP_FLAGS_PUSH);
Tip->LoopWait(Callback, this, 5000);
if(tcp->Flags & TCP_FLAGS_ACK)
debug_printf("发送成功!\r\n");
else
debug_printf("发送失败!\r\n");
}
// 连接远程服务器记录远程服务器IP和端口后续发送数据和关闭连接需要
bool TcpSocket::Connect(IPAddress ip, ushort port)
{
debug_printf("Tcp::Connect ");
Tip->ShowIP(ip);
debug_printf(":%d ...... \r\n", port);
RemoteIP = ip;
RemotePort = port;
TCP_HEADER* tcp = Create();
tcp->Init(true);
tcp->Seq = 0;
//seqnum = 0;
Head(tcp, 0, true, false);
Status = SynSent;
Send(tcp, 0, TCP_FLAGS_SYN);
if(Tip->LoopWait(Callback, this, 5000))
{
if(tcp->Flags & TCP_FLAGS_SYN)
{
Status = Established;
debug_printf("连接成功!\r\n");
return true;
}
Status = Closed;
debug_printf("拒绝连接!\r\n");
return false;
}
Status = Closed;
debug_printf("连接超时!\r\n");
return false;
}
bool Callback(TinyIP* tip, void* param)
{
ETH_HEADER* eth = (ETH_HEADER*)tip->Buffer;
if(eth->Type != ETH_IP) return false;
IP_HEADER* _ip = (IP_HEADER*)eth->Next();
if(_ip->Protocol != IP_TCP) return false;
TcpSocket* socket = (TcpSocket*)param;
TCP_HEADER* tcp = (TCP_HEADER*)_ip->Next();
// 检查端口
if(tcp->DestPort != socket->Port) return false;
socket->Header = tcp;
//if(Status == SynSent)
{
// 处理。如果对方回发第二次握手包,或者终止握手
MemoryStream ms(tip->Buffer, tip->BufferSize);
socket->Process(&ms);
}
//else
{
}
return true;
}