Loading... # 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()`中调用要将接收端和发送端互换 ## 实验源码 代码基于上次作业修改完成,故有较多注释 ```cpp /* * 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 日 © 禁止转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏
2 条评论
写的很好,支持一下
谢谢