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 函数接口

问题记录

  1. gSrcPort应该为2007,而非指导书上的2005
  2. 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;
}
最后修改:2020 年 10 月 11 日
如果觉得我的文章对你有用,请随意赞赏