计算机网络课程设计指导书11计算机网络工程Word格式.docx
《计算机网络课程设计指导书11计算机网络工程Word格式.docx》由会员分享,可在线阅读,更多相关《计算机网络课程设计指导书11计算机网络工程Word格式.docx(33页珍藏版)》请在冰豆网上搜索。
如图1所示的QuickPing程序。
图1QuickPing运行界面效果图
3、基于IP多播的网络会议程序
参照附录3的局域网IP多播程序,设计一个图形界面的网络会议程序(实现文本多播方式即可)。
4、网络嗅探器的设计与实现
参照附录4rawsocket编程例子,设计一个可以监视网络的状态、数据流动情况以及网络上传输的信息的网络嗅探器。
5、电子邮件客户端程序设计与实现
参照教材6.5节原理,设计一个电子邮件客户端程序。
6、TELNET终端设计与实现
参照RFC854、RFC855文档,设计一个TELNET终端程序。
7、网络代理服务器的设计与实现
实现一个简易的proxy程序。
proxy程序的功能:
能够做“二传手”的工作。
它自身处在能同时连通外界目标服务器和我的机器的位置上。
我的机器把请求发送给它,它接受请求,把请求原封不动的抄下来发送给外界目标服务器;
外界目标服务器响应了请求,把回答发送给它,它再接受回答,把回答原封不动的抄下来发送给我的机器。
这样,我的机器实际上是把它当作了目标服务器(由于是原封不动的转抄,请求和回答没有被修改)。
而它则是外界目标服务器的客户端。
四、推荐参考文献
[1]谢希仁,计算机网络(第五版),电子工业出版社,2008.
[2]AndrewS.Tanenbaum.计算机网络(第四版)[M].北京:
清华大学出版社,2004.
[3]中国Linux论坛:
[4]UNIX技术网站(永远的UNIX):
[5]Google
[6]蒋清明,C语言程序设计,人民邮电出版社,2008.
[7]陈维兴,林小茶,C++面向对象程序设计(第二版),中国铁道出版社,2009年12月
五、附录
附录1:
WindowsSocket编程简介
附录2:
PING源代码
附录3:
用VisualC++实现局域网IP多播
附录4:
rawsocket编程例子(基于LINUX操作系统)
附录5:
课程设计报告封面设计
附录1、WindowsSocket编程简介
使用WinSockAPI的编程,应该了解TCP/IP的基础知识。
虽然你可以直接使用WinSockAPI来写网络应用程序,但是,要写出优秀的网络应用程序,还是必须对TCP/IP协议有一些了解的。
1.TCP/IP协议与WinSock网络编程接口的关系
WinSock并不是一种网络协议,它只是一个网络编程接口,也就是说,它不是协议,但是它可以访问很多种网络协议,你可以把它当作一些协议的封装。
现在的WinSock已经基本上实现了与协议无关。
你可以使用WinSock来调用多种协议的功能。
那么,WinSock和TCP/IP协议到底是什么关系呢?
实际上,WinSock就是TCP/IP协议的一种封装,你可以通过调用WinSock的接口函数来调用TCP/IP的各种功能.例如我想用TCP/IP协议发送数据,你就可以使用WinSock的接口函数Send()来调用TCP/IP的发送数据功能,至于具体怎么发送数据,WinSock已经帮你封装好了这种功能。
2、TCP/IP协议介绍
TCP/IP协议包含的范围非常的广,它是一种四层协议,包含了各种硬件、软件需求的定义。
TCP/IP协议确切的说法应该是TCP/UDP/IP协议。
UDP协议(UserDatagramProtocol用户数据报协议),是一种保护消息边界的,不保障可靠数据的传输。
TCP协议(TransmissionControlProtocol传输控制协议),是一种流传输的协议。
他提供可靠的、有序的、双向的、面向连接的传输。
保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息。
也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。
而面向流则是指无保护消息边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。
举例来说,假如,我们连续发送三个数据包,大小分别是2k、4k、8k,这三个数据包都已经到达了接收端的网络堆栈中,如果使用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有三次接收动作,才能够把所有的数据包接收完。
而使用TCP协议,我们只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的数据包接收下来,只需要有一次接收动作。
这就是因为UDP协议的保护消息边界使得每一个消息都是独立的。
而流传输,却把数据当作一串数据流,它不认为数据是一个一个的消息。
所以有很多人在使用TCP协议通讯的时候,并不清楚TCP是基于流的传输,当连续发送数据的时候,他们时常会认识TCP会丢包。
其实不然,因为当它们使用的缓冲区足够大时,它们有可能会一次接收到两个甚至更多的数据包,而很多人往往会忽视这一点,只解析检查了第一个数据包,而已经接收的其它据包却被忽略了。
3.WinSock编程简单流程
WinSock编程分为服务器端和客户端两部分,TCP服务器端的大体流程如下:
对于任何基于WinSock的编程首先必须要初始化WinSockDLL库。
intWSAStarup(WORDwVersionRequested,LPWSADATAlpWsAData)。
wVersionRequested是我们要求使用的WinSock的版本。
调用这个接口函数可以初始化WinSock。
然后必须创建一个套接字(Socket)。
SOCKETSocket(intaf,inttype,intprotocol);
套接字可以说是WinSock通讯的核心。
WinSock通讯的所有数据传输,都是通过套接字来完成的,套接字包含了两个信息,一个是IP地址,一个是Port端口号,使用这两个信息,就可以确定网络中的任何一个通讯节点。
当调用了Socket()接口函数创建了一个套接字后,必须把套接字与你需要进行通讯的地址建立联系,可以通过绑定函数bind来实现这种联系。
intbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);
structsockaddr_in{
shortsin_family;
u_shortsin_port;
structin_addrsin_addr;
charsin_sero[8];
}
就包含了需要建立连接的本地的地址,包括地址族、IP和端口信息。
sin_family字段必须把它设为AF_INET,这是告诉WinSock使用的是IP地址族。
sin_port就是要用来通讯的端口号。
sin_addr就是要用来通讯的IP地址信息。
在这里,必须还得提一下有关'
大头(big-endian)'
小头(little-endian)'
。
因为各种不同的计算机处理数据时的方法是不一样的,IntelX86处理器上是用'
小头'
形式来表示多字节的编号,就是把低字节放在前面,把高字节放在后面,而互联网标准却正好相反,所以,必须把主机字节转换成网络字节的顺序。
WinSockAPI提供了几个函数。
把主机字节转化成网络字节的函数;
u_longhtonl(u_longhostlong);
u_shorthtons(u_shorthostshort);
把网络字节转化成主机字节的函数;
u_longntohl(u_longnetlong);
u_shortntohs(u_shortnetshort);
这样,设置IP地址和port端口时,就必须把主机字节转化成网络字节后,才能用Bind()函数来绑定套接字和地址。
当绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。
intlisten(SOCKETs,intbacklog);
这个函数可以把套接字转成监听模式。
如果客户端有了连接请求,我们还必须使用
intaccept(SOCKETs,structsockaddrFAR*addr,intFAR*addrlen);
来接受客户端的请求。
现在基本上已经完成了一个服务器的建立,而客户端的建立的流程则是初始化WinSock,然后创建Socket套接字,再使用
intconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);
来连接服务端。
下面是一个最简单的创建服务器端和客户端的例子:
服务器端的创建:
WSADATAwsd;
SOCKETsListen;
SOCKETsclient;
UINTport=800;
intiAddrSize;
structsockaddr_inlocal,client;
WSAStartup(0x11,&
wsd);
sListen=Socket(AF_INET,SOCK_STREAM,IPPOTO_IP);
local.sin_family=AF_INET;
local.sin_addr=htonl(INADDR_ANY);
local.sin_port=htons(port);
bind(sListen,(structsockaddr*)&
local,sizeof(local));
listen(sListen,5);
sClient=accept(sListen,(structsockaddr*)&
client,&
iAddrSize);
客户端的创建:
SOCKETsClient;
charszIp[]="
127.0.0.1"
;
structsockaddr_inserver;
sClient=Socket(AF_INET,SOCK_STREAM,IPPOTO_IP);
server.sin_family=AF_INET;
server.sin_addr=inet_addr(szIp);
server.sin_port=htons(port);
connect(sClient,(structsockaddr*)&
server,sizeof(server));
当服务器端和客户端建立连接以后,无论是客户端,还是服务器端都可以使用
intsend(SOCKETs,constcharFAR*buf,intlen,intflags);
intrecv(SOCKETs,charFAR*buf,intlen,intflags);
函数来接收和发送数据,因为,TCP连接是双向的。
当要关闭通讯连接的时候,任何一方都可以调用
intshutdown(SOCKETs,inthow);
来关闭套接字的指定功能,再调用
intcloseSocket(SOCKETs);
来关闭套接字句柄,这样一个通讯过程就算完成了。
计算机网络(第5版)280页图6-32所示的系统调用使用顺序:
注意:
上面的代码没有任何检查函数返回值,如果你作网络编程就一定要检查任何一个WinSockAPI函数的调用结果,因为很多时候函数调用并不一定成功。
上面介绍的函数,返回值类型是int的话,如果函数调用失败的话,返回的都是SOCKET_ERROR。
4.VC中socket编程步骤
sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);
基于TCP的socket编程是采用的流式套接字。
在这个程序中,将两个工程添加到一个工作区。
要链接一个ws2_32.lib的库文件。
服务器端编程的步骤:
1:
加载套接字库,创建套接字(WSAStartup()/socket());
2:
绑定套接字到一个IP地址和一个端口上(bind());
3:
将套接字设置为监听模式等待连接请求(listen());
4:
请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());
5:
用返回的套接字和客户端进行通信(send()/recv());
6:
返回,等待另一连接请求;
7:
关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
服务器端代码如下:
#include<
stdio.h>
Winsock2.h>
voidmain()
{
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested=MAKEWORD(1,1);
err=WSAStartup(wVersionRequested,&
wsaData);
if(err!
=0){
return;
}
if(LOBYTE(wsaData.wVersion)!
=1||HIBYTE(wsaData.wVersion)!
=1){
WSACleanup();
SOCKETsockSrv=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_INaddrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
bind(sockSrv,(SOCKADDR*)&
addrSrv,sizeof(SOCKADDR));
listen(sockSrv,5);
SOCKADDR_INaddrClient;
intlen=sizeof(SOCKADDR);
while
(1){
SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&
addrClient,&
len);
charsendBuf[50];
sprintf(sendBuf,"
Welcome%stohere!
"
inet_ntoa(addrClient.sin_addr));
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
charrecvBuf[50];
recv(sockConn,recvBuf,50,0);
printf("
%s\n"
recvBuf);
closesocket(sockConn);
客户端编程的步骤:
向服务器发出连接请求(connect());
和服务器端进行通信(send()/recv());
客户端的代码如下:
SOCKETsockClient=socket(AF_INET,SOCK_STREAM,0);
addrSrv.sin_addr.S_un.S_addr=inet_addr("
);
connect(sockClient,(SOCKADDR*)&
send(sockClient,"
hello"
strlen("
)+1,0);
charrecvBuf[50];
recv(sockClient,recvBuf,50,0);
printf("
closesocket(sockClient);
WSACleanup();
附录2、PING源代码
//PING.H
#defineWIN32_LEAN_AND_MEAN
windows.h>
winsock2.h>
ws2tcpip.h>
stdlib.h>
typedefstructtagIP_HEADER{
unsignedinth_len:
4;
//lengthofheader(4b)
unsignedintver:
//version(4b)
unsignedchartos;
//tos(1B)
unsignedshorttotal_len;
//totallength(2B)
unsignedshortident;
//identification(2B)
unsignedshortfrag_flags;
//fragandflags(2B)
unsignedcharttl;
//timeoflives(1B)
unsignedcharprotocol;
//protocol(1B)
unsignedshortchecksum;
//checksum(2B)
unsignedintsourceip;
//sourceip(4B)
unsignedintdestip;
//destinationip(4B)
}IP_HEADER,*PIP_HEADER;
//lengthoftotal(20B)
typedefstructtagIP_OPT_HEADER{
unsignedcharcode;
//optiontype(1B)
unsignedcharlen;
//lengthofoptionheader(1B)
unsignedcharptr;
//下一个可存放地址的位置(1B)
unsignedlongaddr[9];
//listofipaddress(4B/d)
}IP_OPT_HEADER,*PIP_OPT_HEADER;
//lengthoftotal(39B)
typedefstructtagICMP_HEADER{
unsignedchartype;
//icmptype(1B)
//codeoftype(1B)
unsignedshortid;
//identification(2B)
unsignedshortseq;
//sequence(2B)
unsignedlongtimestamp;
//(2B)
//thisisnotstandardheader,butwereservespacefortime
}ICMP_HEADER,*PICMP_HEADER;
//totallength(10B)
#defineDEF_PACKET_SIZE32
#defineMAX_PACKET_SIZE1024
#defineICMP_ECHO8
#defineICMP_ECHOREPLY0
#defineIP_RECORD_ROUTER7
voidusageinfo(char*progname);
voidFillIcmpData(char*icmp_data,intsize);
USHORTCheckSum(USHORT*buf,intsize);
voidDecodeIcmpHeade