忘记打开〈ws2tcpip。
h〉头文件;
问题2:
提示出现内存(ox000000)错误?
分析:
voidDecodeICMPHeader(char*buf,intbytes,SOCKADDR_IN*from)
buf动态指针指向错误。
问题3:
连接时所有有关winsock的库函数连接不上?
分析:
一定是缺少了某个头文件或没打开动态库,解决办法是在开头加上一下代码:
#pragmacomment(lib,"ws2_32.lib");
实验三
1.实验名称:
基于IP多播组的网络会议
2。
实验内容:
了解IP多播的基本原理,参照局域网IP多播程序,设计一个简易的网络会议程序。
3。
实验目的:
1、加深对计算机局域网IP多播工作原理的理解,通过编写计算机程序实现、模拟其功能,使学生理解并掌握其基本原理及工作过程.2、提高网络编程和应用的能力.提高实际编程能力和灵活运用所学知识解决问题的能力。
4.实验要求:
编写一个简易的局域网IP多播网络会议程序,理解IP多播的基本概念和工作原理,程序由Sender和Receiver两个部分组成,Sender用户从控制台上输入多播发送数据,Receiver端都要求加入同一个多播组,完成接收Sender发送的多播数据。
5。
实验原理:
IP多播技术,也常称为组播通信,它是基于IP层的通信技术。
是一种允许一台或多台主机(多播源)发送单一数据包到多台主机(一次的,同时的)的TCP/IP网络技术,是一点对多点的通信。
采用多播通信技术,不仅可以实现一个发送者和多个接收者之间进行通信的功能,而且可以有效减轻网络通信的负担,避免资源的无谓浪费。
并不是所有的协议都支持多播通信,通常多播通信应用都建立在TCP/IP协议之上,IP地址采用D类地址来支持多播,由特殊的多播路由器来实现.
IP多播提供两类服务:
1)向多个目的地址传送数据。
有许多向多个接收者传送信息的应用:
例如交互式会议系统和向多个接收者分发邮件或新闻。
如果不采用多播,目前这些应用大多采用TCP来完成(向每个目的地址传送一个单独的数据复制)。
然而,即使使用多播,某些应用可能继续采用TCP来保证它的可靠性。
2)客户对服务器的请求.例如,无盘工作站需要确定启动引导服务器。
目前,这项服务是通过广播来提供的,但是使用多播可降低不提供这项服务主机的负担。
6.实验过程:
1、启动VisualC++6。
0,创建一个控制台项目工程。
在此项目工程中添加Sender和Receiver两个项目。
Receiver项目实现步骤:
(1)、创建一个SOCK_DGRAM类型的Socket。
(2)、将此Socket绑定到本地的一个端口上,为了接收服务器端发送的多播数据。
(3)、加入多播组。
(4)、接收多播数据。
Sender实现步骤:
(1)、创建一个SOCK_DGRAM类型的Socket.
(2)、加入多播组.
(3)、发送多播数据。
2、编译两个项目,在局域网中按如下步骤测试:
(1)、将Sender。
exe拷贝到发送多播数据的PC上。
(2)、将Receiver。
exe拷贝到多个要求接收多播数据的PC上。
(3)、各自运行相应的程序。
(4)、在SenderPC上输入多播数据后,你就可以在ReceiverPC上看到输入的多播数据.
实验四
1。
实验名称:
网络嗅探器
2。
实验原理:
嗅探器作为一种网络通讯程序,也是通过对网卡的编程来实现网络通讯的,对网卡的编程也是使用通常的套接字(socket)方式来进行。
但是,通常的套接字程序只能响应与自己硬件地址相匹配的或是以广播形式发出的数据帧,对于其他形式的数据帧比如已到达网络接口但却不是发给此地址的数据帧,网络接口在验证投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取到达的数据包。
而网络嗅探器的目的恰恰在于从网卡接收所有经过它的数据包,这些数据包即可以是发给它的也可以是发往别处的。
显然,要达到此目的就不能再让网卡按通常的正常模式工作,而必须将其设置为混杂模式。
具体到编程实现上,这种对网卡混杂模式的设置是通过原始套接字(rawsocket)来实现的,这也有别于通常经常使用的数据流套接字和数据报套接字.在创建了原始套接字后,需要通过setsockopt()函数来设置IP头操作选项,然后再通过bind()函数将原始套接字绑定到本地网卡.为了让原始套接字能接受所有的数据,还需要通过ioctlsocket()来进行设置,而且还可以指定是否亲自处理IP头。
至此,实际就可以开始对网络数据包进行嗅探了,对数据包的获取仍象流式套接字或数据报套接字那样通过recv()函数来完成.但是与其他两种套接字不同的是,原始套接字此时捕获到的数据包并不仅仅是单纯的数据信息,而是包含有IP头、TCP头等信息头的最原始的数据信息,这些信息保留了它在网络传输时的原貌.通过对这些在低层传输的原始信息的分析可以得到有关网络的一些信息。
由于这些数据经过了网络层和传输层的打包,因此需要根据其附加的帧头对数据包进行分析.下面先给出结构.数据包的总体结构:
数据包
IP头TCP头(或其他信息头)数据
数据在从应用层到达传输层时,将添加TCP数据段头,或是UDP数据段头.其中UDP数据段头比较简单,由一个8字节的头和数据部分组成,具体格式如下:
16位16位
源端口目的端口
UDP长度UDP校验和
而TCP数据头则比较复杂,以20个固定字节开始,在固定头后面还可以有一些长度不固定的可选项,下面给出TCP数据段头的格式组成:
16位16位
源端口目的端口
顺序号
确认号
TCP头长(保留)7位URGACKPSHRSTSYNFIN窗口大小
校验和紧急指针
可选项(0或更多的32位字)
数据(可选项)
对于此TCP数据段头的分析在编程实现中可通过数据结构_TCP来定义:
typedefstruct_TCP{WORDSrcPort;//源端口
WORDDstPort;//目的端口
DWORDSeqNum;//顺序号
DWORDAckNum;//确认号
BYTEDataOff;//TCP头长
BYTEFlags;//标志(URG、ACK等)
WORDWindow;//窗口大小
WORDChksum;//校验和
WORDUrgPtr;//紧急指针
}TCP;
typedefTCP*LPTCP;
typedefTCPUNALIGNED*ULPTCP;
在网络层,还要给TCP数据包添加一个IP数据段头以组成IP数据报。
IP数据头以大端点机次序传送,从左到右,版本字段的高位字节先传输(SPARC是大端点机;Pentium是小端点机)。
如果是小端点机,就要在发送和接收时先行转换然后才能进行传输。
IP数据段头格式如下:
16位16位
版本IHL服务类型总长
标识标志分段偏移
生命期协议头校验和
源地址
目的地址
选项(0或更多)
同样,在实际编程中也需要通过一个数据结构来表示此IP数据段头,下面给出此数据结构的定义:
typedefstruct_IP{
union{BYTEVersion;//版本
BYTEHdrLen;//IHL
};
BYTEServiceType;//服务类型
WORDTotalLen;//总长
WORDID;//标识
union{WORDFlags;//标志
WORDFragOff;//分段偏移
};
BYTETimeToLive;//生命期
BYTEProtocol;//协议
WORDHdrChksum;//头校验和
DWORDSrcAddr;//源地址
DWORDDstAddr;//目的地址
BYTEOptions;//选项
}IP;
typedefIP*LPIP;
typedefIPUNALIGNED*ULPIP;
在明确了以上几个数据段头的组成结构后,就可以对捕获到的数据包进行分析了.
嗅探器的具体实现
根据前面的设计思路,不难写出网络嗅探器的实现代码,下面就给出一个简单的示例,该示例可以捕获到所有经过本地网卡的数据包,并可从中分析出协议、IP源地址、IP目标地址、TCP源端口号、TCP目标端口号以及数据包长度等信息.由于前面已经将程序的设计流程讲述的比较清楚了,因此这里就不在赘述了,下面就结合注释对程序的具体是实现进行讲解,同时为程序流程的清晰起见,去掉了错误检查等保护性代码。
主要代码实现清单为:
//检查Winsock版本号,WSAData为WSADATA结构对象
WSAStartup(MAKEWORD(2,2),&WSAData);
//创建原始套接字
sock=socket(AF_INET,SOCK_RAW,IPPROTO_RAW));
//设置IP头操作选项,其中flag设置为ture,亲自对IP头进行处理
setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&flag,sizeof(flag));
//获取本机名
gethostname((char*)LocalName,sizeof(LocalName)-1);
//获取本地IP地址
pHost=gethostbyname((char*)LocalName));
//填充SOCKADDR_IN结构
addr_in.sin_addr=*(in_addr*)pHost->h_addr_list[0];//IP
addr_in.sin_family=AF_INET;
addr_in。
sin_port=htons(57274);
//把原始套接字sock绑定到本地网卡地址上
bind(sock,(PSOCKADDR)&addr_in,sizeof(addr_in));
//dwValue为输入输出参数,为1时执行,0时取消
DWORDdwValue=1;
//设置SOCK_RAW为SIO_RCVALL,以便接收所有的IP包。
其中SIO_RCVALL
//的定义为:
#defineSIO_RCVALL_WSAIOW(IOC_VENDOR,1)
ioctlsocket(sock,SIO_RCVALL,&dwValue);
前面的工作基本上都是对原始套接字进行设置,在将原始套接字设置完毕,使其能按预期目的工作时,就可以了。
3.IP数据报头的结构体:
structipheader{
unsignedcharip_hl:
4;/*headerlength(报头长度)*/
unsignedcharip_v:
4;/*version(版本)*/
unsignedcharip_tos;/*typeosservice服务类型*/
unsignedshortintip_len;/*totallength(总长度)*/
unsignedshortintip_id;/*identification(标识符)*/
unsignedshortintip_off;/*fragmentoffsetfield(段移位域)*/
unsignedcharip_ttl;/*timetolive(生存时间)*/
unsignedcharip_p;/*protocol(协议)*/
unsignedshortintip_sum;/*checksum(校验和)*/
unsignedintip_src;/*sourceaddress(源地址)*/
unsignedintip_dst;/*destinationaddress(目的地址)*/
}/*totalipheaderlength:
20bytes(=160bits)*/
4.TCP报头结构体:
typedefstructtcpheader{
unsignedshortintsport;/*sourceport(源端口号)*/
unsignedshortintdport;/*destinationport(目的端口号)*/
unsignedintth_seq;/*sequencenumber(包的序列号)*/
unsignedintth_ack;/*acknowledgementnumber(确认应答号)*/
unsignedcharth_x:
4;/*unused(未使用)*/
unsignedcharth_off:
4;/*dataoffset(数据偏移量)*/
unsignedcharFlags;/*标志全*/
unsignedshortintth_win;/*windows(窗口)*/
unsignedshortintth_sum;/*checksum(校验和)*/
unsignedshortintth_urp;/*urgentpointer(紧急指针)*/
}TCP_HDR;
5.UDP报头结构体:
typedefstructudphdr{
unsignedshortsport;/*sourceport(源端口号)*/
unsignedshortdport;/*destinationport(目的端口号)*/
unsignedshortlen;/*udplength(udp长度)*/
unsignedshortcksum;/*udpchecksum(udp校验和)*/
}UDP_HDR;
5。
变量
SOCKETsock;/*进行网络通信的套接字*/
SOCKETsocket(intaf,inttype,intprotocol);
应用程序调用socket函数来创建一个能够进行网络通信的套接字.*第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;
第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;
第三个参数指定应用程序所使用的通信协议.
该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。
套接字描述符
是一个整数类型的值.每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。
该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。
每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。
WSADATAwsd;
存储被WSAstartup函数调用后返回的Windowssockets数据,即存放windowssocket初始化信息
DWORDdwBytesRet;32bit的无符号整数
unsignedintoptval=1;
unsignedchar*dataudp,*datatcp;
inti,pCount=0,lentcp,lenudp;
SOCKADDR_INsa,saSource,saDest;用来指定ip地址和端口信息
structhostentFAR*pHostent;主机指针
charFARname[MAX_HOSTNAME_LAN];
charszSourceIP[MAX_ADDR_LEN],szDestIP[MAX_ADDR_LEN],RecvBuf[65535]={0};
structudphdr*pUdpheader;
structipheader*pIpheader;
structtcpheader*pTcpheader;
WSAStartup(MAKEWORD(2,1),&wsd);
MAKEWORD(2,1)创建一个被指针变量连接而成的word变量
6。
过滤规则:
if((sock=socket(AF_INET,SOCK_RAW,IPPROTO_IP))==SOCKET_ERROR)
exit
(1);
gethostname(name,MAX_HOSTNAME_LAN);
pHostent=gethostbyname(name);
sa。
sin_family=AF_INET;
sa。
sin_port=htons(6000);
memcpy(&sa。
sin_addr.S_un。
S_addr,pHostent—〉h_addr_list[0],pHostent-〉h_length);
bind(