TCP协议实验
[TOC]
实验目的
本实验的主要目的是学习和了解 TCP 协议的原理和设计实现的机制。TCP 协议还要向应用层提供编程接口,即网络编程中所普遍使用的 Socket 函数。通过实现这些接口函数,可以深入了解网络编程的原理,提高网络程序的设计和调试能力。
实验要求
1) 了解 TCP 协议的主要内容,并针对客户端角色的、“停-等”模式的 TCP 协议,完成对接收和发送流程的设计。
2) 实现 TCP 报文的接收流程,重点是报文接收的有限状态机。
3) 实现 TCP 报文的发送流程,完成 TCP 报文的封装处理。
4) 实现客户端 Socket 函数接口。
实验内容
实验内容主要包括:
1) 设计保存 TCP 连接相关信息的数据结构(一般称为 TCB,Transmission Control Block
)。
2) TCP 协议的接收处理。
3) TCP 协议的封装发送。
4) TCP 协议提供的 Socket 函数接口
问题记录
gSrcPort
应该为2007,而非指导书上的2005- 在
stud_tcp_output()
中新建的TCB
,在stud_tcp_input()
中调用要将接收端和发送端互换
实验源码
代码基于上次作业修改完成,故有较多注释
/*
* THIS FILE IS FOR TCP TEST
*/
/*
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
*/
#include "sysInclude.h"
extern void tcp_DiscardPkt(char *pBuffer, int type);
extern void tcp_sendReport(int type);
extern void tcp_sendIpPkt(unsigned char *pData, UINT16 len, unsigned int srcAddr, unsigned int dstAddr, UINT8 ttl);
extern int waitIpPacket(char *pBuffer, int timeout);
extern unsigned int getIpv4Address();
extern unsigned int getServerIpv4Address();
#define BUFFER_SIZE 1024
#define TIMEOUT 10
#define FIN 0x01
#define SYN 0x02
#define ACK 0x10
#define FIN_ACK 0x11
#define SYN_ACK 0x12
#define CLOSED 0
#define LISTEN 1
#define SYN_RCVD 2
#define SYN_SENT 3
#define ESTABLISHED 4
#define FIN_WAIT_1 5
#define FIN_WAIT_2 6
#define TIME_WAIT 7
#define GetSrcPort(p) (ntohs(*(UINT16*)p))
#define GetDstPort(p) (ntohs(*(UINT16*)(p+2)))
#define GetSeq(p) (ntohl(*(UINT32*)(p+4)))
#define GetACK(p) (ntohl(*(UINT32*)(p+8)))
#define GetHeaderLength(p) ((p[12]>>2)&0x3C)
#define GetFlags(p) (p[13]&0x13)
#define SetSrcPort(p,x) *(UINT16*)p=htons(x)
#define SetDstPort(p,x) *(UINT16*)(p+2)=htons(x)
#define SetSeq(p,x) *(UINT32*)(p+4)=htonl(x)
#define SetACK(p,x) *(UINT32*)(p+8)=htonl(x)
#define SetHeaderLength(p,x) p[12]=(x<<2)
#define SetFlags(p,x) p[13]=x
#define SetWindow(p,x) *(UINT16*)(p+14)=htons(x)
#define SetCheckSum(p,x) *(UINT16*)(p+16)=x
int gSrcPort = 2007;
int gDstPort = 2006;
int gSeqNum=1;
int gAckNum=1;
struct TCB{
TCB* next_tcb;
int socketfd;
UINT32 srcAddr;
UINT32 dstAddr;
UINT16 srcPort;
UINT16 dstPort;
UINT32 seq;
UINT32 ack;
UINT16 window;
UINT8 state;
TCB(int sockfd){
seq=gSeqNum;
ack=gAckNum;
window=1;
state=CLOSED;
next_tcb=NULL;
socketfd=sockfd;
}
TCB(){
seq=gSeqNum;
ack=gAckNum;
window=1;
state=CLOSED;
next_tcb=NULL;
socketfd=0;
srcAddr=getIpv4Address();
dstAddr=getServerIpv4Address();
srcPort=gSrcPort;
dstPort=gDstPort;
}
};
TCB * tcb_linklist_head = NULL;
static int socketfd=1;
UINT16 calculate_checksum(char *pBuffer, int len, UINT32 srcAddr, UINT32 dstAddr){
int tcp_len=len+12;
UINT32 sum=0;
if(tcp_len & 0x1 ==1)
tcp_len+=1;
char * buffer=new char[tcp_len];
memset(buffer, 0, tcp_len);
memcpy(buffer+12, pBuffer, len);
*(UINT32*)buffer=htonl(srcAddr);
*(UINT32*)(buffer+4)=htonl(dstAddr);
buffer[9]=6;
*(UINT16*)(buffer+10)=htons(len);
for(int i=0; i<tcp_len; i+=2)
sum += *(UINT16*)(buffer+i);
sum = (sum & 0xffff)+(sum>>16);
sum=~sum;
delete[] buffer;
printf("checksum:%x\n",sum);
return sum;
}
TCB* searchTCB_byaddr(UINT32 srcAddr, UINT16 srcPort, UINT32 dstAddr, UINT16 dstPort){
TCB * tcb=tcb_linklist_head;
while(tcb!=NULL && !(tcb->srcAddr==srcAddr&&tcb->srcPort==srcPort&&tcb->dstAddr==dstAddr&& tcb->dstPort==dstPort)){
tcb=tcb->next_tcb;
}
return tcb;
}
TCB* searchTCB_byid(int socketfd){
TCB* tcb = tcb_linklist_head;
while(tcb!=NULL && tcb->socketfd!=socketfd)
tcb=tcb->next_tcb;
return tcb;
}
int stud_tcp_input(char *pBuffer, unsigned short len, unsigned int srcAddr, unsigned int dstAddr)
{
printf("\nTCP Input!\n");
if(calculate_checksum(pBuffer,len,ntohl(srcAddr),ntohl(dstAddr))!=0){
return -1;
}
UINT16 srcPort=GetSrcPort(pBuffer);
UINT16 dstPort=GetDstPort(pBuffer);
UINT32 seq=GetSeq(pBuffer);
UINT32 ack=GetACK(pBuffer);
UINT8 flags=GetFlags(pBuffer);
TCB *tcb = searchTCB_byaddr(ntohl(dstAddr),dstPort,ntohl(srcAddr),srcPort);
if(tcb==NULL){
return -1;
}
if(ack!=tcb->seq+1){
tcp_DiscardPkt(pBuffer,STUD_TCP_TEST_SEQNO_ERROR);
return -1;
}
if(tcb->state==SYN_SENT && flags==SYN_ACK){
tcb->seq=ack;
tcb->ack=seq+1;
stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
}else if(tcb->state==FIN_WAIT_1 && flags==ACK){
tcb->state=FIN_WAIT_2;
}else if(tcb->state==FIN_WAIT_2 && flags==FIN_ACK){
tcb->seq=ack;
tcb->ack=seq+1;
tcb->state=TIME_WAIT;
stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
tcb->state=CLOSED;
}
return 0;
}
void stud_tcp_output(char *pData, unsigned short len, unsigned char flag, unsigned short srcPort, unsigned short dstPort, unsigned int srcAddr, unsigned int dstAddr)
{
printf("\nTCP output\n");
TCB * tcb=searchTCB_byaddr(srcAddr, srcPort, dstAddr, dstPort);
if(tcb_linklist_head==NULL){
printf("\n NEW TCB!\n");
tcb= new TCB();
tcb_linklist_head=tcb;
}
if(tcb==NULL){
return;
}
if(tcb->window==0){
return;
}
unsigned char * packet = new unsigned char[len + 20];
memset(packet,0, len+20);
memcpy(packet+20,pData,len);
SetSrcPort(packet,tcb->srcPort);
SetDstPort(packet,tcb->dstPort);
SetSeq(packet,tcb->seq);
SetACK(packet,tcb->ack);
SetHeaderLength(packet,20);
switch(flag){
case PACKET_TYPE_SYN:
SetFlags(packet,SYN);
tcb->state=SYN_SENT;
break;
case PACKET_TYPE_ACK:
SetFlags(packet,ACK);
break;
case PACKET_TYPE_SYN_ACK:
SetFlags(packet,SYN_ACK);
break;
case PACKET_TYPE_FIN:
SetFlags(packet, FIN);
break;
case PACKET_TYPE_FIN_ACK:
SetFlags(packet,FIN_ACK);
tcb->state=FIN_WAIT_1;
break;
case PACKET_TYPE_DATA:
break;
}
SetWindow(packet,tcb->window);
SetCheckSum(packet,calculate_checksum((char*)packet,len+20,srcAddr,dstAddr));
tcp_sendIpPkt(packet,len+20,tcb->srcAddr,tcb->dstAddr,255);
delete[] packet;
return;
}
int stud_tcp_socket(int domain, int type, int protocol)
{
printf("\nTCP SOCKET\n");
TCB * tcb= new TCB(socketfd++);
tcb->next_tcb=tcb_linklist_head;
tcb_linklist_head=tcb;
return tcb->socketfd;
}
int stud_tcp_connect(int sockfd, struct sockaddr_in *addr, int addrlen)
{
printf("\n TCP CONNECT\n");
char buffer[BUFFER_SIZE];
TCB * tcb=searchTCB_byid(sockfd);
if(tcb==NULL)
return -1;
tcb->srcAddr= getIpv4Address();
tcb->srcPort=gSrcPort;
tcb->dstAddr=ntohl(addr->sin_addr.s_addr);
tcb->dstPort=ntohs(addr->sin_port);
stud_tcp_output(NULL,0,PACKET_TYPE_SYN,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
if(waitIpPacket(buffer,TIMEOUT)==-1)
return -1;
if(GetFlags(buffer)!=SYN_ACK)
return -1;
tcb->seq=GetACK(buffer);
tcb->ack=GetSeq(buffer)+1;
stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
tcb->state=ESTABLISHED;
return 0;
}
int stud_tcp_send(int sockfd, const unsigned char *pData, unsigned short datalen, int flags)
{
printf("\nTCP SEND\n");
char buffer[BUFFER_SIZE];
TCB* tcb=searchTCB_byid(sockfd);
if(tcb==NULL)
return -1;
if(tcb->state!=ESTABLISHED)
return -1;
stud_tcp_output((char*)pData,datalen,PACKET_TYPE_DATA,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
if(waitIpPacket(buffer,TIMEOUT)==-1)
return -1;
if(GetFlags(buffer)!=ACK)
return -1;
tcb->seq=GetACK(buffer);
tcb->ack=GetSeq(buffer)+1;
return 0;
}
int stud_tcp_recv(int sockfd, unsigned char *pData, unsigned short datalen, int flags)
{
printf("\nTCP RECEIVE\n");
char buffer[BUFFER_SIZE];
int len=0;
TCB *tcb=searchTCB_byid(sockfd);
if(tcb==NULL)
return -1;
if(tcb->state!=ESTABLISHED)
return -1;
len=waitIpPacket(buffer,TIMEOUT);
if(len==-1)
return -1;
int header_length=GetHeaderLength(buffer);
memcpy(pData,buffer+header_length,len-header_length);
tcb->seq=GetACK(buffer);
tcb->ack=GetSeq(buffer)+(len-header_length);
stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
return 0;
}
int stud_tcp_close(int sockfd)
{
printf("\nTCP CLOS\n");
char buffer[BUFFER_SIZE];
TCB *pre = NULL;
TCB * tcb =tcb_linklist_head;
while(tcb!=NULL && tcb->socketfd!=sockfd){
pre=tcb;
tcb=tcb->next_tcb;
}
if(tcb==NULL)
return -1;
if(tcb->state!=ESTABLISHED){
if(pre!=NULL){
pre->next_tcb=tcb->next_tcb;
}else{
tcb_linklist_head=tcb->next_tcb;
}
delete tcb;
return -1;
}
stud_tcp_output(NULL,0,PACKET_TYPE_FIN_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
if(waitIpPacket(buffer,TIMEOUT)==-1)
return -1;
if(GetFlags(buffer)==ACK){
tcb->state=FIN_WAIT_2;
tcb->seq=GetACK(buffer);
tcb->ack=GetSeq(buffer)+1;
if(waitIpPacket(buffer,TIMEOUT)==-1)
return -1;
if(GetFlags(buffer)==FIN_ACK){
tcb->state=TIME_WAIT;
tcb->seq=GetACK(buffer);
tcb->ack=GetSeq(buffer)+1;
stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr);
}else{
return -1;
}
}else{
return -1;
}
if(pre!=NULL){
pre->next_tcb=tcb->next_tcb;
}else{
tcb_linklist_head=tcb->next_tcb;
}
delete tcb;
return 0;
}
2 条评论
写的很好,支持一下
谢谢