一 : 发送和接收TCP数据包
湖北工业大学
计算机网络课程设计
设计题目:发送和接收
班 级:
姓 名:
学 号:
指导老师: TCP数据包 XXX xxx XXX XXX
tcp数据包 发送和接收TCP数据包
发送和接受TCP数据包
1.设计题目与要求
1.1设计题目
发送和接收TCP数据包
1.2技术要求
本设计的功能是填充一个TCP数据包,并发送给目的主机。[www.61k.com]
1)以命令行形式运行:SendTCP source_ip source_port dest_ip dest_port,其中SendTCP是程序名,source_ip为源端IP地址,source_port为源端口号,dest_ip为目的地址,dest_port为目的端口号。
2)其他的TCP头部参数请自行设定。
3)数据字段为“This is my homework of network,I am happy!”。
4)成功发送后在屏幕上输出“send OK”。
2.总的设计思想及系统平台、语言、工具等
2.1工作环境
软件:Microsoft Visual C++ 6.0;硬件:PC机一台。
2.2设计思想
本课程设计的目标是发送一个TCP数据包,可以利用原始套接字来完成这个工作。整个程序由初始化原始套接字和发送TCP数据包两个部分组成。
2.2.1创建一个原始套接字,并设置IP头选项
1
tcp数据包 发送和接收TCP数据包
SOCKET sock;
sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
或者:
sock=WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);
这里,我们设置了SOCK_RAW标志,表示我们声明的是一个原始套接字类型。(www.61k.com) 为使用发送接收超时设置,必须将标志位置位置为WSA_FLAG_OVERLAPPED。在本课程设计中,发送TCP包时隐藏了自己的IP地址,因此我们要自己填充IP头,设置IP头操作选项。其中flag设置为ture,并设定 IP_HDRINCL 选项,表明自己来构造IP头。注意,如果设置IP_HDRINCL 选项,那么必须具有 administrator权限,要不就必须修改注册表:
HKEY_LOCAL_MACHINESystemCurrentControlSetServicesAfdParameter 修改键: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.2构造IP头和TCP头
这里, IP头和TCP头以及TCP伪部的构造请参考下面它们的数据结构。
2.2.3计算校验和的子函数
在填充数据包的过程中,需要调用计算校验和的函数checksum两次,分别用于校验IP头和TCP头部(加上伪头部),其实现代码如下:
USHORT checksum(USHORT *buffer, int size)
{
2
tcp数据包 发送和接收TCP数据包
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);
}
3.数据结构与模块说明(功能与流程图)
3.1数据结构
typedef struct _iphdr //定义IP首部
{
UCHAR h_lenver; //4位首部长度+4位IP版本号 UCHAR tos; //8位服务类型TOS
USHORT total_len; //16位总长度(字节)
USHORT ident; //16位标识
USHORT frag_and_flags; //3位标志位
UCHAR ttl; //8位生存时间 TTL
UCHAR proto; //8位协议 (TCP, UDP 或其他) USHORT checksum; //16位IP首部校验和
ULONG sourceIP; //32位源IP地址
ULONG destIP; //32位目的IP地址
3
tcp数据包 发送和接收TCP数据包
}IP_HEADER;
typedef struct psd_hdr //定义TCP伪首部 {
ULONG saddr; //源地址
ULONG daddr; //目的地址 UCHAR mbz; //没用
UCHAR ptcl;
USHORT tcpl;
}PSD_HEADER;
typedef struct _tcphdr
{
USHORT th_sport;
USHORT th_dport; //16
ULONG th_seq;
ULONG th_ack;
UCHAR th_lenres;
UCHAR th_flag;
USHORT th_win;
USHORT th_sum;
USHORT th_urp; //16
}TCP_HEADER;
//协议类型 //TCP长度 //定义TCP首部 //16位源端口 位目的端口 //32位序列号 //32位确认号 //4位首部长度/6位保留字 //6位标志位 //16位窗口大小 //16位校验和 位紧急数据偏移量 4
tcp数据包 发送和接收TCP数据包
4.源程序
#include <stdio.h> #include <winsock2.h> #include <ws2tcpip.h> #include <time.h> 5
tcp数据包 发送和接收TCP数据包
#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位标识
61阅读提醒您本文地址:
USHORT frag_and_flags; //3位标志位
UCHAR ttl; //8位生存时间 TTL
UCHAR 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;
6
tcp数据包 发送和接收TCP数据包
//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;
}
7
tcp数据包 发送和接收TCP数据包
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(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);
8
tcp数据包 发送和接收TCP数据包
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(unsigned
long)<<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));
61阅读提醒您本文地址:
//填充发送缓冲区
/*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);
//发送数据报的目的地址
9
tcp数据包 发送和接收TCP数据包
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!:%dn",WSAGetLastError());
return false;
}
else
printf("send ok!n");
closesocket(sock);
WSACleanup();
return 1;
}
5.运行结果与运行情况
在Microsoft Visual C++ 6.0下编写源程序,然后调试成功后运行,生成可执行文件123.exe。[www.61k.com)因为该程序是带参数的,所以进入dos,并进入到该可执行文件的目录下后在命令提示行下输入:sendtcp 202.114.181.1 202.114.181.2000,运行结果如下:
10
tcp数据包 发送和接收TCP数据包
6.调试报告
6.1调试记录
第一、二天,主要是查阅资料,学习算法。(www.61k.com]该期间重点了解了IP首部、TCP首部的结构,以及如何将它们和数据字段一起封装成数据包。同时也掌握了计算校验和的算法,从而通过该算法分别计算出IP首部和TCP首部的校验和。接着就是对网络编程接口socket套接字的学习,除了掌握它的初始化外,还对一些经常使用的高级套接字函数进行运用,例如WSAStartup()、WSASocket()、socket()、bind()、listen()、accept()、connect()、sendto()、send()等。当然根据课程设计的要求,有很多函数并不需要用到。第三、四天,主要是编程实现,调试源程序,不断发现问题并修改。
调试过程中没有出现重大的语法错误,主要是运行的结果不理想,和预期的结果有差距。经分析,影响运行结果的主要因素是实现数据包发送的代码部分,相对而言,填充数据包的部分是不容易出错的,因为它是按照规定的格式自定义填充的,只要耐心一点都不会出错。至于数据包的发送,由于是利用函数sendto()
11
tcp数据包 发送和接收TCP数据包
来实现的,最初运行程序时出现了这个错误:send error!10004 。[www.61k.com]查阅资料得知该错误的信息:case 10004: error = "Interrupted system call 中断的系统呼叫"; break;。起初甚至怀疑windows XP系统下不能正常调用sendto(),并向这个方向尝试了很多修改,问题依然没有得到解决。最后尝试性的将sendto()中的参数datasize修改位一个很小的具体的值是得到“send ok!”的运行结果,而只要超过一定值就依然出现以前的问题。这时我回到源程序中检查发现在自定义IP首部中将协议类型定义TCP(Header.proto=IPPROTO_TCP;),而sendto()是面向UDP的,将协议类型修改为UDP(Header.proto=IPPROTO_UDP;) 后,调试运行成功,问题得到解决。
6.2自我评析和总结
这次的课程设计让我学到了很多东西。我最大的收获是学会了TCP数据包的填充和发送。在对TCP数据包进行填充时,首先需要我们去充分了解它的数据结构,在这个过程中可以了解相应字节上应该存放的内容和它们的功能。在实现TCP数据包的发送中,我第一次深深接触了网络编程接口socket套接字的相关知识,虽然本次的课程设计只用到了其中一小部分知识,但这并不会影响到我对这方面知识的全面了解。总之这次的课程设计,让我对网络中的数据收发有了一定的了解,并激发了我对计算机网络的浓厚兴趣。
7.参考文献
(1)谢希仁编著,《计算机网络》,电子工业出版社,2003年。
(2)周明天、汪文勇编著,“TCP/IP网络原理与技术”,清华大学出版社,1993年。
(3)上网查询相关资料。
12
61阅读提醒您本文地址:
二 : 关于TCP,NODELAY和TCP
关于TCP_NODELAY和TCP_CORK选项
这两个选项都对网络连接的行为具有重要的作用。许多UNIX系统都实现了TCP_NODELAY选项,但是,TCP_CORK则是Linux系统所独有的 而且相对较新;它首先在内核版本2.4上得以实现。此外,其他UNIX系统版本也有功能类似的选项,值得注意的是,在某种由BSD派生的系统上的 TCP_NOPUSH选项其实就是TCP_CORK的一部分具体实现。
TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。 John Nagle是Nagle算法的发明人,后者就是用他的名字来命名的,他在1984年首次用这种方法来尝试解决福特汽车公司的网络拥塞问题(欲了解详情请参 看IETF RFC 896)。他解决的问题就是所谓的silly window syndrome ,中文称“愚蠢窗口症候群”,具体含义是,因为普遍终端应用程序每产生一次击键操作就会发送一个包,而典型情况下一个包会拥有一个字节的数据载荷以及40 个字节长的包头,于是产生4000%的过载,很轻易地就能令网络发生拥塞,。 Nagle化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为缺省配置了,但在我们看来,有些场合下把这一选项关掉也是合乎需要的。
现在让我们假设某个应用程序发出了一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。如果我们马上发 送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。例如,当我们正在发送一个较短的请求并且等候较大的响应时,相关过载与传输的数据总量相 比就会比较低,而且,如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的TCP_NODELAY选项来完成,这样就禁用了Nagle 算法。
另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服务器。应用 Nagle算法在这种情况下就会产生问题。但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同 TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就让我们仔细分析下其工作原理。
假设应用程序使用sendfile()函数来转移大量数据。应用协议通常要求发送某些信息来预先解释数据,这些信息其实就是报头内容。典型情况下报头很 小,而且套接字上设置了TCP_NODELAY。有报头的包将被立即传输,在某些情况下(取决于内部的包计数器),因为这个包成功地被对方收到后需要请求 对方确认。这样,大量数据的传输就会被推迟而且产生了不必要的网络流量交换。
但是,如果我们在套接字上设置了TCP_CORK(可以比喻为在管道上插入“塞子”)选项,具有报头的包就会填补大量的数据,所有的数据都根据大小自动地 通过包传输出去。当数据传输完成时,最好取消TCP_CORK 选项设置给连接“拔去塞子”以便任一部分的帧都能发送出去。这同“塞住”网络连接同等重要。
总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文),那么我们建议你设置TCP_CORK选项,这样在这些数据之间不存在延迟。能极大地有益于WWW、FTP以及文件服务器的性能,同时也简化了你的工作。示例代码如下:
intfd, on = 1;
…
/* 此处是创建套接字等操作,出于篇幅的考虑省略*/
…
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* cork */
write (fd, …);
fprintf (fd, …);
sendfile (fd, …);
write (fd, …);
sendfile (fd, …);
…
on = 0;
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* 拔去塞子 */
不幸的是,许多常用的程序并没有考虑到以上问题。例如,Eric Allman编写的sendmail就没有对其套接字设置任何选项。
Apache HTTPD是因特网上最流行的Web服务器,它的所有套接字就都设置了TCP_NODELAY选项,而且其性能也深受大多数用户的满意。这是为什么呢?答 案就在于实现的差别之上。由BSD衍生的TCP/IP协议栈(值得注意的是FreeBSD)在这种状况下的操作就不同。当在TCP_NODELAY 模式下提交大量小数据块传输时,大量信息将按照一次write()函数调用发送一块数据的方式发送出去。然而,因为负责请求交付确认的记数器是面向字节而 非面向包(在Linux上)的,所以引入延迟的概率就降低了很多。结果仅仅和全部数据的大小有关系。而 Linux 在第一包到达之后就要求确认,FreeBSD则在进行如此操作之前会等待好几百个包。
在Linux系统上,TCP_NODELAY的效果同习惯于BSD TCP/IP协议栈的开发者所期望的效果有很大不同,而且在Linux上的Apache性能表现也会更差些。其他在Linux上频繁采用TCP_NODELAY的应用程序也有同样的问题。
TCP_DEFER_ACCEPT
我们首先考虑的第1个选项是TCP_DEFER_ACCEPT(这是Linux系统上的叫法,其他一些操作系统上也有同样的选项但使用不同的名字)。为了 理解TCP_DEFER_ACCEPT选项的具体思想,我们有必要大致阐述一下典型的HTTP客户/服务器交互过程。请回想下TCP是如何与传输数据的目 标建立连接的。在网络上,在分离的单元之间传输的信息称为IP包(或IP 数据报)。一个包总有一个携带服务信息的包头,包头用于内部协议的处理,并且它也可以携带数据负载。服务信息的典型例子就是一套所谓的标志,它把包标记代 表TCP/IP协议栈内的特殊含义,例如收到包的成功确认等等。通常,在经过“标记”的包里携带负载是完全可能的,但有时,内部逻辑迫使TCP/IP协议 栈发出只有包头的IP包。这些包经常会引发讨厌的网络延迟而且还增加了系统的负载,结果导致网络性能在整体上降低。
现在服务器创建了一个套接字同时等待连接。TCP/IP式的连接过程就是所谓“3次握手”。首先,客户程序发送一个设置SYN标志而且不带数据负载的 TCP包(一个SYN包)。服务器则以发出带SYN/ACK标志的数据包(一个SYN/ACK包)作为刚才收到包的确认响应。客户随后发送一个ACK包确 认收到了第2个包从而结束连接过程。在收到客户发来的这个SYN/ACK包之后,服务器会唤醒一个接收进程等待数据到达。当3次握手完成后,客户程序即开 始把“有用的”的数据发送给服务器。通常,一个HTTP请求的量是很小的而且完全可以装到一个包里。但是,在以上的情况下,至少有4个包将用来进行双向传 输,这样就增加了可观的延迟时间。此外,你还得注意到,在“有用的”数据被发送之前,接收方已经开始在等待信息了。
为了减轻这些问题所带来的影响,Linux(以及其他的一些操作系统)在其TCP实现中包括了TCP_DEFER_ACCEPT选项。它们设置在侦听套接 字的服务器方,该选项命令内核不等待最后的ACK包而且在第1个真正有数据的包到达才初始化侦听进程。在发送SYN/ACK包之后,服务器就会等待客户程 序发送含数据的IP包。现在,只需要在网络上传送3个包了,而且还显著降低了连接建立的延迟,对HTTP通信而言尤其如此。
这一选项在好些操作系统上都有相应的对等物。例如,在FreeBSD上,同样的行为可以用以下代码实现:
/* 为明晰起见,此处略去无关代码 */
struct accept_filter_arg af = { "dataready", "" };
setsockopt(s, SOL_SOCKET, SO_ACCEPTFILTER, &af, sizeof(af));
这个特征在FreeBSD上叫做“接受过滤器”,而且具有多种用法。不过,在几乎所有的情况下其效果与TCP_DEFER_ACCEPT是一样的:服务器 不等待最后的ACK包而仅仅等待携带数据负载的包。要了解该选项及其对高性能Web服务器的重要意义的更多信息请参考Apache文档上的有关内容。
就HTTP客户/服务器交互而言,有可能需要改变客户程序的行为。客户程序为什么要发送这种“无用的”ACK包呢?这是因为,TCP协议栈无法知道ACK 包的状态。如果采用FTP而非HTTP,那么客户程序直到接收了FTP服务器提示的数据包之后才发送数据。在这种情况下,延迟的ACK将导致客户/服务器 交互出现延迟。为了确定ACK是否必要,客户程序必须知道应用程序协议及其当前状态。这样,修改客户行为就成为必要了。
对Linux客户程序来说,我们还可以采用另一个选项,它也被叫做TCP_DEFER_ACCEPT。我们知道,套接字分成两种类型,侦听套接字和连接套 接字,所以它们也各自具有相应的TCP选项集合。因此,经常同时采用的这两类选项却具有同样的名字也是完全可能的。在连接套接字上设置该选项以后,客户在 收到一个SYN/ACK包之后就不再发送ACK包,而是等待用户程序的下一个发送数据请求;因此,服务器发送的包也就相应减少了。
TCP_QUICKACK
阻止因发送无用包而引发延迟的另一个方法是使用TCP_QUICKACK选项。这一选项与 TCP_DEFER_ACCEPT不同,它不但能用作管理连接建立过程而且在正常数据传输过程期间也可以使用。另外,它能在客户/服务器连接的任何一方设 置。如果知道数据不久即将发送,那么推迟ACK包的发送就会派上用场,而且最好在那个携带数据的数据包上设置ACK 标志以便把网络负载减到最小。当发送方肯定数据将被立即发送(多个包)时,TCP_QUICKACK选项可以设置为0。对处于“连接”状态下的套接字该选 项的缺省值是1,首次使用以后内核将把该选项立即复位为1(这是个一次性的选项)。
在某些情形下,发出ACK包则非常有用。ACK包将确认数据块的接收,而且,当下一块被处理时不至于引入延迟。这种数据传输模式对交互过程是相当典型的,因为此类情况下用户的输入时刻无法预测。在Linux系统上这就是缺省的套接字行为。
在上述情况下,客户程序在向服务器发送HTTP请求,而预先就知道请求包很短所以在连接建立之后就应该立即发送,这可谓HTTP的典型工作方式。既然没有 必要发送一个纯粹的ACK包,所以设置TCP_QUICKACK为0以提高性能是完全可能的。在服务器方,这两种选项都只能在侦听套接字上设置一次。所有 的套接字,也就是被接受呼叫间接创建的套接字则会继承原有套接字的所有选项。
通过TCP_CORK、TCP_DEFER_ACCEPT和TCP_QUICKACK选项的组合,参与每一HTTP交互的数据包数量将被降低到最小的可接 受水平(根据TCP协议的要求和安全方面的考虑)。结果不仅是获得更快的数据传输和请求处理速度而且还使客户/服务器双向延迟实现了最小化。
三 : 发送TCP数据包
课程设计任务书
题目六: 发送TCP数据包
初始条件:
1) 学习相关知识;
2) C/C++/VC/VB/JAVA语言;
3) PC机一台;
要求完成的主要任务: (包括课程设计工作量及其技术要求,以及说明书撰写等具体要求) 本设计的功能是填充一个TCP数据包,并发送给目的主机。
程序具体要求如下:
1) 以命令行形式运行:SendTCP、source_ip、source_port、dest_ip、dest_port,其中SendTCP是程序名,source_ip为源端IP地址,source_port为源端口号,dest_ip为目的地址,dest_port为目的端口号;
2) 其他的TCP头部参数请自行设定;
3) 数据字段为“This is my homework of network ,I am happy !”
4) 成功发送后在屏幕上输出“Send OK” 。
时间安排: 2008年6月22日-27日 (第18周)
第一、二天:查阅资料,学习算法
第三、四天:编程调试
第五天:书写报告
指导教师签名: 2008年6月20日 系主任(或责任教师)签名: 2008年6月20日
目 录
摘要 .................................................................................................................................................................................... 1 I
关键字 ................................................................................................................................................................................ 1
0.引言 .............................................................................................................................................................................. 1
1.设计课题 ...................................................................................................................................................................... 2
1.1 课程设计课题....................................................................................................................................................... 2
1.2 课程设计要求....................................................................................................................................................... 2
2.系统概述 ...................................................................................................................................................................... 2
2.1 设计程序目的....................................................................................................................................................... 2
2.2 程序设计思想....................................................................................................................................................... 3
2.3 程序运行操作平台 ............................................................................................................................................... 3
3.概要设计 ...................................................................................................................................................................... 3
3.1 相关知识 .............................................................................................................................................................. 3
3.2 程序流程图.......................................................................................................................................................... 4
4.详细设计 ...................................................................................................................................................................... 6
4.1 定义各头部数据结构 ........................................................................................................................................... 6
4.2 计算校验和子程序 ............................................................................................................................................... 7
4.3 填充数据包 .......................................................................................................................................................... 8
4.4 发送数据包 .......................................................................................................................................................... 9
4.5 执行程序 ............................................................................................................................................................ 10
4.6 分析程序 ............................................................................................................................................................ 11
5. 设计中遇到的问题 ..................................................................................................................................................... 12
6.设计后的感想及收获 ................................................................................................................................................ 12
6.1 设计后对计算机网络课程的认识 ..................................................................................................................... 12
6.2 自己此次课程设计的收获 ................................................................................................................................. 13
7. 致谢 ............................................................................................................................................................................. 13
参考文献 .......................................................................................................................................................................... 13
源程序 .............................................................................................................................................................................. 14
II
发送TCP数据包
摘要
在这个课程设计中,主要多对TCP数据包的结构以及TCP协议与IP协议的关系进行了说明。在Linux环境下,用C语言编程,设计一个服务器程序(Server),一个客户端程序(Client),由服务器向客户端发送数据。在设计的过程中,需要对IP首部、TCP首部和伪首部定义数据结构,并且填充IP数据包和TCP数据包。在填充数据包的过程中,需要调用两次计算校验和的函数,分别用于校验IP头和TCP头部(加上伪头部)。在发送数据包的过程中,要填充发送缓冲区并填入数据报的目的地址。这样整个发送过程就结束了。相对而言,客户端程序比较简单。
关键字
TCP,IP,伪头部,校验和,源IP地址,源端口号,目的IP地址,目的端口号 0.引言
《计算机网络》课程是一门很重要的专业课,它主要是对各个层的协议做了详细的介绍,学习这门课程我们就必须掌握各个层的协议的工作原理。OSI模型有7个不同的层,分为两个组。上面三层定义了中断系统中的应用程序将被彼此通信,以及如何与用户通信。下面4层定义了三怎样进行端到端的数据传输。OSI参考模型的7层和各层的功能
[1]. Application layer (应用层)文件、打印、消息、数据库和应用程序
[2]. Presentation layer(表示层) 数据加密、压缩和转换服务
[3]. Session layer (会话层)会话控制
[4]. Transport layer(传输层) 端到端连接
[5]. Network layer (网络层)路由选择
[6]. Data Link layer (数据链路层)数据组合成帧
1
[7]. Physical layer (物理层)物理拓扑
在这个课程中,主要涉及到传输层的协议TCP,对它的包的结构和发送过程做了详细的分析。
1.设计课题
1.1 课程设计课题
发送TCP数据包。
1.2 课程设计要求
本设计的功能是填充一个TCP数据包,并发送给目的主机。
程序具体要求如下:
1) 以命令行形式运行:SendTCP、source_ip、source_port、dest_ip、dest_port,其中SendTCP是程序名,source_ip为源端IP地址,source_port为源端口号,dest_ip为目的地址,dest_port为目的端口号;
2) 其他的TCP头部参数请自行设定;
3) 数据字段为“This is my homework of network ,I am happy !”
4) 成功发送后在屏幕上输出“Send OK” 。
2.系统概述
2.1 设计程序目的
TCP(传输控制协议)是一种面向连接的、可靠的传输层协议。TCP协议在网络层IP协议的基础上,向应用层用户进程提供可靠的、全双工的数据流传输。本课程设计的目的就是设计一个发送TCP数据包的程序,并根据本设计说明TCP数据包的结构以及TCP协议与IP协议的关系,使学生对TCP协议的工作原理有更深入的认识。
2
2.2程序设计思想
当多信息的传输要求比较高时,我们才用TCP来实现数据的传输。本程序是在linux环境下实现了发送和接受TCP数据包的功能。首先是服务器端程序的开发,主要包括定义IP头部、TCP头部、和伪头部的数据结构,填充IP头部,计算IP头部校验和,构造TCP伪头部,填充TCP头部,计算TCP头部校验和,发送TCP数据报。接着是对客户端程序的开发,程序结构与服务端程序类似,也包括定义IP头部、TCP头部、和伪头部的数据结构,另外还有等待接受wait_tcp_signal子程序。在运行程序时,首先打开客户端程序,再打开服务器端程序,并将要传送的数据填入文件中(本文是hello.txt),接受端将收到的数据存放在hello2.txt中。
2.3 程序运行操作平台
2.3.1运行环境
本次课程设计是在linux的系统平台下用C语言实现发送TCP数据包的程序,所使用的工具有vi编辑器、gcc编译器等。
2.3.2使用方法
编辑 vi
编译gcc 目标文件名 源文件名
运行 ./*.out
建文本文档 ./client hello2.txt
3.概要设计
3.1 相关知识
TCP(传输控制 它是OST里的传输层里的协议。它是一种面向连接的、可靠的传输层协议。TCP协议在网络层IP协议的基础 ,向应用层用户进程提供可靠的、双工的数据流传输。
3
对TCP协议,当应用进程有报文需要通过TCP协议发送时,它就将此应用层报文传送给执行TCP协议的传输实体。TCP实体将用户数据加上TCP报头,形成TCP数据包, TCP数据包 增加IP头部,形成IP包。下图是TCP数据包和IP的关系。
TCP协议的传输单元称为报文段、其格式如所示:
报文段报头的长度为20B-60B。其中固定长度是20B,选项部分长度最多为40B。 伪头部为12B,它本身并不是TCP数据包的真正头部,只是在计算校验和时,临时和TCP数据包连接在一起。伪头部的格式如果所示:
0 8 16 24 31
3.2 程序流程图:
服务器端流程图:
4
客户端流程程图:
5
4.详细设计
4.1 定义各头部数据结构 //IP数据报首部
struct iphdr
{
unsigned char h_verlen; unsigned char tos; unsigned short total_len; unsigned short ident; unsigned short frag_and_flags; unsigned char ttl; unsigned char proto; unsigned short checksum; unsigned int sourceIP; unsigned int destIP; };
//TCP数据报首部
struct tcphdr{ unsigned short th_sport; unsigned short th_dport; unsigned int th_seq; unsigned int th_ack; unsigned char th_lenres; unsigned char th_flag; unsigned short th_win; //版本首部长度 //服务类型 //总长度 //标识 //标志和片偏移 //生存时间 //协议 //校验和 //源端IP地址 //目的地址 //tcp数据报首部 //源端口 //目的端口 //序号 //确认号 //数据偏移&保留 //标志 //窗口 6
unsigned short th_sum; //校验和
unsigned short th_urp; //紧急指针
};
//伪首部
struct psehdr{ //伪首部
unsigned long saddr; //源IP地址
unsigned long daddr; //目的IP地址
unsigned char reserved;
unsigned char proto; //协议号
unsigned short len; //TCP长度
};
4.2 计算校验和子程序
in_cksum为计算校验和的函数,在填充数据包的过程中,需要调用这个函数两次,分别用于校验IP头部和TCP头部(加上伪头部)。
unsigned short in_cksum(unsigned short *addr,int len) //计算校验和 {
register int sum = 0;
register u_short *w = addr;
register int nleft = len;
u_short value =0;
while( nleft > 1 ){
sum += *w++;
nleft -= 2;
}
if( nleft == 1 ){
7
*(u_char *)(&value) = *(u_char *)w;
sum += value;
}
sum = ( sum >> 16 ) + ( sum & 0xffff );
sum += ( sum >> 16 );
return value;
}
4.3 填充数据包
在填充数据包的过程中,我们不仅要填充TCP头部,还要填充IP头部。同时,为了填充校验和,我们还要对TCP伪头部进行填充。
//填充IP首部
ip.h_verlen = ( 4 << 4 | sizeof(struct iphdr) / sizeof(unsigned long) ); ip.tos = 0;
ip.total_len = htons(PACKLEN);
ip.frag_and_flags = 0x40;
ip.ident = 13;
ip.ttl = 255;
ip.proto = IPPROTO_TCP;
ip.sourceIP = inet_addr(src_ip);
ip.destIP = inet_addr(dst_ip);
ip.checksum = 0;
//填充TCP首部
tcp.th_sport = htons(atoi(src_port));
tcp.th_dport = htons(atoi(dst_port));
8
tcp.th_seq = htonl(SEQ);
tcp.th_ack = htonl(0);
tcp.th_lenres= (sizeof(struct tcphdr) / 4 << 4 | 0 );
tcp.th_flag = 2;
tcp.th_win = htons(512);
tcp.th_sum = 0;
tcp.th_urp = 0;
//构造TCP伪首部
pseuhdr.saddr = ip.sourceIP;
pseuhdr.daddr = ip.destIP;
pseuhdr.reserved = 0 ;
pseuhdr.proto = ip.proto;
pseuhdr.len = htons( TCPLEN + TROJANLEN );
//TCP校验和
tcp.th_sum = in_cksum( (unsigned short *)data_buf,( PSELEN + TCPLEN + TROJANLEN + data_len ) );
4.4 发送数据包
填充完TCP包后,最后的工作就是把数据包发送出去。
//填充发送缓冲区
memcpy(data_buf,&pseuhdr,PSELEN);
memcpy(data_buf + PSELEN,&tcp,TCPLEN);
memcpy(data_buf + PSELEN + TCPLEN,&trojan,TROJANLEN);
memcpy(data_buf,&ip,IPLEN);
9
memcpy(data_buf + IPLEN,&tcp,TCPLEN);
memcpy(data_buf + IPLEN + TCPLEN,&trojan,TROJANLEN);
//发送数据报的目的地址
remote.sin_family = AF_INET;
remote.sin_port = tcp.th_dport;
remote.sin_addr.s_addr = ip.destIP;
if( (s_len = sendto( sock_id,data_buf,PACKLEN,0,(struct sockaddr *)&remote,sizeof(struct sockaddr)) )< 0 ){
perror("[-] sendto");
exit(1);
}
//发送成功
printf("[+] Packet Successfuly Sending %d size.\n",s_len);
4.5 执行程序
4.5.1先写文件hello.txt 输入内容,放在/root下。文字如下:
This is my homework of network,I am happy!
4.5.2打开客户端,使用命令./client hello2.txt 后面那个参数是传送给这个文件 使用这个命令后,就会在/root 目录下生成一个新的文件hello2.txt。
4.5.3客户端等待信号:
10
4.5.4打开服务器端,键入
./ server 127.0.0.1 12345 127.0.0.1 12345 hello.txt
4.5.5传送数据,服务器端显示:
4.5.6传送成功,客户端显示:
4.5.7看hello2.txt里面的数据:
4.6 分析程序
没文件的错误,或者ip填错了,则会显示如果提示:
11
如果键入的参数不符合,程序也会做出提示,重新输入参数:
在运行程序的过程中,先打开客户端程序,等待信号的输入,然后再打开服务器端,准备发送数据。
5. 设计中遇到的问题
5.1首先得熟悉linux的命令,这是在linux下编程的基础。
编译gcc 目标文件名 源文件名
运行 ./*.out
5.2发送TCP数据包的程序内容多,但是结构简单清晰。要对包的每项内容搞清楚,并定义相应的数据结构,从而对IP首部,TCP首部和TCP伪头部给予相应的数据结构定义。另外在数据包的填充过程中,也要对IP首部,TCP首部和伪首部分别填充。设计到的一个子函数,计算校验和,在老师所给的资料中已有,可以引用。
6.设计后的感想及收获
6.1 设计后对计算机网络课程的认识
《计算机网络》是一门比较重要的专业课,它设计的内容主要就是协议。对各个层的协议搞清楚了,并对其相应的算法有一定的了解就掌握了《计算机网络》这门课程。
TCP(传输控制协议)是一种面向连接的、可靠的传输层协议。TCP协议在网络层IP协议的基础上,向应用层用户进程提供可靠的、全双工的数据流传输。TCP传输实体将用户数
12
据加上TCP报头,形成TCP数据包,在TCP数据包上增加IP头部,形成IP包,这就是TCP数据包和IP数据包的关系。
6.2 自己此次课程设计的收获
在做这个实验的过程中,我查阅了很多资料,自己收获颇深。通过做发送TCP数据包的课程设计,让我对刚刚熟悉的Linux有了进一步了解,对Linux下C编程更加熟悉了。同时,我还对TCP数据包的传送有了更深刻的认识,了解了TCP数据包、IP数据包的格式,并了解了TCP协议与IP协议的关系,让我对TCP协议的工作原理有了更深入的认识。
7. 致谢
首先感谢学校及老师给了我们这次上机实习的机会。虽然它与实际生活联系不是太密
切,但毕竟让我们感到了实战的感觉。在实践编程过程中积累了宝贵的经验。与此同时,
在编程序过程中,进一步提高了对操作系统的认识,发现了许多问题,更学到了许多知识。
在这次课程设计中,我不但出现的错误主要是在同学的指导下得到解决的,同学热情
帮助,而且不厌其烦地为我解答问题。在此,我真诚地向他们表达最深诚的谢意。
参考文献
[1] 谢希仁编,《计算机网络》,电子工业出版社。
[2] 吴功宣编,《计算机网络课程设计》,机械工业出版社。
[3] 杨树青编,《Linux环境下C编程指南 》,清华大学出版社。
13
源程序
/*
SERVER:
by cecily
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#define MAXSIZE 65535
#define DATASIZE 1024
#define SEQ 12345
#define TROJAN_ID 6789
#define IPLEN sizeof(struct iphdr)
#define TCPLEN sizeof(struct tcphdr)
#define PACKLEN sizeof(struct iphdr) + sizeof(struct tcphdr) + sizeof(unsigned int) + sizeof(unsigned int) + strlen(trojan.data)
#define PSELEN sizeof(struct psehdr)
#define TROJANLEN sizeof(unsigned int) + sizeof(unsigned int) + strlen(trojan.data)
//ip数据报首部
struct iphdr
{
unsigned char h_verlen; //版本首部长度
unsigned char tos; //服务类型
unsigned short total_len; //总长度
unsigned short ident; //标识
unsigned short frag_and_flags; //标志和片偏移
unsigned char ttl; //生存时间
unsigned char proto; //协议
unsigned short checksum; //校验和
unsigned int sourceIP; //源端IP地址
unsigned int destIP; //目的地址
};
14
本文标题:发送和接收tcp数据包-发送和接收TCP数据包61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1