实验目的:
• 设计一个发送 TCP 数据包的程序,并根据本设计说明 TCP 数据包的结构
以及 TCP 协议与 IP 协议的关系,使大家对 TCP 协议的工作原理有更深入
的认识。
•
实验要求:
本程序的功能是填充一个 TCP 数据包,并发送给目的主机。
• 以命令行形式运行:SendTCP source_ip source_port dest_ip dest_port
其中 SendTCP 为程序名;source_ip 为源 IP 地址;source_port 为源端口;dest_ip
为目的 IP 地址; dest_port 为目的端口。
• 其他的 TCP 头部参数自行设定。
• 数据字段为“This is my homework of network!”.
• 成功发送后在屏幕上输出“send OK”。
课程设计分析:
• 使用原始套接字
• 定义 IP 头部、TCP 头部和伪头部的数据结构
• 填充数据包
• 发送数据包
设计思想:
本课程设计的目标是发送一个 TCP 数据包,可以利用原始套接字来完成这个
工作。整个程序由初始化原始套接字和发送 TCP 数据包两个部分组成。
创建一个原始套接字,并设置 IP 头选项
SOCKET sock;
sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
或者:
sock=WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OV
ERLAPPED);
设置 SOCK_RAW 标志,表示我们声明的是一个原始套接字类型。
为使用发送接收超时设置,必须将标志位置位置为 WSA_FLAG_OVERLAPPED。
在本课程设计中,发送 TCP 包时隐藏了自己的 IP 地址,因此我们要自己填充 IP
头,设置 IP 头操作选项。其中 flag 设置为 ture,并设定 IP_HDRINCL 选项,表
1
明 自己 来 构造 IP 头 。注 意 ,如 果 设置 IP_HDRINCL 选 项, 那 么必 须 具有
administrator 权限,要不就必须修改注册表:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Afd\Parameter\
修改键: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。
构造 IP 头和 TCP 头
这里, IP 头和 TCP 头以及 TCP 伪部的构造请参考下面它们的数据结构。
计算校验和的子函数
在填充数据包的过程中,需要调用计算校验和的函数 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);
2
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
程序流程图:
开始
构造原始套接字,并初始化
填充IP头部
计算IP头部检验和
构造TCP伪头部
填充TCP头部
计算TCP头部校验和
发送TCP数据报
结束
源程序代码:
#include
#include
3
#include
#include
#include
#include
#include
#include
#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;
UCHAR proto;
//8 位生存时间 TTL
//8 位协议 (TCP, UDP 或其他)
USHORT checksum;
//16 位 IP 首部校验和
ULONG sourceIP;
//32 位源 IP 地址
ULONG destIP;
}IP_HEADER;
//32 位目的 IP 地址
typedef struct tsd_hdr //定义 TCP 伪首部
{
ULONG saddr;
//源地址
ULONG daddr;
//目的地址
4
UCHAR mbz;
//没用
UCHAR ptcl;
//协议类型
USHORT tcpl;
//TCP 长度
}PSD_HEADER;
typedef struct tcp_hdr //定义 TCP 首部
{
USHORT th_sport;
USHORT th_dport;
ULONG th_seq;
ULONG th_ack;
//16 位源端口
//16 位目的端口
//32 位序列号
//32 位确认号
UCHAR th_lenres;
//4 位首部长度/6 位保留字
UCHAR th_flag;
USHORT th_win;
USHORT th_sum;
USHORT th_urp;
}TCP_HEADER;
//6 位标志位
//16 位窗口大小
//16 位校验和
//16 位紧急数据偏移量
//CheckSum:计算校验和的子函数
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size >1)
{
}
cksum+=*buffer++;
size -=sizeof(USHORT);
if(size)
{
5
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;
6
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))==
SO CKET_ERROR)
{
}
printf("setsockopt IP_HDRINCL error!\n");
return false;
nTimeOver=1000;
(setsockopt(sock,
SOL_SOCKET,
SO_SNDTIMEO,
(char*)&nTimeOver,
sizeof(nTimeOver))==SOCKET_ERROR)
printf("setsockopt SO_SNDTIMEO error!\n");
return false;
if
{
}
//填充 IP 首部
ipHeader.h_verlen=(IPVER<<4 | sizeof(ipHeader)/sizeof(unsigned long));
7
ipHeader.tos=(UCHAR)0;
ipHeader.total_len=htons((unsigned
short)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; //检验和暂时为 0
ipHeader.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;
tcpHeader.th_ack=0;
//TCP 长度和保留位
//SYN 序列号
//ACK 序列号置为 0
8