?? tcp.c
字號:
/**---------------------版權 (c)----------------------------------------------------------***
*** 作者:顏章健 ***
*** 郵件:jenkinyan@163.com ***
*** ***
***---------------------File Info---------------------------------------------------------***
*** 創 建 人: 顏章健 ***
*** 創建日期: 2008-03-27 ***
*** 創建版本: ***
*** 文件描述: TCP(傳輸控制協議) ***
***---------------------------------------------------------------------------------------***
*** 修 訂 人: 顏章健 ***
*** 修訂日期: 2008-04-14 ***
*** 修訂版本: ***
*** 修訂描述: 修改TCP Socket結構:使用hook的時候不申請接收窗口緩存 ***
***---------------------------------------------------------------------------------------***
*** 修 訂 人: 顏章健 ***
*** 修訂日期: 2008-05-04 ***
*** 修訂版本: ***
*** 修訂描述: 修改TCP Socket結構:省去接收窗口,去掉函數指針,發送窗口使用固定 ***
*** 緩沖區 ***
***---------------------------------------------------------------------------------------***
*** 修 訂 人: 顏章健 ***
*** 修訂日期: 2008-05-16 ***
*** 修訂版本: ***
*** 修訂描述: 增加條件編譯,允許配置使用與發送窗口等長的接收窗口 ***
***---------------------------------------------------------------------------------------**/
#include "config.h"
#include <math.h>
#if MAX_TCP_SOCKETS > 0
static TCP_SOCKET *TcpFirstSocket; // TCP 客戶端接口鏈表第一個接口
static uint8 TcpTxWindowBuffer[MAX_TCP_SOCKETS][TCP_TXWINDOW_SIZE];
#if TCP_RXWINDOW_SIZE > 0
static uint8 TcpRxWindowBuffer[MAX_TCP_SOCKETS][TCP_RXWINDOW_SIZE];
#endif
// 函數聲明
static void TcpSendPacket(TCP_SOCKET *This,uint8 *Option, uint8 OLength,uint8 Flag,uint8 DatEnable);
#endif
#if MAX_TCP_SOCKETS > 0
/********************************************************************************************
*** 函數名稱: WindowPush
*** 函數描述:
*** 入 口:
*** 出 口: 返回實際壓入的數據長度
********************************************************************************************/
static uint16 WindowPush(TCP_WINDOW *win,uint8 *src, uint16 len)
{
uint32 i;
uint16 ret;
ret = win->BufLen - win->DatLen;
if(ret > len)
{
ret=len;
}
i = ret;
while(i--)
{
*win->In++ = *src++;
if( win->In > win->End)
{
win->In = win->Start;
}
}
win->DatLen += ret;
win->Size = win->BufLen - win->DatLen;
return ret;
}
/********************************************************************************************
*** 函數名稱: WindowPop
*** 函數描述:
*** 入 口:
*** 出 口: 實際彈出的數據長度
********************************************************************************************/
/*
static uint16 WindowPop(TCP_WINDOW *win,uint8 *dst,uint16 len)
{
uint32 i;
uint16 ret;
ret = win->DatLen;
if(ret > len)
{
ret = len;
}
i = ret;
while(i--)
{
*dst++ = *win->Out++;
if(win->Out > win->End)
{
win->Out = win->Start;
}
}
win->DatLen -= ret;
win->Size = win->BufLen - win->DatLen;
return ret;
}*/
/********************************************************************************************
*** 函數名稱: WindowGetPacket
*** 函數描述:
*** 入 口:
*** 出 口: 實際讀出的數據長度
********************************************************************************************/
static uint16 WindowGetPacket(TCP_WINDOW *win,uint16 ofs,NET_PKT *pkt,uint16 Length)
{
static NET_PKT pkt2;
uint8 *dat;
uint16 len;
if(ofs >= win->DatLen)
{
pkt->Data = NULL;
pkt->Length = 0;
pkt->Next = NULL;
return 0;
}
len = win->DatLen - ofs;
if(len > Length)
{
len = Length;
}
dat = win->Out + ofs;
if(dat > win->End)
{
dat = win->Start + (dat - win->End) - 1;
}
pkt->Data = dat;
if((dat + len) > win->End)
{
pkt->Length = win->End - dat + 1;
if(pkt->Next != NULL)
{
pkt->Next->Data = win->Start;
pkt->Next->Length = len - pkt->Length;
pkt->Next->Next = NULL;
}
else
{
pkt->Next = &pkt2;
pkt2.Data = win->Start;
pkt2.Length = len - pkt->Length;
pkt2.Next = NULL;
}
}
else
{
pkt->Length = len;
pkt->Next = NULL;
}
return len;
}
/********************************************************************************************
*** 函數名稱: WindowDel
*** 函數描述:
*** 入 口:
*** 出 口:
********************************************************************************************/
static void WindowDel(TCP_WINDOW *win,uint16 len)
{
if(len > win->DatLen)
{
len = win->DatLen;
}
win->Out += len;
if(win->Out > win->End)
{
win->Out = win->Start + (win->Out - win->End - 1);
}
win->DatLen -= len;
}
/********************************************************************************************
*** 函數名稱:
*** 函數描述:
*** 入 口:
*** 出 口:
********************************************************************************************/
static void WindowCreate(TCP_WINDOW *Window,uint8 *Buffer,uint16 BufLen)
{
Window->Start = Buffer;
Window->End = Buffer + BufLen - 1;
Window->BufLen = BufLen;
Window->In = Buffer;
Window->Out = Buffer;
Window->DatLen = 0;
Window->Size = BufLen;
}
/********************************************************************************************
*** 函數名稱:
*** 函數描述:
*** 入 口:
*** 出 口:
********************************************************************************************/
uint16 TcpSocketSend(TCP_SOCKET *This,uint8 *src,uint16 len)
{
This->UREQ = UREQ_SEND;
return WindowPush(&This->TxWindow,src,len);
}
/********************************************************************************************
*** 函數名稱:
*** 函數描述:
*** 入 口:
*** 出 口:
********************************************************************************************/
uint16 TcpSocketRecv(TCP_SOCKET *This,uint8 *dst,uint16 len)
{
//return WindowPop(&This->RxWindow,dst,len); // Changed by Yan Zhangjian(C) @ 2008-04-14
/*
if(This->hook == NULL)
{
return WindowPop(&This->RxWindow,dst,len);
}
else*/
//{
if(This->Rxl > 0)
{
uint16 i,n=0;
for(i=len; i>0; i--)
{
*dst++ = *This->Rxd++;
n++;
if((This->Rxl--) == 0)
{
break;
}
}
return n;
}
else
{
return 0;
}
//}
}
/********************************************************************************************
*** 函數名稱: SocketConnect
*** 函數描述: 主動發起TCP連接
*** 入 口:
*** 出 口:
********************************************************************************************/
void TcpSocketConnect(TCP_SOCKET *This)
{
This->UREQ = UREQ_CONN;
}
/********************************************************************************************
*** 函數名稱: SocketClose
*** 函數描述: 主動關閉TCP連接
*** 入 口:
*** 出 口:
********************************************************************************************/
void TcpSocketClose(TCP_SOCKET *This)
{
This->UREQ = UREQ_CLOSE;
}
/********************************************************************************************
*** 函數名稱: TcpSocketCreate
*** 函數描述: 創建TCP接口
*** 入 口:
*** 出 口:
********************************************************************************************/
//TCP_SOCKET *TcpSocketCreate
uint8 TcpSocketCreate
( TCP_SOCKET *Socket,
uint8 *DestAddr, uint16 DestPort,
uint16 LocalPort, void (*RecvHook)(TCP_SOCKET *)
)
{
int i;
// 搜索窗口緩沖區
{
uint8 success;
TCP_SOCKET *sock;
for(i=0; i<MAX_TCP_SOCKETS; i++)
{
success = 1;
sock = TcpFirstSocket;
while(sock != NULL)
{
if(sock->TxWindow.Start == TcpTxWindowBuffer[i])
{
success = 0;
break;
}
else
{
sock = sock->Next;
}
}
if(success == 1)
{
WindowCreate(&Socket->TxWindow,TcpTxWindowBuffer[i],TCP_TXWINDOW_SIZE);
#if TCP_RXWINDOW_SIZE > 0
WindowCreate(&Socket->RxWindow,TcpRxWindowBuffer[i],TCP_RXWINDOW_SIZE);
#endif
break;
}
}
}
if(i == MAX_TCP_SOCKETS)
{
return TCP_ERR_USER;
}
Socket->TOT = 0;
Socket->RTT = 750; // RTT初始化為 750 ms,采樣后自動調整
Socket->DEV = 0; // 平均方差初始化為0 added by Yan Zhangjian(C) @ 2008-04-12
Socket->Timeout = 1500; // 超時時間初始化為1500ms,采樣后自動調整
Socket->TXS = SOCKS_CLOSED;
Socket->RXS = SOCKS_CLOSED;
Socket->UREQ = UREQ_NONE;
// 設置Socket屬性
for(i=0; i<4; i++)
{
Socket->DestAddr[i] = *DestAddr++; // 設置目的地址
}
Socket->DestPort = DestPort; // 設置目的端口
Socket->LocalPort = LocalPort; // 設置本地端口
if(TCP_RXWINDOW_SIZE > 1400)
{
Socket->MRU = 1400;
}
else
{
Socket->MRU = TCP_RXWINDOW_SIZE;
}
Socket->MTU = 500; // 初始化MTU為500字節,連接建立后自動調整
Socket->DestSize = 500; // 初始化對方窗口大小為500字節,連接建立后自動調整
Socket->Seq = (uint32)rand();
Socket->Ack = 0;
Socket->LastRcvSeq = Socket->Seq - 2;
Socket->LastRcvSeq = 0;
Socket->hook = RecvHook;
// 將Socket插入鏈表
Socket->Next = TcpFirstSocket;
TcpFirstSocket = Socket;
return TCP_ERR_NONE;
}
/********************************************************************************************
*** 函數名稱: TcpSocketDelete
*** 函數描述: 刪除已經創建的TCP接口
*** 入 口: Socket :要刪除的接口
*** 出 口: 無
********************************************************************************************/
void TcpSocketDelete(TCP_SOCKET *Socket)
{
TCP_SOCKET *sock,*next;
if(TcpFirstSocket == NULL) // 鏈表為空,直接返回
{
return;
}
if(Socket == TcpFirstSocket) // 要刪除的接口是鏈表中的第一個接口
{
Socket->TxWindow.Start = NULL;
TcpFirstSocket = TcpFirstSocket->Next;
return;
}
if(TcpFirstSocket->Next == NULL) // 鏈表只有一個接口,但不是要刪除的接口
{
return;
}
// 鏈表有多個接口,搜索要刪除的接口
sock = TcpFirstSocket;
next = sock->Next;
while(next != NULL)
{
if(next == Socket)
{
sock->TxWindow.Start = NULL; // Added by Yan Zhangjian(R) @ 2008-05-04
sock->Next = next->Next;
break;
}
next = next->Next;
sock = sock->Next;
}
}
/********************************************************************************************
*** 函數名稱: TcpSocketReset
*** 函數描述: 復位TCP連接
*** 入 口:
*** 出 口:
********************************************************************************************/
void TcpSocketReset(TCP_SOCKET *Socket)
{
TcpSendPacket(Socket,NULL,0,TCPFLAG_RST,0);
Socket->TXS = SOCKS_CLOSED;
Socket->RXS = SOCKS_CLOSED;
}
/********************************************************************************************
*** 函數名稱: TcpCheckSum
*** 函數描述: TCP校驗值計算
*** 入 口:
*** 出 口:
********************************************************************************************/
static uint16 TcpCheckSum(NET_PKT *Packet)
{
__packed uint16 *_ptr;
uint16 i,len;
uint32 sum = 0;
_ptr = (__packed uint16 *)Packet->Data;
while(Packet != NULL)
{
_ptr = (__packed uint16 *)Packet->Data;
len = Packet->Length >> 1;
for(i=0; i<len; i++)
{
sum += *_ptr++;
}
if(Packet->Next == NULL)
{
break;
}
Packet = Packet->Next;
}
if(Packet->Length & 0x0001)
{
sum += ((*_ptr)&0xff);
}
sum = (sum & 0xffff) + ((sum>>16)&0xffff);
if(sum&0xffff0000) sum++;
return (uint16)(sum & 0xffff);
}
/********************************************************************************************
*** 函數名稱: TcpSendPacket
*** 函數描述: 運輸層發送 TCP 數據包
*** 入 口: This :Socket接口
*** Data :用戶數據緩沖區指針
*** Length :用戶數據長度
*** DatEnable : 0,不允許攜帶數據; 1,允許攜帶數據
*** 出 口: 無
********************************************************************************************/
static void TcpSendPacket(
TCP_SOCKET *This,
uint8 *Option, uint8 OLength,
uint8 Flag, uint8 DatEnable)
{
uint8 TcpHead[32];
NET_PKT Head,Opt,Data;
uint16 v16;
uint32 v32;
int i;
// 設置鏈表(包含偽頭部):用于計算校驗和
Head.Data = TcpHead;
Head.Length = 32;
Head.Next = &Opt;
Opt.Data = Option;
Opt.Length = OLength;
Opt.Next = &Data;
Data.Next = NULL;
// 填充數據
v16 = This->TxWindow.DatLen;
if(v16 > This->MTU) // 限制發送數據不大于對方MTU
{
v16 = This->MTU;
}
if(v16 > This->DestSize) // 限制發送數據不大于對方窗口
{
v16 = This->DestSize;
}
//if(This->TXS < SOCKS_SENDREQ)
if(DatEnable == 0)
{
v16 = 0;
}
v16 = WindowGetPacket(&This->TxWindow,0,&Data,v16);
v16 = v16 + OLength + 20; // 計算TCP數據包總長
// 填充偽頭部內容 TcpHead[] 的前12字節
for(i=0; i<4; i++)
{
TcpHead[i] = LinkLayer.Config.Ip[i]; // 本地地址
TcpHead[i+4] = This->DestAddr[i]; // 目的地址
}
TcpHead[ 8] = 0; TcpHead[ 9] = 6;
TcpHead[10] = (v16 >> 8); TcpHead[11] = (uint8)v16;
// 填充頭部內容 TcpHead[] 的后20字節
v16 = This->LocalPort; // 本地端口
TcpHead[12] = (v16 >> 8); TcpHead[13] = (uint8)v16;
v16 = This->DestPort; // 目的端口
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -