正交实验设计与数据分析结果报告

实验报告

课程名称   计算机在材料科学中的应用                     ,指导教师     付老师     ,日期    2012.5.9    

班级    09材化一班           ,小组成员姓名与学号  唐帆 200910230107 ,黄斌 200910230114 ,张敬文 200910230116                       

实验名称_______正交试验设计与数据分析________________

一.实验目的

1.掌握正交试验设计方法.

2.会用相关软件或自编程序对正交实验数据进行分析.

二.实验原理

   在材料科学研究与陶瓷配方中,常遇到正交实验设计内容。在正交实验设计中,因素可以是定量的,也可以是定性的。而定量因素个水平间的距离可以相等也可以不相等。利用依据数学原理制作好的规格化表——正交表。用正交表来安排实验及分析实验结果,这种方法叫正交实验法。

三.问题描述

            羟基磷灰石是一类生物陶瓷材料。利用正交实验设计法,对湿法制备羟基磷灰石的几个重要因素,如反应物初始浓度,回流时间,NaOH浓度,陈化时间作为正交表的因素,并分别拟定三个水平,

建立正交实验表进行实验研究。实验得到的产率结果

   对表中的数据进行分析,其中B的极差最大,固其对实验结果影响最大,A次之,D最小,再分析它们的均值,最后确定制备HAP的最优条件是 B1A1C2D2.

   2.为了求某无机材料的组成(SiO2,Na2O,K2O,CaO)对其弹性模量的影响,

     用L9(3*4)正交表进行实验的弹性模量

   对表中的数据进行分析,其中D的极差最大,固其对实验结果影响最大,C次之,A最小,再分析它们的均值,最后确定材料的最佳组成是D1C3B2A1/D1C3B2A2.

 3.为提高某产品的转化率,选择了三个有关的因素进行条件实验;反应温度A,反应时间B,用碱量C,并确定了它们的实验原因;

 

  用L9(3*4)正交表进行实验的转化率结果

 

 

     

   对表中的数据进行分析,其中A的极差最大,固其对实验结果影响最大,C次之,B最小,再分析它们的均值,最后确定材料的最佳生产条件是A3C2B2.

四.问题讨论

 最佳方案的选择:1.极差或均值最大,对实验结果影响很大;2.对主要因素,选择使指示最好的水平;3.对次要因素,以节约方便原则选取水平。

 

第二篇:计算机网络+课程设计源代码和实验报告+帧封装、IP数据包解析和发送TCP数据包


     

计算机网络课程设计报告

院(系):  计算机学院       

专    业:  计算机科学与技术 

姓    名:             

班    级:          

学    号:         

指导教师:             

 2009 7 4

计算机网络课程设计之协议编程

实验一  帧封装

 

实验目的:

?    编写程序,根据给出的原始数据,组装一个IEEE 802.3格式的帧(题目)默认的输入文件为二进制原始数据(文件名分别为input1和input2))。

?    要求程序为命令行程序。比如,可执行文件名为framer.exe,则命令行形式如下:framer inputfile outputfile,其中,inputfile为原始数据文件,outputfile为输出结果。

?    输出:对应input1和input2得结果分别为output1和output2。

试验要求:

?    编写程序,根据给出的原始数据,组装一个IEEE 802.3格式的帧(题目)默认的输入文件为二进制原始数据(文件名分别为input1和input2))。

?    要求程序为命令行程序。比如,可执行文件名为framer.exe,则命令行形式如下:framer inputfile outputfile,其中,inputfile为原始数据文件,outputfile为输出结果。

输出:对应input1和input2得结果分别为output1和output2

验设计相关知识:

帧:来源于串行线路上的通信。其中,发送者在发送数据的前后各添加特殊的字符,使它们成为一个帧。Ethernet从某种程度上可以被看作是机器之间的数据链路层连接。

按802.3标准的帧结构如下表所示(802.3标准的Ethernet帧结构由7部分组成)

802.3标准的帧结构

其中,帧数据字段的最小长度为46B。如果帧的LLC数据少于46B,则应将数据字段填充至46B。填充字符是任意的,不计入长度字段值中。

在校验字段中,使用的是CRC校验。校验的范围包括目的地址字段、源地址字段、长度字段、LLC数据字段。

循环冗余编码(CRC)是一种重要的线性分组码、编码和解码方法,具有简单、检错和纠错能力强等特点,在通信领域广泛地用于实现差错控制。CRC校验码的检错能力很强,不仅能检查出离散错误,还能检查出突发错误。

利用CRC进行检错的过程可简单描述如下:在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的r位监督码(CRC码),附在原始信息的后边,构成一个新的二进制码序列(共k+r位),然后发送出去。在接收端,根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。这个规则在差错控制理论中称为“生成多项式”。

CRC的基本实现

循环冗余校验码的特点:(1)CRC校验码可检测出所有单个错误。(2)CRC校验码可检测出所有奇数位错误。(3)CRC校验码可检测出所有双位的错误(4)CRC校验码可检测出所有小于、等于校验位长度的突发错误。(5)CRC校验码可以的概率检测出长度为(K+1)位的突发错误

实验分析:

?    填充帧头部字段

     要完成一次帧封装的过程,首先要完成的就是帧头部的装入,这一过程只要将签到吗、定界符、目的地址、源地址、长度字段的相应数值按顺序写入就可以了。其中,长度字段的值即为要发送的数据的实际长度。

?    填充数据字段

在填充数据字段的过程中要注意的主要问题是数据字段的长度。802.3标准中规定了帧数据字段的最小长度为46B,最大长度为1500B。如果数据不足46B,则需要通过填充0来补足;若数据长度超过1500B,则的大奖超过部分封装入下一个帧进行发送。

?    CRC校验

     帧封装的最后一步就是对数据进行校验,并将校验结果记入帧校验字段。

 

程序流程图:

CRC计算流程图:

CRC流程图

序源代码:

#include<iostream.h>

#include<fstream.h>

#include<stdlib.h>

void main(int argc,char*argv[])

{

 //如果输入命令行不正确,则输出提示后退出。

   if(argc!=3)

 {

  cout<<endl<<"请按以下格式输入:framer inputfile outputfile"<<endl;

  exit(0);

 }

 //打开指定的输出文件,以二进制方式打开并可读可写,如文件存在,则清除其内容。

 fstream file(argv[2],ios::out|ios::in|ios::binary|ios::trunc,0);

 for(int i=0;i<7;i++)

  file.put((char)0xaa);

     file.put((char)0xab);//写入B的前导码和B的帧前定界符。

     char des_add[]={char(0x00),char(0x00),char(0xE4),char(0x86),char(0x3A),char(0xDC)};

     file.write(des_add,6);//写入B的目的地址。

     char sor_add[]={char(0x00),char(0x00),char(0x80),char(0x1A),char(0xE6),char(0x65)};

     file.write(sor_add,6);//写入B的源地址。

 //创建输入文件流并打开指定的输入文件,以二进制方式打开并可读。

 ifstream infile(argv[1],ios::in|ios::binary,0);

 int length=0;

 infile.seekg(0,ios::end);//将读指针移到文件末尾。

 length=infile.tellg();//计算指针偏移量,即为输入文件的长度。

 unsigned char* data=new unsigned char[length];//创建字符指针并根据文件长度初始化。

 infile.seekg(0,ios::beg);//将读指针移到文件开始。

 infile.read(data,length);//将文件数据读入到字符指针data中。

    file.put(char(length>>8));

 file.put(char(length&0xff));//将文件长度值按照逆序写入到输出文件的长度字段中。

 file.write(data,length);//将data内容写入到输出文件中。

 //如果输入文件长度不足B,则用补足B。

 if(length<46)

 {

  for(int j=length;j<46;j++)

   file.put(char(0x00));

 }

 file.put(char(0x00));//将数据字段后添加个

 file.seekg(8,ios::beg);//将读指针指向目的地址字段,从此处开始CRC计算

 unsigned char ch;//ch用来保存读入的字符。

 unsigned char crc=char(0x00);//余数初始值为。

 while(1)//进行CRC计算

 {

  file.get(ch);

  if(ch==0xff)//判断是否到了文件结尾,如果是,则退出循环。

   break;

  for(i=0;i<8;i++)//对入读入的字符的位分别处理。

  {

   if(0x80==(crc&(0x80)))//当前余数最高位为,需要进行除法运算。

   {

    crc=(crc<<1)&(0xff);//crc左移位,最低位补。

    crc=crc|((ch&0x80)>>7);//将输入数据相应的值递补到余数末位。

    crc=crc^(0x07);//进行除法运算,即与除数的低位相异或。

   }

   else//当前余数的最高位为,不需要进行除法运算。

   {

    crc=(crc<<1)&(0xff);//crc左移位,最低位补。

    crc=crc|((ch&0x80)>>7);//将输入数据相应位的值递补到余数末位。

   }

   ch=ch<<1;//读到的字符左移位,使数据下一位作为输入位。

  }

 }

 file.clear();

 file.seekp(-1,ios::end);//将写指针移到输出文件的最后。

 file.put(crc);//写入crc码。

 file.close();

 infile.close();//关闭输入文件和输出文件。

 cout<<endl<<"数据帧文件"<<argv[2]<<" 封装完成"<<endl;

}

运行结果:

运行结果如下所示:

执行framer.exe文件的结果如下所示:

实验小结:

实现帧的封装,主要是将帧的七个部分---前导码、帧前定界符、目的地址、源地址、长度字段、数据字段和校验字段,一个一个按顺序封装的,最后使得一个帧的封装得以完成。同时,在编写程序的过程中,用到了很多的函数,这些函数的运用使得程序简便而且正确的运行出来。

实验二  解析IP数据包

实验目的:

?    设计一个解析IP数据包的程序,并根据这个程序,说明IP数据包的结构及IP协议的相关问题,从而对IP层的工作原理有更好的理解和认识。

实验要求:

    本实验的目标是捕获网络中的IP数据包,解析数据包的内容,见个结果显示在标准输出上,并同时写入日志文件。

    程序的具体要求如下:

?    以命令行形式运行:ipparse logfile,其中ipparse是程序名,而logfile则代表记录结果的日志文件。

?    在标准输出、和日志文件中写入捕获的IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。

?    当程序接收到键盘输入Ctrl+C时退出。

设计相关知识:

IP数据报的格式说明IP协议都具有什么功能。其首部,版本目前广泛使用的版本号为4;首部长度站4bit;服务类型占8bit,其中服务类型TOS子域占4位,优先级子域占3位,另一位为保留位;总长度字段为2B,IP数据包的最大长度是65535B;标识占16bit,它是一个计数器,用来产生数据报的标识;标志占3bit,其中最低为为MF,MF=1时为后面“还有分片”,MF=0表示这是数据报片中的最后一个,DF=0时,表示允许分片;片偏移以8个字节为偏移单位;生存时间字段记为TTL,单位为秒;协议段占8bit,用于指出次数据是使用何种协议,典型的协议号有6:TCP,17:UDP,1:ICMP。

本程序使用套接字socket编程,将网卡设为能够接受流经网卡的所有类型的数据包。首先,初始化套接字,然后监听数据包,解析数据包。

SOCKET sock=socket(AF_INET,SOCK_RAW,IPPROTO_IP)用来创建套接字,其参数为通信发生的区字段和套接字的类型。

WSAIoctl(sock , IO_RCVALL ,&dwBufferInLen , sizeof(dwBufferInLen)函数用来把网卡设置为混杂模式。

recv(sock,buffer,65535,0)函数用来接收经过的IP包,其参数分别是套接字描述符,缓冲区的地址,缓冲区的大小。

typedef struct IP_HEAD

{

}}ip_head;用来定义IP头部数据。

setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)函数用来获取本机IP地址

htons()函数将无符号短整型转换为网络字节顺序的数据

本程序在windows环境下利用C++语言编写。

实验设计分析:

    为了获取网络中的IP数据包,必须对网卡进行编程,我们使用套接字进行编程。

?    使用套接字

?    接收数据包

?    定义IP头部的数据结构

?    IP包的解析

程序流程图:

具体程序代码:

#include<iostream>

#include<winsock2.h>

#include<ws2tcpip.h>

#include<fstream>

#include <windows.h>

#pragma comment(lib,"ws2_32") //指定连接到网络应用和internet

#define IO_RCVALL _WSAIOW(IOC_VENDOR,1)

typedef struct IP_HEAD

{

union //定义联合

{

unsigned char Version;

unsigned char HeadLen;

};

unsigned char ServiceType;

unsigned short TotalLen;

unsigned short Identifier;

union

{

unsigned short Flags;

unsigned short FragOffset;

};

unsigned char TimeToLive;

unsigned char Protocol;

unsigned short HeadChecksum;

unsigned int SourceAddr;

unsigned int DestinAddr;

unsigned char Options;

}ip_head; //定义IP头部的数据结构

void main(int argc,char *argv[])

{

using namespace std;

ofstream outfile("C:\\logfile.txt",ios::out);

if(argc!=2)

{

cout<<endl<<"请以下格式输入命令行:PackParse packet_sum"<<endl;

return;

}

WSADATA WSAData;

if(WSAStartup(MAKEWORD(2,2), &WSAData)!=0)

{

cout<<endl<<"WSASTartup初始化失败"<<endl;

return;

}

SOCKET sock=socket(AF_INET,SOCK_RAW,IPPROTO_IP); //三个参分别为通信发生的区字段,套接字的类型,与IP协议

if(sock==INVALID_SOCKET)

{

cout<<endl<<"创建Socket失败!"<<endl;

closesocket(sock);

WSACleanup();

}

BOOL flag=TRUE;

if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *) &flag,sizeof(flag))==SOCKET_ERROR)

{

cout<<endl<<"setsockopt操作失败:"<<WSAGetLastError()<<endl;

closesocket(sock);

WSACleanup();

}

char hostName[128];//获取主机名

if(gethostname(hostName,100)==SOCKET_ERROR)

{

cout<<endl<<"gethostname操作失败:"<<WSAGetLastError()<<endl;

closesocket(sock);

WSACleanup();

}

hostent *pHostIP; //获取本地IP

if((pHostIP=gethostbyname(hostName))==NULL)

{

cout<<endl<<"gethostbyname操作失败:"<<WSAGetLastError()<<endl;

closesocket(sock);

WSACleanup();

}

sockaddr_in host_addr;//

host_addr.sin_family=AF_INET;

host_addr.sin_port=htons(6000);

host_addr.sin_addr=*(in_addr *)pHostIP->h_addr_list[0];

if(bind(sock,(PSOCKADDR)&host_addr,sizeof(host_addr))==SOCKET_ERROR)

{

cout<<endl<<"bind操作失败:"<<WSAGetLastError()<<endl;

closesocket(sock); //绑定网卡

WSACleanup();

}

DWORD dwBufferLen[10];

DWORD dwBufferInLen=1;

DWORD dwBytesReturned=0;

if(WSAIoctl(sock , IO_RCVALL ,&dwBufferInLen , sizeof(dwBufferInLen) , &dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL)==SOCKET_ERROR)

{

cout<<endl<<"WSAIoctl操作失败:"<<WSAGetLastError()<<endl;

closesocket(sock); //将网卡设为混杂模式,以接受所有数据

WSACleanup();

}

cout<<endl<<"开始解析IP包:"<<endl;

char buffer[65535]; //设置缓冲区

int packsum=atoi(argv[1]); //字符串转换为整形

for(int i=0;i<packsum;i++)

{

if(recv(sock,buffer,65535,0)>0) //四个参数分别是套接字描述符,缓冲区的地址,缓冲区大小,附加标志

{

ip_head ip=*(ip_head *)buffer;

cout<<"-----------------------"<<endl;

cout<<"版本:"<<(ip.Version>>4)<<endl; //获取头部长度字段

cout<<"头部长度:"<<((ip.HeadLen &0x0f)*4)<<endl; //获取头部长度字段

cout<服务类型:Priority"<<(ip.ServiceType>>5)<<", Service"<<((ip.ServiceType>>1)&0x0f)<<endl; //优先级子域和TOS子域

cout<<"总长度:"<<ip.TotalLen<<endl;//获取总长度字段

cout<<"标识符:"<<ip.Identifier<<endl;//获取标识字段

cout<<"标志位:"<<((ip.Flags>>15)&0x01)<<",DF= "<<((ip.Flags>>14)&0x01)<<",Mf="<<((ip.Flags>>13)&0x01)<<endl; //获得标志字段

cout<<"片偏移:"<<(ip.FragOffset&0x1fff)<<endl; //获取分段偏移字段

cout<<"生存周期:"<<(int)ip.TimeToLive<<endl; //获取生存时间字段

cout<<"协议:Protocol"<<(int)ip.Protocol<<endl; //获取协议字段

cout<<"头部校验和:"<<ip.HeadChecksum<<endl; //获取头校验和字段

cout<<"原地址:"<<inet_ntoa(*(in_addr *)&ip.SourceAddr)<<endl; //获取源IP地址字段

cout<<"目的IP地址:"<<inet_ntoa(*(in_addr *)&ip.DestinAddr)<<endl; //获取目的IP地址字段

outfile<<"-----------------------"<<endl;

outfile<<"版本:"<<(ip.Version>>4)<<endl;

outfile<<"头部长度:"<<((ip.HeadLen &0x0f)*4)<<endl;

outfile<<"服务类型:Priority"<<(ip.ServiceType>>5)<<", Service"<<((ip.ServiceType>>1)&0x0f)<<endl;

outfile<<"总长度:"<<ip.TotalLen<<endl;

outfile<<"标识符:"<<ip.Identifier<<endl;

outfile<<"标志位:"<<((ip.Flags>>15)&0x01)<<",DF= "<<((ip.Flags>>14)&0x01)<<",Mf="<<((ip.Flags>>13)&0x01)<<endl;

outfile<<"片偏移:"<<(ip.FragOffset&0x1fff)<<endl;

outfile<<"生存周期:"<<(int)ip.TimeToLive<<endl;

outfile<<"协议:Protocol"<<(int)ip.Protocol<<endl;

outfile<<"头部校验和:"<<ip.HeadChecksum<<endl;

outfile<<"原地址:"<<inet_ntoa(*(in_addr *)&ip.SourceAddr)<<endl;

outfile<<"目的IP地址:"<<inet_ntoa(*(in_addr *)&ip.DestinAddr)<<endl;

}

}

closesocket(sock);

WSACleanup();

}

程序运行结果:

程序编译运行后:

2

以命令行形式运行程序ipparse:

3

同时在程序所在的文件夹中生成了名为logfile的txt文件,里面记录了上面显示的内容。

试验小结:

IP数据报的格式说明了IP协议都具有什么功能,因为完全不知道如何使用套接字socket()函,查阅了相关资料,了解了IP数据报的各种位与协议的概念和意义,通过解析IP数据包这个实验,基本掌握了用套接字编程来实现获取并解析IP数据包的方法。

实验三  发送TCP数据包

实验目的:

?    设计一个发送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_OVERLAPPED);
设置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。如果没有,就添加。
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);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
  

程序流程图:

 

源程序代码:

#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位生存时间 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;

//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))==

SO CKET_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((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;                         //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));

    //填充发送缓冲区

    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;

    }

    else

    printf("send ok!\n");

    closesocket(sock);

    WSACleanup();

    return 1;

}

实验运行结果:

所以进入dos,并进入到该可执行文件的目录下后在命令提示行下输入:sendtcp  192.168.18.3 12 192.168.18.7 25,回车运行,运行截图如下:

QQ截图未命名.png

本实验小结:

在对TCP数据包头部进行填充时,首先需要我们去充分了解它的数据结构,在这个过程中可以了解相应字节上应该存放的内容和它们的功能。由于本次的课程设计只要求填充一个TCP数据包,然后将其发送出去,使用的数据发送函数是sendto(),成功地完成sendto()调用只能保证数据已经从本地发送出去,并不意味着数据传送到达目的地。

课程设计总结

在此次的计算机网络课程设计中,我们一共做了三个程序设计的实验,帧封装、IP数据包解析和发送TCP数据包。

在编写程序的过程中,用到了很多的函数,这些函数的运用使得程序简便而且正确的运行出来。为了正确的实现这些函数,查阅了很多相关的资料,从中获得了大量的有用的信息,收获也颇丰富。

在这次的课程设计中,动手能力得到了很大的提高,而且将这学期所学的网络的知识和以前所学的编程的知识充分的联系起来,对这门课的认识又提高了一层。

相关推荐