发送TCP数据包.docx
《发送TCP数据包.docx》由会员分享,可在线阅读,更多相关《发送TCP数据包.docx(13页珍藏版)》请在冰豆网上搜索。
发送TCP数据包
发送TCP数据包
1.3相关知识
编制本程序前要对TCP协议有一定的了解。
当应用程序有报文需要通过TCP发送时,它就将此应用层报文传送给执行TCP协议的传输实体。
TCP传输实体将用户数据加上TCP报头,形成TCP数据包,在TCP数据包上增加IP头部,形成IP包。
下图显示的是TCP数据包和IP包的关系。
TCP协议的传输单元为报文段,其格式如图所示。
报文段报头的长度为20B~60B。
其中固定长度为20B,报文段长度最多为40B。
TCP报文段主要包括以下字段。
•埠号:
埠号字段包括源埠号和目的埠号。
每个埠号的长度是16位,分别表示发送该
TCP包的应用进程的埠号和接收该TCP包的应用进程的埠号。
有。
•序号:
长度为32位。
由于TCP协议是面向数据流的,它所传送的报文段可以视为连续的数据流,因此需要给每一个字节编号。
序号字段的“序号”指的是本报文段数据的第一个字节的顺序旦
节的序号。
•报头长度:
该字段长度为4位。
TCP报头长度是以4B为一个单元来计算的,实际上报头长度在20B^60B子间。
因此这个字段的值在5~15之间。
•保留:
长度为6位,留作今后使用,目前全部置0。
•控制:
这个字段定义了6种不同的标志,每个标志占一位,在同一时间可以设置一位或多位。
URG为1时,表明有需要紧急处理的数据。
ACK为1时,表明确认号的字段有效。
PST位为1时,表明要强制切断连接。
SYN位为1时,表明有确立连接的请求,这时,把序号字段的初始值作为序号字段的值,以便开始通信。
FIN为1时,表明发送方已经没有
资料发送了。
•窗口大小:
长度为16位,窗口对应的数据是以字节为单位的数据,因此最多能够传送的数据为65535R
•紧急指针:
该字段的长度为16位,指向必须紧急处理的数据的位置,只有当标志URG=1时紧急指针才有效。
从TCP报头后面的报文资料开始,到紧急指针所指出的长度的数据,就是必须紧急处理的数据。
•选项:
该字段可以多达40B,包括单字节选项和多字节选项。
TCP报头以及应用
•校验和:
该字段长度多达16位,校验和的校验范围包括伪头部、层来的数据。
其计算方法与IP协议头部的校验的计算方法一样。
伪头部为12B,它本身并不是TCP数据包的真正头部,只是在计算校验和时,临时和TCP数据包连接在一起。
伪头部的格式如下图所示。
源IP地址
目的IP地址
1.4工作环境
软件:
MicrosoftVisualC++6.0;硬件:
PC机一台。
课程设计分析
本课程设计的目标是发送一个TCP资料包,可以利用原始套接字来完成这个工作。
整个程序由初始化原始套接字和发送TCP数据包两个部分组成。
2.1使用原始套接字
SOCKETsock;
sock=socket(AF_INET,SOCK_RAW,IPPROTO_IP);
或者:
sock=WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPP
ED);
这里,我们设置了SOCK_RAW标志,表示我们声明的是一个原始套接字类型。
为使用发送接收超时设置,必须将标志位置位置为WSA_FLAG_OVERLAPPED。
在本课程设计中,发送TCP包时隐藏了自己的IP地址,因此我们要自己填充IP头,设置IP头操作选项。
其中flag设置为ture,并设定IP_HDRINCL选项,表明自己来构造IP头。
注意,如果设置IP_HDRINCL选项,那么必须具有administrator权限,要不就必须修改注册表:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Afd\Parameter\
修改键:
DisableRawSecurity(类型为DWORD),把值修改为1。
如果没有,就添加。
BOOLFlag=TRUE;
setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&Flag,sizeof(Flag));
inttimeout=1000;setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout));
在这里我们使用基本套接字SOL_SOCKET,设置SO_SNDTIMEO表示使用发送超时设置,超时时间设置为1000ms。
2.2定义IP头部、TCP头部和伪头部的数据结构
//定义IP首部
typedefstruct_iphdr
}IP_HEADER;
//定义TCP伪首部typedefstructpsd_hdr
}PSD_HEADER;
//定义TCP首部
typedefstruct_tcphdr
}TCP_HEADER;
2.3计算校验和的子函数
在填充数据包的过程中,需要调用计算校验和的函数checksum两次,分别用于校验IP头
和TCP头部(加上伪头部),其实现代码如下:
USHORTchecksum(USHORT*buffer,intsize){
unsignedlongcksum=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
#include
#include
#include
#include
#include
#include
#include
#pragmacomment(lib,"ws2_32.lib")
#defineIPVER4
//IP协议预定
//源地址//目的地址//没用//协议类型//TCP长度
#defineMAX_BUFF_LEN65500//发送缓冲区最大值
//4位首部长度,4位IP版本号
//8位服务类型TOS
//16位总长度(字节)
//16位标识
//3位标志位
//8位生存时间TTL
//8位协议(TCP,UDP或其它)
//16位IP首部校验和
//32位源IP地址
//32位目的IP地址
typedefstructtsd_hdr//定义TCP伪首部{ULONGsaddr;ULONGdaddr;
UCHARmbz;
UCHARptcl;USHORTtcpl;}PSD_HEADER;
typedefstructtcp_hdr//定义TCP首部{
//16位源埠
//16位目的端口
//32位序列号
//32位确认号
//4位首部长度/6位保留字
//6位标志位
//16位窗口大小
//16位校验和
//16位紧急数据偏移量
USHORTth_sport;
USHORTth_dport;
ULONGth_seq;
ULONGth_ack;
UCHARth_lenres;
UCHARth_flag;
USHORTth_win;
USHORTth_sum;
USHORTth_urp;
}TCP_HEADER;
//CheckSum计算校验和的子函数
USHORTchecksum(USHORT*buffer,intsize){
unsignedlongcksum=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);
}
intmain(intargc,char*argv[]){
WSADATAWSAData;
SOCKETsock;
IP_HEADERipHeader;
TCP_HEADERtcpHeader;
PSD_HEADERpsdHeader;
charSendto_Buff[MAX_BUFF_LEN];//发送缓冲区unsignedshortcheck_Buff[MAX_BUFF_LEN];//检验和缓冲区constchartcp_send_data[]={"Thisismyhomeworkofnetwort,Iamhappy!
"};
BOOLflag;intrect,nTimeOver;if(argc!
=5)
printf("Useage:
SendTcpsoruce_ipsource_portdest_ipdest_port\n");returnfalse;
if(WSAStartup(MAKEWORD(2,2),&WSAData)!
=0){
printf("WSAStartupError!
\n");returnfalse;
}if((sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)
{
printf("SocketSetupError!
\n");returnfalse;
}flag=true;if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&flag,sizeof(flag))==SOCKET_ERROR)
{
printf("setsockoptIP_HDRINCLerror!
\n");
returnfalse;
}nTimeOver=1000;
if(setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(char*)&nTimeOver,sizeof(nTimeOver))==SOCKET_ERROR)
{
printf("setsockoptSO_SNDTIMEOerror!
\n");returnfalse;
}
//填充IP首部
ipHeader.h_verlen=(IPVER<<4|sizeof(ipHeader)/sizeof(unsignedlong));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((unsignedshort)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));
intdatasize=sizeof(IP_HEADER)+sizeof(TCP_HEADER)+sizeof(tcp_send_data);
//发送数据报的目的地址
SOCKADDR_INdest;
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,(structsockaddr*)&dest,sizeof(dest));if(rect==SOCKET_ERROR)
{
printf("senderror!
:
%d\n",WSAGetLastError());
returnfalse;
}
else
printf("sendok!
\n");closesocket(sock);WSACleanup();return1;
}
谢谢大家下载,本文档下载后可根据实际情况进行编辑修改.再次谢谢大家下载.翱翔在知
识的海洋吧.