当前位置:文档之家› 发送TCP数据包设计说明书

发送TCP数据包设计说明书

发送TCP数据包设计说明书一课程设计准备1.1课程设计目的TCP(传输控制协议)是一种面向连接的、可靠的传输层协议。

TCP协议在网络层IP协议的基础上,向应用层用户进程提供可靠的、全双工的数据传输流。

本课程设计的目的就是设计一个发送TCP数据包的程序,并根据本设计说明TCP数据包的结构以及TCP协议与IP协议的关系,使学生对TCP 协议的工作原理有更深入的认识。

1.2课程设计的要求本程序的功能是填充一个TCP数据包,并发送给目的主机。

1)以命令行形式运行:SendTCP sourse_ip sourse_port dest_ip dest_port其中,SendTCP为程序名;sourse_ip为源端IP地址;sourse_port为源埠;dest_ip为目的IP地址;dest_port为目的埠。

2)其它的TCP头部参数请自行设定。

3)数据字段为“This is my homework of network,I am happy!”。

4)发送成功后在屏幕上输出”send OK”。

1.3相关知识编制本程序前要对TCP协议有一定的了解。

当应用程序有报文需要通过TCP发送时,它就将此应用层报文传送给执行TCP协议的传输实体。

TCP传输实体将用户数据加上TCP报头,形成TCP数据包,在TCP数据包上增加IP头部,形成IP包。

下图显示的是TCP数据包和IP包的关系。

TCP协议的传输单元为报文段,其格式如图所示。

报文段报头的长度为20B~60B。

其中固定长度为20B,报文段长度最多为40B。

TCP报文段主要包括以下字段。

·埠号:埠号字段包括源埠号和目的埠号。

每个埠号的长度是16位,分别表示发送该TCP包的应用进程的埠号和接收该TCP包的应用进程的埠号。

·序号:长度为32位。

由于TCP协议是面向数据流的,它所传送的报文段可以视为连续的数据流,因此需要给每一个字节编号。

序号字段的“序号”指的是本报文段数据的第一个字节的顺序号。

·确认号:该字段的长度为32位,它表示接收端希望接收的下一个TCP 包的第一个字节的序号。

·报头长度:该字段长度为4位。

TCP报头长度是以4B为一个单元来计算的,实际上报头长度在20B~60B子间。

因此这个字段的值在5~15之间。

·保留:长度为6位,留作今后使用,目前全部置0。

·控制:这个字段定义了6种不同的标志,每个标志占一位,在同一时间可以设置一位或多位。

URG为1时,表明有需要紧急处理的数据。

ACK为1时,表明确认号的字段有效。

PST位为1时,表明要强制切断连接。

SYN位为1时,表明有确立连接的请求,这时,把序号字段的初始值作为序号字段的值,以便开始通信。

FIN为1时,表明发送方已经没有资料发送了。

·窗口大小:长度为16位,窗口对应的数据是以字节为单位的数据,因此最多能够传送的数据为65535B。

·紧急指针:该字段的长度为16位,指向必须紧急处理的数据的位置,只有当标志URG=1时紧急指针才有效。

从TCP报头后面的报文资料开始,到紧急指针所指出的长度的数据,就是必须紧急处理的数据。

·选项:该字段可以多达40B,包括单字节选项和多字节选项。

·校验和:该字段长度多达16位,校验和的校验围包括伪头部、TCP报头以及应用层来的数据。

其计算方法与IP协议头部的校验的计算方法一样。

伪头部为12B,它本身并不是TCP数据包的真正头部,只是在计算校验和时,临时和TCP数据包连接在一起。

伪头部的格式如下图所示。

1.4工作环境软件:Microsoft Visual C++ 6.0;硬件:PC机一台。

二课程设计分析本课程设计的目标是发送一个TCP资料包,可以利用原始套接字来完成这个工作。

整个程序由初始化原始套接字和发送TCP数据包两个部分组成。

2.1使用原始套接字SOCKET sock;sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);或者:sock=WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERL APPED);这里,我们设置了SOCK_RAW标志,表示我们声明的是一个原始套接字类型。

为使用发送接收超时设置,必须将标志位置位置为WSA_FLAG_OVERLAPPED。

在本课程设计中,发送TCP包时隐藏了自己的IP地址,因此我们要自己填充IP头,设置IP头操作选项。

其中flag设置为ture,并设定 IP_HDRINCL 选项,表明自己来构造IP头。

注意,如果设置IP_HDRINCL 选项,那么必须具有 administrator权限,要不就必须修改注册表:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Afd\Paramete r\修改键:DisableRawSecurity(类型为DWORD),把值修改为 1。

如果没有,就添加。

BOOL Flag=TRUE;setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&Flag, sizeof(Flag));int timeout=1000;setsockopt(sock, SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout, sizeof(timeout));在这里我们使用基本套接字SOL_SOCKET,设置SO_SNDTIMEO表示使用发送超时设置,超时时间设置为1000ms。

2.2定义IP头部、TCP头部和伪头部的数据结构//定义IP首部typedef struct _iphdr{UCHAR h_lenver; //4位首部长度+4位IP版本号UCHAR tos; //8位服务类型TOSUSHORT total_len; //16位总长度(字节)USHORT ident; //16位标识USHORT frag_and_flags; //3位标志位UCHAR ttl; //8位生存时间 TTLUCHAR proto; //8位协议 (TCP, UDP 或其它) USHORT checksum; //16位IP首部校验和ULONG sourceIP; //32位源IP地址ULONG destIP; //32位目的IP地址}IP_HEADER;//定义TCP伪首部typedef struct psd_hdr {ULONG saddr; //源地址ULONG daddr; //目的地址UCHAR mbz; //没用UCHAR ptcl; //协议类型USHORT tcpl; //TCP长度}PSD_HEADER;//定义TCP首部typedef struct _tcphdr{USHORT th_sport; //16位源埠USHORT th_dport; //16位目的端口ULONG th_seq; //32位序列号ULONG th_ack; //32位确认号UCHAR th_lenres; //4位首部长度/6位保留字UCHAR th_flag; //6位标志位USHORT th_win; //16位窗口大小USHORT th_sum; //16位校验和USHORT th_urp; //16位紧急数据偏移量}TCP_HEADER;2.3计算校验和的子函数在填充数据包的过程中,需要调用计算校验和的函数checksum两次,分别用于校验IP头和TCP头部(加上伪头部),其实现代码如下:USHORT checksum(USHORT *buffer, int size){unsigned long cksum=0;while(size >1){cksum+=*buffer++;size -=sizeof(USHORT);}if(size ){cksum += *(UCHAR*)buffer;}cksum = (cksum >> 16) + (cksum & 0xffff);cksum += (cksum >>16);return (USHORT)(~cksum);}2.4程序流程图三源代码#include <stdio.h>#include <winsock2.h>#include <ws2tcpip.h>#include <time.h>#include <windows.h>#include <string.h>#include <stdlib.h>#include <iostream.h>#pragma comment(lib,"ws2_32.lib")#define IPVER 4 //IP协议预定#define MAX_BUFF_LEN 65500 //发送缓冲区最大值typedef struct ip_hdr //定义IP首部{UCHAR h_verlen; //4位首部长度,4位IP版本号UCHAR tos; //8位服务类型TOS USHORT total_len; //16位总长度(字节)USHORT ident; //16位标识USHORT frag_and_flags; //3位标志位UCHAR ttl; //8位生存时间 TTLUCHAR proto; //8位协议 (TCP, UDP 或其它) USHORT checksum; //16位IP首部校验和ULONG sourceIP; //32位源IP地址ULONG destIP; //32位目的IP地址}IP_HEADER;typedef struct tsd_hdr //定义TCP伪首部{ULONG saddr; //源地址ULONG daddr; //目的地址UCHAR mbz; //没用UCHAR ptcl; //协议类型USHORT tcpl; //TCP长度}PSD_HEADER;typedef struct tcp_hdr //定义TCP首部{USHORT th_sport; //16位源埠USHORT th_dport; //16位目的端口ULONG th_seq; //32位序列号ULONG th_ack; //32位确认号UCHAR th_lenres; //4位首部长度/6位保留字UCHAR th_flag; //6位标志位USHORT th_win; //16位窗口大小USHORT th_sum; //16位校验和USHORT th_urp; //16位紧急数据偏移量}TCP_HEADER;//CheckSum:计算校验和的子函数USHORT checksum(USHORT *buffer, int size){unsigned long cksum=0;while(size >1){cksum+=*buffer++;size -=sizeof(USHORT);}if(size){cksum += *(UCHAR*)buffer;}cksum = (cksum >> 16) + (cksum & 0xffff);cksum += (cksum >>16);return (USHORT)(~cksum);}int main(int argc, char* argv[]){WSADATA WSAData;SOCKET sock;IP_HEADER ipHeader;TCP_HEADER tcpHeader;PSD_HEADER psdHeader;char Sendto_Buff[MAX_BUFF_LEN]; //发送缓冲区unsigned short check_Buff[MAX_BUFF_LEN]; //检验和缓冲区const char tcp_send_data[]={"This is my homework of networt,I am happy!"};BOOL flag;int rect,nTimeOver;if (argc!= 5){printf("Useage: SendTcp soruce_ip source_port dest_ip dest_port \n");return false;}if (WSAStartup(MAKEWORD(2,2), &WSAData)!=0){printf("WSAStartup Error!\n");return false;}if((sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET){printf("Socket Setup Error!\n");return false;}flag=true;if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&flag,sizeof(flag ))==SOCKET_ERROR){printf("setsockopt IP_HDRINCL error!\n");return false;}nTimeOver=1000;if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver, sizeof(nTimeOver))==SOCKET_ERROR){printf("setsockopt SO_SNDTIMEO error!\n");return false;}//填充IP首部ipHeader.h_verlen=(IPVER<<4 | sizeof(ipHeader)/sizeof(unsigned long));ipHeader.tos=(UCHAR)0;ipHeader.total_len=htons((unsignedshort)sizeof(ipHeader)+sizeof(tcpHeader)+sizeof(tcp_send_data)); ipHeader.ident=0; //16位标识ipHeader.frag_and_flags=0; //3位标志位ipHeader.ttl=128; //8位生存时间ipHeader.proto=IPPROTO_UDP; //协议类型ipHeader.checksum=0; //检验和暂时为0ipHeader.sourceIP=inet_addr(argv[1]); //32位源IP地址ipHeader.destIP=inet_addr(argv[3]); //32位目的IP地址//计算IP头部检验和memset(check_Buff,0,MAX_BUFF_LEN);memcpy(check_Buff,&ipHeader,sizeof(IP_HEADER));ipHeader.checksum=checksum(check_Buff,sizeof(IP_HEADER));//构造TCP伪首部psdHeader.saddr=ipHeader.sourceIP;psdHeader.daddr=ipHeader.destIP;psdHeader.mbz=0;psdHeader.ptcl=ipHeader.proto;psdHeader.tcpl=htons(sizeof(TCP_HEADER)+sizeof(tcp_send_data));//填充TCP首部tcpHeader.th_dport=htons(atoi(argv[4])); //16位目的端口号tcpHeader.th_sport=htons(atoi(argv[2])); //16位源埠号tcpHeader.th_seq=0; //SYN序列号tcpHeader.th_ack=0; //ACK序列号置为0//TCP长度和保留位tcpHeader.th_lenres=(sizeof(tcpHeader)/sizeof(unsignedlong)<<4|0);tcpHeader.th_flag=2; //修改这里来实现不同的标志位探测,2是SYN,1是//FIN,16是ACK探测等等tcpHeader.th_win=htons((unsigned short)16384); //窗口大小 tcpHeader.th_urp=0; //偏移大小tcpHeader.th_sum=0; //检验和暂时填为0//计算TCP校验和memset(check_Buff,0,MAX_BUFF_LEN);memcpy(check_Buff,&psdHeader,sizeof(psdHeader));memcpy(check_Buff+sizeof(psdHeader),&tcpHeader,sizeof(tcpHeader)) ;memcpy(check_Buff+sizeof(PSD_HEADER)+sizeof(TCP_HEADER),tcp_send_data,sizeof(tcp_send_data));tcpHeader.th_sum=checksum(check_Buff,sizeof(PSD_HEADER)+ sizeof(TCP_HEADER)+sizeof(tcp_send_data));//填充发送缓冲区memset(Sendto_Buff,0,MAX_BUFF_LEN);memcpy(Sendto_Buff,&ipHeader,sizeof(IP_HEADER));memcpy(Sendto_Buff+sizeof(IP_HEADER),&tcpHeader,sizeof(TCP_HEADER));memcpy(Sendto_Buff+sizeof(IP_HEADER)+sizeof(TCP_HEADER),tcp_send_data,sizeof(tcp_send_data));int datasize=sizeof(IP_HEADER)+sizeof(TCP_HEADER)+sizeof(tcp_send_data);//发送数据报的目的地址SOCKADDR_IN dest;memset(&dest,0,sizeof(dest));dest.sin_family=AF_INET;dest.sin_addr.s_addr=inet_addr(argv[3]);dest.sin_port=htons(atoi(argv[4]));rect=sendto(sock,Sendto_Buff,datasize, 0,(struct sockaddr*)&dest, sizeof(dest));if (rect==SOCKET_ERROR){printf("send error!:%d\n",WSAGetLastError());return false;}elseprintf("send ok!\n");closesocket(sock);WSACleanup();return 1;}四运行结果与运行情况在Microsoft Visual C++ 6.0下编写源程序,然后调试成功后运行,生成可执行档sendtcp.exe。

相关主题