计算机网络课程设计指导书_11计算机_网络工程

时间:2024.4.20

计 算 机 网 络

课程设计指导书

(11级计算机、网络工程专业)

湖南科技大学计算机科学与工程学院

20##1


一、课程设计目的

1.加深对计算机网络通信系统的工作原理的理解

通过编写计算机程序实现、模拟网络的某些功能,使学生理解并掌握计算机网络的基本工作原理及工作过程。

2.实现应用进程跨越网络的通信

了解系统调用和应用编程接口基本知识,理解应用程序和操作系统之间传递控制权的机制,掌握套接字的创建和运用,通过socket系统调用实现跨网通信。

3.提高网络编程和应用的能力

提高实际编程能力和灵活运用所学知识解决问题的能力。培养调查研究、查阅技术文献、资料、手册以及编写技术文档的能力,理论应用于实践的能力。

二、课程设计要求

1. 仔细分析每一个实验的具体内容、步骤和要求,按设计要求完成任务。

2. 使用C和C++语言,用基础SOCKET编程方法实现设计功能。

3. 程序设计需要完整的程序流程图、说明文档和源程序清单,设计者需要清楚每个模块的功能和原理。

4. 完成程序的编写、编译、执行和测试,每人至少完成三个题目的设计工作。

5. 提交课程设计报告(含括课程设计名称、课程设计题目、课程设计内容、课程设计步骤、调试过程、课程设计结果及结果分析、心得体会)、程序源文件、可执行文件各一份。

三、课程设计考核方式

1.     测试中演示所设计的程序,占总成绩30%;

2.     测试中回答指导老师所提出的问题,占总成绩20%;

3.     设计报告,占总成绩40%;

4.     考勤情况,占总成绩10%。

三、课程设计题目

1、网络聊天程序的设计与实现

参照附录1,了解Socket通信的原理,在此基础上编写一个聊天程序。

2、Ping程序设计与实现

参照附录2,了解Ping程序的实现原理,并调试通过。如有可能则在此基础上,编写一个可以测试本局域网的所有机器是否在线的程序。如图1所示的QuickPing程序。

图1 QuickPing运行界面效果图

3、基于IP多播的网络会议程序

参照附录3的局域网IP多播程序,设计一个图形界面的网络会议程序(实现文本多播方式即可)。

4、网络嗅探器的设计与实现

参照附录4 raw socket编程例子,设计一个可以监视网络的状态、数据流动情况以及网络上传输的信息的网络嗅探器。

5、电子邮件客户端程序设计与实现

参照教材6.5节原理,设计一个电子邮件客户端程序。

6、TELNET终端设计与实现

参照RFC854、RFC855文档,设计一个TELNET终端程序。

7、网络代理服务器的设计与实现

实现一个简易的proxy程序。proxy程序的功能:能够做“二传手”的工作。它自身处在能同时连通外界目标服务器和我的机器的位置上。我的机器把请求发送给它,它接受请求,把请求原封不动的抄下来发送给外界目标服务器;外界目标服务器响应了请求,把回答发送给它,它再接受回答,把回答原封不动的抄下来发送给我的机器。这样,我的机器实际上是把它当作了目标服务器(由于是原封不动的转抄,请求和回答没有被修改)。而它则是外界目标服务器的客户端。

四、推荐参考文献

[1] 谢希仁,计算机网络(第五版),电子工业出版社,2008.

[2] Andrew S.Tanenbaum.计算机网络(第四版)[M].北京:清华大学出版社,2004.

[3] 中国Linux论坛:http://www.linuxforum.net/

[4] UNIX技术网站(永远的UNIX):http://fanqiang.chinaunix.net/

[5] Google  http://www.google.cn/

[6] 蒋清明,C语言程序设计,人民邮电出版社,2008.

[7] 陈维兴,林小茶, C++面向对象程序设计(第二版), 中国铁道出版社, 20##年12月

五、附录

附录1:Windows Socket编程简介

附录2:PING源代码

附录3:用Visual C++实现局域网IP多播

附录4:raw socket编程例子(基于LINUX操作系统)

附录5:课程设计报告封面设计


附录1、Windows Socket编程简介

使用WinSock API的编程,应该了解TCP/IP的基础知识。虽然你可以直接使用WinSock API来写网络应用程序,但是,要写出优秀的网络应用程序,还是必须对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协议(User Datagram Protocol 用户数据报协议),是一种保护消息边界的,不保障可靠数据的传输。TCP协议(Transmission Control Protocol 传输控制协议),是一种流传输的协议。他提供可靠的、有序的、双向的、面向连接的传输。

保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。

而面向流则是指无保护消息边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。

举例来说,假如,我们连续发送三个数据包,大小分别是2k、4k、8k,这三个数据包都已经到达了接收端的网络堆栈中,如果使用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有三次接收动作,才能够把所有的数据包接收完。而使用TCP协议,我们只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的数据包接收下来,只需要有一次接收动作。

这就是因为UDP协议的保护消息边界使得每一个消息都是独立的。而流传输,却把数据当作一串数据流,它不认为数据是一个一个的消息。所以有很多人在使用TCP协议通讯的时候,并不清楚TCP是基于流的传输,当连续发送数据的时候,他们时常会认识TCP会丢包。其实不然,因为当它们使用的缓冲区足够大时,它们有可能会一次接收到两个甚至更多的数据包,而很多人往往会忽视这一点,只解析检查了第一个数据包,而已经接收的其它据包却被忽略了。

3.WinSock编程简单流程

WinSock编程分为服务器端和客户端两部分,TCP服务器端的大体流程如下:

对于任何基于WinSock的编程首先必须要初始化WinSock DLL库。

int WSAStarup( WORD wVersionRequested,LPWSADATA lpWsAData )。

wVersionRequested是我们要求使用的WinSock的版本。

调用这个接口函数可以初始化WinSock 。

然后必须创建一个套接字(Socket)。

SOCKET Socket(int af,int type,int protocol);

套接字可以说是WinSock通讯的核心。WinSock通讯的所有数据传输,都是通过套接字来完成的,套接字包含了两个信息,一个是IP地址,一个是Port端口号,使用这两个信息,就可以确定网络中的任何一个通讯节点。

当调用了Socket()接口函数创建了一个套接字后,必须把套接字与你需要进行通讯的地址建立联系,可以通过绑定函数bind来实现这种联系。

int bind(SOCKET s,const struct sockaddr FAR* name,int namelen) ;

struct sockaddr_in{

short sin_family ;

u_short sin_port;

struct in_addr sin_addr ;

char sin_sero[8] ;

}

就包含了需要建立连接的本地的地址,包括地址族、IP和端口信息。sin_family字段必须把它设为AF_INET,这是告诉WinSock使用的是IP地址族。sin_port就是要用来通讯的端口号。sin_addr就是要用来通讯的IP地址信息。

在这里,必须还得提一下有关'大头(big-endian)'小头(little-endian)'。因为各种不同的计算机处理数据时的方法是不一样的,Intel X86处理器上是用'小头'形式来表示多字节的编号,就是把低字节放在前面,把高字节放在后面,而互联网标准却正好相反,所以,必须把主机字节转换成网络字节的顺序。WinSock API提供了几个函数。

把主机字节转化成网络字节的函数;

u_long htonl(u_long hostlong);

u_short htons(u_short hostshort);

把网络字节转化成主机字节的函数;

u_long ntohl(u_long netlong);

u_short ntohs(u_short netshort) ;

这样,设置IP地址和port端口时,就必须把主机字节转化成网络字节后,才能用Bind()函数来绑定套接字和地址。

当绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。

int listen(SOCKET s,int backlog);

这个函数可以把套接字转成监听模式。

如果客户端有了连接请求,我们还必须使用

int accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

来接受客户端的请求。

现在基本上已经完成了一个服务器的建立,而客户端的建立的流程则是初始化WinSock,然后创建Socket套接字,再使用

int connect(SOCKET s,const struct sockaddr FAR* name,int namelen) ;

来连接服务端。

下面是一个最简单的创建服务器端和客户端的例子:

服务器端的创建:

WSADATA wsd;

SOCKET sListen;

SOCKET sclient;

UINT port = 800;

int iAddrSize;

struct sockaddr_in local , 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 , (struct sockaddr*)&local , sizeof( local ) );

listen( sListen , 5 );

sClient = accept( sListen , (struct sockaddr*)&client , &iAddrSize );

客户端的创建:

WSADATA wsd;

SOCKET sClient;

UINT port = 800;

char szIp[] = "127.0.0.1";

int iAddrSize;

struct sockaddr_in server;

WSAStartup( 0x11 , &wsd );

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 , (struct sockaddr*)&server , sizeof( server ) );

当服务器端和客户端建立连接以后,无论是客户端,还是服务器端都可以使用

int send( SOCKET s,const char FAR* buf,int len,int flags);

int recv( SOCKET s,char FAR* buf,int len,int flags);

函数来接收和发送数据,因为,TCP连接是双向的。

当要关闭通讯连接的时候,任何一方都可以调用

int shutdown(SOCKET s,int how);

来关闭套接字的指定功能,再调用

int closeSocket(SOCKET s) ;

来关闭套接字句柄,这样一个通讯过程就算完成了。

计算机网络(第5版)280页图6-32所示的系统调用使用顺序:

注意:上面的代码没有任何检查函数返回值,如果你作网络编程就一定要检查任何一个WinSock API函数的调用结果,因为很多时候函数调用并不一定成功。上面介绍的函数,返回值类型是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>

#include <Winsock2.h>

void main()

{

       WORD wVersionRequested;

       WSADATA wsaData;

       int err;

      

       wVersionRequested = MAKEWORD( 1, 1 );

      

       err = WSAStartup( wVersionRequested, &wsaData );

       if ( err != 0 ) {

              return;

       }

      

       if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {

              WSACleanup( );

              return;

       }

       SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

      

       SOCKADDR_IN addrSrv;

       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_IN addrClient;

       int len=sizeof(SOCKADDR);

       while(1){

              SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);

              char sendBuf[50];

              sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));

              send(sockConn,sendBuf,strlen(sendBuf)+1,0);

              char recvBuf[50];

              recv(sockConn,recvBuf,50,0);

              printf("%s\n",recvBuf);

              closesocket(sockConn);

       }

}

客户端编程的步骤:

1:加载套接字库,创建套接字(WSAStartup()/socket());

2:向服务器发出连接请求(connect());

3:和服务器端进行通信(send()/recv());

4:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

客户端的代码如下:

#include <stdio.h>

#include <Winsock2.h>

void main()

{

       WORD wVersionRequested;

       WSADATA wsaData;

       int err;

      

       wVersionRequested = MAKEWORD( 1, 1 );

      

       err = WSAStartup( wVersionRequested, &wsaData );

       if ( err != 0 ) {

              return;

       }

       if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {

              WSACleanup( );

              return;

       }

       SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);

      

       SOCKADDR_IN addrSrv;

       addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

       addrSrv.sin_family=AF_INET;

       addrSrv.sin_port=htons(6000);

       connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

       send(sockClient,"hello",strlen("hello")+1,0);

       char recvBuf[50];

       recv(sockClient,recvBuf,50,0);

       printf("%s\n",recvBuf);

       closesocket(sockClient);

       WSACleanup();

}


附录2、PING源代码

// PING.H

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#include <winsock2.h>

#include <ws2tcpip.h>

#include <stdio.h>

#include <stdlib.h>

typedef struct tagIP_HEADER{

       unsigned int    h_len:4;                        //length of header   (4b)

       unsigned int    ver:4;                                  //version                (4b)

       unsigned char  tos;                              //tos                      (1B)

       unsigned short total_len;                      //total length          (2B)

       unsigned short ident;                                  //identification (2B)

       unsigned short frag_flags;                           //frag and flags       (2B)

       unsigned char  ttl;                               //time of lives        (1B)

       unsigned char  protocol;                      //protocol              (1B)

       unsigned short checksum;                           //checksum                   (2B)

       unsigned int    sourceip;                      //source ip                    (4B)

       unsigned int    destip;                                 //destination ip       (4B)

}IP_HEADER,*PIP_HEADER;                                      //length of total      (20B)

typedef struct tagIP_OPT_HEADER{

       unsigned char code;                     //option type                               (1B)

       unsigned char len;                //length of option header              (1B)

       unsigned char ptr;                 //下一个可存放地址的位置         (1B)

       unsigned long addr[9];          //list of ip address                 (4B/d)

}IP_OPT_HEADER,*PIP_OPT_HEADER; //length of total                           (39B)

typedef struct tagICMP_HEADER{

       unsigned char  type;                                   //icmp type                   (1B)

       unsigned char  code;                                   //code of type         (1B)

       unsigned short checksum;                           //checksum                   (2B)

       unsigned short id;                                      //identification (2B)

       unsigned short seq;                              //sequence                     (2B)

       unsigned long timestamp;                           //                                 (2B)

                                   //this is not standard header,but we reserve space for time

}ICMP_HEADER,*PICMP_HEADER;                            //total length          (10B)

#define           DEF_PACKET_SIZE           32

#define           MAX_PACKET_SIZE          1024

#define           ICMP_ECHO                   8

#define           ICMP_ECHOREPLY            0

#define           IP_RECORD_ROUTER       7

void usageinfo(char *progname);

void FillIcmpData(char *icmp_data,int size);

USHORT CheckSum(USHORT *buf,int size);

void DecodeIcmpHeader(char *buf,int ret,LPSOCKADDR_IN lpSin);

void DecodeIpHeader(char *buf,int bytes);

//PING.CPP

#include "Ping.h"

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

{                   //error:return -1,no err:return 0

       if(argc==1)

       {

              usageinfo(argv[0]);

              return -1;

       }

       BOOL                   bRecordRout          =FALSE;

       SOCKET               hSocket                 =INVALID_SOCKET;

       SOCKADDR_IN           dstSin;

       SOCKADDR_IN           fromSin;

       IP_OPT_HEADER ipOptHeader;

       char*                    pIcmpData             =NULL;

       char*                    pRecvData             =NULL;

       char*                    lpDstIp                  =NULL;

       int                        datasize          =DEF_PACKET_SIZE;

       int                        ret;

    int              rcvNum;

      

       for(int i=1;i<argc;i++)

       {

              if(strchr(argv[i],'-')){

                     switch(tolower(argv[i][1])){

                     case 'r':

                            bRecordRout=TRUE;

                            break;

                     case 'd':

                            datasize=atoi(argv[i+1]);

                            i=argc+1;              // to quit the for loop

                            break;    

                     }

              }

              else if(strchr(argv[i],'.')){

                     int l=strlen(argv[i]);

                     if(l<7||l>15)

                            usageinfo(argv[0]);

                     else

                            lpDstIp=argv[i];

              }

       }

      

       //initialize winsock

       //

       WSADATA wsaData;

       WORD wVer=MAKEWORD(2,2);

       if(WSAStartup(wVer,&wsaData)!=0){

              printf("WSAStartup Error!\n");

              return -1;                      //err,return -1,

       }

       //create socket handle

       //

       hSocket=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);

       if(hSocket==INVALID_SOCKET)

       {

              printf("WSASocket Error,Code:%d",WSAGetLastError());

              WSACleanup();

              return -1;

       }

       //set ip option header(record router)

       if(bRecordRout)

       {

              ZeroMemory(&ipOptHeader,sizeof(ipOptHeader));

              ipOptHeader.code=IP_RECORD_ROUTER;

              ipOptHeader.len=39;

              ipOptHeader.ptr=4;

              if((ret=setsockopt(hSocket,IPPROTO_IP,IP_OPTIONS,(char *)&ipOptHeader,

                     sizeof(ipOptHeader)))==SOCKET_ERROR){

                            printf("setsockopt(IP_OPTIONS) error,code:%d",WSAGetLastError());

                            WSACleanup();

                            closesocket(hSocket);

                            return -1;

                     }

       }

       //set socket recv and send timeout;

       int timeout=1000;

       if((ret=setsockopt(hSocket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout)))==SOCKET_ERROR)

       {

              printf("setsockopt(SO_RCVTIMEO) error,code:%d",WSAGetLastError());

              WSACleanup();

              closesocket(hSocket);

              return -1;

       }

       if((ret=setsockopt(hSocket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout)))==SOCKET_ERROR){

              printf("setsockopt(SO_SNDTIMEO) error,code:%d",WSAGetLastError());

              WSACleanup();

              return -1;

       }

      

       pIcmpData=(char *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET_SIZE);

       pRecvData=(char *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET_SIZE);

       if(pIcmpData==NULL||pRecvData==NULL)

       {

              printf("HeapAlloc Error\n");

              WSACleanup();

              return -1;

       }

       datasize+=sizeof(ICMP_HEADER);

       ZeroMemory(&dstSin,sizeof(dstSin));

       dstSin.sin_family=AF_INET;

       dstSin.sin_addr.s_addr=inet_addr(lpDstIp);

       FillIcmpData(pIcmpData,datasize);

       printf("Ping %s with %d bytes of data\n",inet_ntoa(dstSin.sin_addr),datasize);

       int count=0;

       int seq=0;

       int rcvNum=0;

       while(1)

       {

              count++;

              if(count==5)

                     break;

              ((PICMP_HEADER)pIcmpData)->checksum=0;

              ((PICMP_HEADER)pIcmpData)->seq=seq++;

              ((PICMP_HEADER)pIcmpData)->timestamp=GetTickCount();

              ((PICMP_HEADER)pIcmpData)->checksum=

                     CheckSum((USHORT*)pIcmpData,datasize);

              if((ret=sendto(hSocket,pIcmpData,datasize,0,(LPSOCKADDR)&dstSin,sizeof(dstSin)))==SOCKET_ERROR){

                     if(WSAGetLastError()==WSAETIMEDOUT){

                            printf("time out.\n");

                            continue;

                     }

                     else{

                            printf("sendto error,code:%d",WSAGetLastError());

                            closesocket(hSocket);

                            WSACleanup();

                            return -1;

                     }

              }

              int fromLen=sizeof(fromSin);

              if((ret=recvfrom(hSocket,pRecvData,MAX_PACKET_SIZE,0,(sockaddr *)&fromSin,

                     &fromLen))==SOCKET_ERROR){

                     if(WSAGetLastError()==WSAETIMEDOUT)

                     {

                            printf("time out.\n");

                            continue;

                     }

                     printf("recvform fail!\n");

                     closesocket(hSocket);

                     WSACleanup();

                     return -1;

              }

              rcvNum++;

              DecodeIcmpHeader(pRecvData,ret,&fromSin);

       }

      

       printf("\n Ping Statistics for : %s\n",lpDstIp);

       printf("\t Send= %d, Received= %d,Lost= %d (%d%% loss)",4,rcvNum,4-rcvNum,(4-rcvNum)/4*100);

      

       if(hSocket!=INVALID_SOCKET)

              closesocket(hSocket);

       HeapFree(GetProcessHeap(),0,pIcmpData);

       HeapFree(GetProcessHeap(),0,pRecvData);

       WSACleanup();

       return 0;

}

void usageinfo(char *progname)

{

       printf("Ping tool,by blode(blode@peoplemail.com.cn\n");

       printf("usage:ping [-r] <host ip> [-d ][data size]\n");

       printf("\t-r:\trecord router\n");

       printf("\thost ip:\thost ip to ping\n");

       printf("\t-d:\tuse data size option\n");

       printf("\tdata size:\tdata size to ping(<=1024)\n");

}

void FillIcmpData(char *icmp_data,int size)

{

       ICMP_HEADER *icmpHdr;

       icmpHdr=(PICMP_HEADER)icmp_data;

       icmpHdr->checksum=0;

       icmpHdr->code=0;

       icmpHdr->id=(unsigned short)GetCurrentProcessId();

       icmpHdr->seq=0;

       icmpHdr->type=ICMP_ECHO;

       icmpHdr->timestamp=0;

}

USHORT CheckSum(USHORT *buf,int size)

{                   //check sum function

       USHORT cksum=0;

       while(size>1)

       {

              cksum+=*buf++;

              size-=sizeof(USHORT);

       }

       if(size)

              cksum+=*buf++;

       cksum=(cksum>>16)+(cksum&0xffff);

       cksum+=(cksum>>16);

       return (USHORT)(~cksum);

}

void DecodeIcmpHeader(char *buf,int ret,LPSOCKADDR_IN lpSin)

{

       ICMP_HEADER *icmpHdr;

       IP_HEADER  *ipHdr;

       int ipHdrLen;

       static int first=0;

       DWORD tick=GetTickCount();

       ipHdr=(IP_HEADER*)buf;

       ipHdrLen=ipHdr->h_len*4;

       if(ipHdrLen==60&&!first)

              DecodeIpHeader(buf,ret);

       icmpHdr=(ICMP_HEADER *)(buf+ipHdrLen);

       if(icmpHdr->type!=ICMP_ECHOREPLY){

              printf("no echo reply %d recved\n",icmpHdr->type);

              return;

       }

       if(icmpHdr->id!=(USHORT)GetCurrentProcessId()){

              printf("someone else's packet!\n");

              return;

       }

       printf("Reply from: %s",inet_ntoa(lpSin->sin_addr));

       printf("\tbytes: %d icmp seq: %d TTL=128",ret,icmpHdr->seq);

       printf(" time: %dms\n",tick-icmpHdr->timestamp);

       first++;

      

       return;

}

void DecodeIpHeader(char *buf,int bytes)

{

       IP_OPT_HEADER *ipOptHdr;   

       IN_ADDR in;

       ipOptHdr=(IP_OPT_HEADER*)(buf+20);

       printf("Record Router: ");

       for(int i=0;i<(ipOptHdr->ptr/4)-1;i++){

              in.S_un.S_addr=ipOptHdr->addr[i];

              printf("\t%-15s\n",inet_ntoa(in));

       }

}


附录3 用Visual C++实现局域网IP多播

在局域网中,管理员常常需要将某条信息发送给一组用户。如果使用一对一的发送方法,虽然是可行的,但是过于麻烦,也常会出现漏发、错发。为了更有效的解决这种组通信问题,出现了一种多播技术(也常称为组播通信),它是基于IP层的通信技术。为了帮助读者理解,下面将简要的介绍一下多播的概念。

众所周知,普通IP通信是在一个发送者和一个接收者之间进行的,我们常把它称为点对点的通信,但对于有些应用,这种点对点的通信模式不能有效地满足实际应用的需求。例如:一个数字电话会议系统由多个会场组成,当在其中一个会场的参会人发言时,要求其它会场都能即时的得到此发言的内容,这是一个典型的一对多的通信应用,通常把这种一对多的通信称为多播通信。采用多播通信技术,不仅可以实现一个发送者和多个接收者之间进行通信的功能,而且可以有效减轻网络通信的负担,避免资源的无谓浪费。

广播也是一种实现一对多数据通信的模式,但广播与多播在实现方式上有所不同。广播是将数据从一个工作站发出,局域网内的其他所有工作站都能收到它。这一特征适用于无连接协议,因为LAN上的所有机器都可获得并处理广播消息。使用广播消息的不利之处是每台机器都必须对该消息进行处理。多播通信则不同,数据从一个工作站发出后,如果在其它LAN上的机器上面运行的进程表示对这些数据"有兴趣",多播数据才会发给它们。

本实例由Sender和Receiver两个程序组成,Sender用户从控制台上输入多播发送数据,Receiver端都要求加入同一个多播组,完成接收Sender发送的多播数据。

一、实现方法

1、 协议支持

并不是所有的协议都支持多播通信,对Win32平台而言,仅两种可从WinSock内访问的协议(IP/ATM)才提供了对多播通信的支持。因通常通信应用都建立在TCP/IP协议之上的,所以本文只针对IP协议来探讨多播通信技术。

支持多播通信的平台包括Windows CE 2.1、Windows 95、Windows 98、Windows NT 4、Windows 2000和WindowsXP。自2.1版开始,Windows CE才开始实现对IP多播的支持。本文实例建立在WindowsXP专业版平台上。

2、多播地址

IP采用D类地址来支持多播。每个D类地址代表一组主机。共有28位可用来标识小组。所以可以同时有多达25亿个小组。当一个进程向一个D类地址发送分组时,会尽最大的努力将它送给小组的所有成员,但不能保证全部送到。有些成员可能收不到这个分组。举个例子来说,假定五个节点都想通过I P多播,实现彼此间的通信,它们便可加入同一个组地址。全部加入之后,由一个节点发出的任何数据均会一模一样地复制一份,发给组内的每个成员,甚至包括始发数据的那个节点。D类I P地址范围在224.0.0.0到239.255.255.255之间。它分为两类:永久地址和临时地址。永久地址是为特殊用途而保留的。比如,224.0.0.0根本没有使用(也不能使用),224.0.0.1代表子网内的所有系统(主机),而224.0.0.2代表子网内的所有路由器。在 RFC 1700文件中,提供了所有保留地址的一个详细清单。该文件是为特殊用途保留的所有资源的一个列表,大家可以找来作为参考。"Internet分配数字专家组"(I A N A)负责着这个列表的维护。在表1中,我们总结了目前标定为"保留"的一些地址。临时组地址在使用前必须先创建,一个进程可以要求其主机加入特定的组,它也能要求其主机脱离该组。当主机上的最后一个进程脱离某个组后,该组地址就不再在这台主机中出现。每个主机都要记录它的进程当前属于哪个组。 表1 部分永久地址说明

地 址 说 明

224.0.0.1 基本地址(保留)

224.0.0.1 子网上的所有系统

224.0.0.2 子网上的所有路由器

224.0.0.5 子网上所有OSPF路由器

224.0.0.6 子网上所有指定的OSPF路由器

224.0.0.9 RIP第2版本组地址

224.0.1.1 网络时间协议

224.0.1.24 WINS服务器组地址

3、 多播路由器

多播由特殊的多播路由器来实现,多播路由器同时也可以是普通路由器。各个多播路由器每分钟发送一个硬件多播信息给子网上的主机(目的地址为224.0.0.1),要求它们报告其进程当前所属的是哪一组,各主机将它感兴趣的D类地址返回。这些询问和响应分组使用IGMP(Internet group management protocol),它大致类似于ICMP。它只有两种分组:询问和响应,都有一个简单的固定格式,其中有效载荷字段的第一个字段是一些控制信息,第二字段是一个D类地址,在RFC1112中有详细说明。

多播路由器的选择是通过生成树实现的,每个多播路由器采用修改过的距离矢量协议和其邻居交换信息,以便向每个路由器为每一组构造一个覆盖所有组员的生成树。在修剪生成树及删除无关路由器和网络时,用到了很多优化方法。

4.库支持

WinSock提供了实现多播通信的API函数调用。针对IP多播,WinSock提供了两种不同的实现方法,具体取决于使用的是哪个版本的 WinSock。第一种方法是WinSock1提供的,要求通过套接字选项来加入一个组;另一种方法是WinSock2提供的,它是引入一个新函数,专门负责多播组的加入,这个函数便是WSAJoinLeaf,它是基层协议无关的。本文将通过一个多播通信的实例的实现过程,来讲叙多播实现的主要步骤。因为Window98以后版本都安装了Winsock2.0以上版本,所以本文实例在WinSock2.0平台上开发的,但在其中对WinSock1实现不同的地方加以说明。

二、编程步骤

1、启动Visual C++6.0,创建一个控制台项目工程MultiCase。在此项目工程中添加Sender和Receiver两个项目。

Receiver项目实现步骤:

(1)、创建一个SOCK_DGRAM类型的Socket。

(2)、将此Socket绑定到本地的一个端口上,为了接收服务器端发送的多播数据。

(3)、加入多播组。

①、 WinSock2中引入一个WSAJoinLeaf,此函数原型如下:

SOCKET WSAJoinLeaf( SOCKET s, const struct sockaddr FAR *name, int namelen,

LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, DWORD dwFlags );

其中,第一个参数s代表一个套接字句柄,是自WSASocket返回的。传递进来的这个套接字必须使用恰当的多播标志进行创建;否则的话WSAJoinLeaf就会失败,并返回错误WSAEINVAL。第二个参数是SOCKADDR(套接字地址)结构,具体内容由当前采用的协议决定,对于IP协议来说,这个地址指定的是主机打算加入的那个多播组。第三个参数namelen(名字长度)是用于指定name参数的长度,以字节为单位。第四个参数lpCallerData(呼叫者数据)的作用是在会话建立之后,将一个数据缓冲区传输给自己通信的对方。第五个参数lpCalleeData(被叫者数据)用于初始化一个缓冲区,在会话建好之后,接收来自对方的数据。注意在当前的Windows平台上,lpCallerData和lpCalleeData这两个参数并未真正实现,所以均应设为NULL。LpSQOS和lpGQOS这两个参数是有关 Qos(服务质量)的设置,通常也设为NULL,有关Qos内容请参阅MSDN或有关书籍。最后一个参数dwFlags指出该主机是发送数据、接收数据或收发兼并。该参数可选值分别是:JL_SENDER_ONLY、JL_RECEIVER_ONLY或者JL_BOTH。

②、在WinSock1平台上加入多播组需要调用setsockopt函数,同时设置IP_ADD_MEMBERSHIP选项,指定想加入的那个组的地址结构。具体实现代码将在下面代码注释列出。

(4)、接收多播数据。

Sender实现步骤:

(1)、创建一个SOCK_DGRAM类型的Socket。

(2)、加入多播组。

(3)、发送多播数据。

3、编译两个项目,在局域网中按如下步骤测试:

(1)、将Sender.exe拷贝到发送多播数据的PC上。

(2)、将Receiver.exe拷贝到多个要求接收多播数据的PC上。

(3)、各自运行相应的程序。

(4)、在Sender PC上输入多播数据后,你就可以在Receiver PC上看到输入的多播数据。

//sender.cpp

#include <winsock2.h>

#include <ws2tcpip.h>

#include <stdio.h>

#include <stdlib.h>

#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。

#define MCASTPORT 5150 //本地端口号。

#define BUFSIZE 1024 //发送数据缓冲大小。

int main( int argc,char ** argv)

{

       WSADATA wsd;

       struct sockaddr_in remote;

       SOCKET sock,sockM;

       TCHAR sendbuf[BUFSIZE];

       int len = sizeof( struct sockaddr_in);

       //初始化WinSock2.2

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

       {

              printf("WSAStartup() failed\n");

              return -1;

       }

       if((sock=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,

              WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|

              WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

       {

              printf("socket failed with:%d\n",WSAGetLastError());

              WSACleanup();

              return -1;

       }

       //加入多播组

       remote.sin_family = AF_INET;

       remote.sin_port = htons(MCASTPORT);

       remote.sin_addr.s_addr = inet_addr( MCASTADDR );

       if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,

              sizeof(remote),NULL,NULL,NULL,NULL,

              JL_BOTH)) == INVALID_SOCKET)

       {

              printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());

              closesocket(sock);

              WSACleanup();

              return -1;

       }

      

       //发送多播数据,当用户在控制台输入"QUIT"时退出。

       while(1)

       {

              printf("SEND : ");

              scanf("%s",sendbuf);

              if( sendto(sockM,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)

                    

                     &remote,sizeof(remote))==SOCKET_ERROR)

              {

                     printf("sendto failed with: %d\n",WSAGetLastError());

                     closesocket(sockM);

                     closesocket(sock);

                     WSACleanup();

                     return -1;

              }

              if(strcmp(sendbuf,"QUIT")==0) break;

              Sleep(500);

       }

      

       closesocket(sockM);

       closesocket(sock);

       WSACleanup();

       return 0;

}

//receiver.cpp

#include <winsock2.h>

#include <ws2tcpip.h>

#include <stdio.h>

#include <stdlib.h>

#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。

#define MCASTPORT 5150 //绑定的本地端口号。

#define BUFSIZE 1024 //接收数据缓冲大小。

int main( int argc,char ** argv)

{

       WSADATA wsd;

       struct sockaddr_in local,remote,from;

       SOCKET sock,sockM;

       TCHAR recvbuf[BUFSIZE];

       /*struct ip_mreq mcast; // Winsock1.0 */

      

       int len = sizeof( struct sockaddr_in);

       int ret;

       //初始化WinSock2.2

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

       {

              printf("WSAStartup() failed\n");

              return -1;

       }

       /*

       创建一个SOCK_DGRAM类型的SOCKET

       其中,WSA_FLAG_MULTIPOINT_C_LEAF表示IP多播在控制面层上属于

"无根"类型;

       WSA_FLAG_MULTIPOINT_D_LEAF表示IP多播在数据面层上属于"无根",

有关控制面层和

       数据面层有关概念请参阅MSDN说明。

       */

       if((sock=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,

              WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|

              WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

       {

              printf("socket failed with:%d\n",WSAGetLastError());

              WSACleanup();

              return -1;

       }

       //将sock绑定到本机某端口上。

       local.sin_family = AF_INET;

       local.sin_port = htons(MCASTPORT);

       local.sin_addr.s_addr = INADDR_ANY;

       if( bind(sock,(struct sockaddr*)&local,sizeof(local)) == SOCKET_ERROR )

       {

              printf( "bind failed with:%d \n",WSAGetLastError());

              closesocket(sock);

              WSACleanup();

              return -1;

       }

       //加入多播组

       remote.sin_family = AF_INET;

       remote.sin_port = htons(MCASTPORT);

       remote.sin_addr.s_addr = inet_addr( MCASTADDR );

       /* Winsock1.0 */

       /*

       mcast.imr_multiaddr.s_addr = inet_addr(MCASTADDR);

       mcast.imr_interface.s_addr = INADDR_ANY;

       if( setsockopt(sockM,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&mcast,

      

         sizeof(mcast)) == SOCKET_ERROR)

         {

         printf("setsockopt(IP_ADD_MEMBERSHIP) failed:%d\n",WSAGetLastError());

         closesocket(sockM);

         WSACleanup();

         return -1;

         }

       */

       /* Winsock2.0*/

       if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,sizeof(remote),

             

              NULL,NULL,NULL,NULL,

              JL_BOTH)) == INVALID_SOCKET)

       {

              printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());

              closesocket(sock);

              WSACleanup();

              return -1;

       }

       //接收多播数据,当接收到的数据为"QUIT"时退出。

       while(1)

       {

              if(( ret = recvfrom(sock,recvbuf,BUFSIZE,0,

                    

                     (struct sockaddr*)&from,&len)) == SOCKET_ERROR)

              {

                     printf("recvfrom failed with:%d\n",WSAGetLastError());

                     closesocket(sockM);

                     closesocket(sock);

                     WSACleanup();

                     return -1;

              }

              if( strcmp(recvbuf,"QUIT") == 0 ) break;

              else {

                     recvbuf[ret] = '\0';

                     printf("RECV:' %s ' FROM <%s> \n",recvbuf,inet_ntoa(from.sin_addr));

              }

       }

      

       closesocket(sockM);

       closesocket(sock);

       WSACleanup();

       return 0;

}


附录4:raw socket编程例子

1原始套接字工作原理与规则

原始套接字是一种不同于SOCK_STREAM和SOCK_DGRAM的套接字,它实现于系统核心。它的创建方式跟TCP/UDP创建方法几乎是一模一样,例如,通过

int sockfd;

sockfd=socktet(AF_INET,SOCK_RAW,IPPROTO_ICMP);

这两句程序你就可以创建一个原始套接字。这种类型套接字的功能与TCP或者UDP类型套接字的功能有很大的不同:TCP/UDP类型的套接字只能够访问传输层以及传输层以上的数据,因为当IP层把数据传递给传输层时,下层的数据包头已经被丢掉了。而原始套接字却可以访问传输层以下的数据,所以使用raw套接字你可以实现上至应用层的数据操作,也可以实现下至链路层的数据操作。比如:通过

sock=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP))

方式创建的rawsocket就能直接读取链路层的数据。

1)使用原始套接字时应该注意的问题(参考<<unix网络编程>>以及网上的优秀文档)

(1):对于UDP/TCP产生的IP数据包,内核不将它传递给任何原始套接字,而只是将这些数据交给对应的UDP/TCP数据处理句柄(所以,如果你想要通过原始套接字来访问TCP/UDP或者其它类型的数据,调用socket函数创建原始套接字第三个参数应该指定为htons(ETH_P_IP),也就是通过直接访问数据链路层来实现。(我们后面的密码窃取器就是基于这种类型的)。

(2):对于ICMP和EGP等使用IP数据包承载数据但又在传输层之下的协议类型的IP数据包,内核不管是否已经有注册了的句柄来处理这些数据,都会将这些IP数据包复制一份传递给协议类型匹配的原始套接字。

(3):对于不能识别协议类型的数据包,内核进行必要的校验,然后会查看是否有类型匹配的原始套接字负责处理这些数据,如果有的话,就会将这些IP数据包复制一份传递给匹配的原始套接字,否则,内核将会丢弃这个IP数据包,并返回一个ICMP主机不可达的消息给源主机。

(4):如果原始套接字bind绑定了一个地址,核心只将目的地址为本机IP地址的数包传递给原始套接字,如果某个原始套接字没有bind地址,核心就会把收到的所有IP数据包发给这个原始套接字。

(5):如果原始套接字调用了connect函数,则核心只将源地址为connect连接的IP地址的IP数据包传递给这个原始套接字。

(6):如果原始套接字没有调用bind和connect函数,则核心会将所有协议匹配的IP数据包传递给这个原始套接字。

2)编程选项

原始套接字是直接使用IP协议的非面向连接的套接字,在这个套接字上可以调用bind和connect函数进行地址绑定。说明如下:

(1)bind函数:调用bind函数后,发送数据包的源IP地址将是bind函数指定的地址。如是不调用bind,则内核将以发送接口的主IP地址填充IP头。如果使用setsockopt设置了IP_HDRINCL(headerincluding)选项,就必须手工填充每个要发送的数据包的源IP地址,否则,内核将自动创建IP首部。

(2)connetc函数:调用connect函数后,就可以使用write和send函数来发送数据包,而且内核将会用这个绑定的地址填充IP数据包的目的IP地址,否则的话,则应使用sendto或sendmsg函数来发送数据包,并且要在函数参数中指定对方的IP地址。

综合以上种种功能和特点,我们可以使用原始套接字来实现很多功能,比如最基本的数据包分析,主机嗅探等。其实也可以使用原始套接字作一个自定义的传输层协议。

2一个简单的应用

下面的代码创建一个直接读取链路层数据包的原始套接字,并从中分析出源MAC地址和目的MAC地址,源IP和目的IP,以及对应的传输层协议,如果是TCP/UDP协议的话,打印其目的和源端口。为了方便阅读,程序中避免了使用任何与协议有关的数据结构,如structether_header,structiphdr等,当然,要完全理解代码,你需要关于指针以及位运算的知识。

/***************SimpelSniffer.c*************/

/*注意:本代码为LINUX操作系统下的源代码*/

//author:duanjigang@2006s

#include <stdio.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <linux/if_ether.h>

#include <linux/in.h>

#define BUFFER_MAX 2048

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

{

      

       int sock, n_read, proto;      

       char buffer[BUFFER_MAX];

       char  *ethhead, *iphead, *tcphead,

              *udphead, *icmphead, *p;

      

       if((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)

    {

        fprintf(stdout, "create socket error\n");

        exit(0);

    }

      

       while(1)

       {

              n_read = recvfrom(sock, buffer, 2048, 0, NULL, NULL);

        /*

        14   6(dest)+6(source)+2(type or length)

        +

        20   ip header

        +

        8   icmp,tcp or udp header

        = 42

        */

              if(n_read < 42)

              {

                     fprintf(stdout, "Incomplete header, packet corrupt\n");

                     continue;

              }

             

        ethhead = buffer;

        p = ethhead;

        int n = 0XFF;

              printf("MAC: %.2X:%02X:%02X:%02X:%02X:%02X==>"

                     "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",

                     p[6]&n, p[7]&n, p[8]&n, p[9]&n, p[10]&n, p[11]&n,

                     p[0]&n, p[1]&n, p[2]&n,p[3]&n, p[4]&n, p[5]&n);

             

              iphead = ethhead + 14; 

              p = iphead + 12;

       

              printf("IP: %d.%d.%d.%d => %d.%d.%d.%d\n",

                     p[0]&0XFF, p[1]&0XFF, p[2]&0XFF, p[3]&0XFF,

                     p[4]&0XFF, p[5]&0XFF, p[6]&0XFF, p[7]&0XFF);

              proto = (iphead + 9)[0];

              p = iphead + 20;

              printf("Protocol: ");

              switch(proto)

              {

              case IPPROTO_ICMP: printf("ICMP\n");break;

              case IPPROTO_IGMP: printf("IGMP\n");break;

              case IPPROTO_IPIP: printf("IPIP\n");break;

              case IPPROTO_TCP :

              case IPPROTO_UDP :

                     printf("%s,", proto == IPPROTO_TCP ? "TCP": "UDP");

                     printf("source port: %u,",(p[0]<<8)&0XFF00 |  p[1]&0XFF);

                     printf("dest port: %u\n", (p[2]<<8)&0XFF00 | p[3]&0XFF);

                     break;

              case IPPROTO_RAW : printf("RAW\n");break;

              default:printf("Unkown, please query in include/linux/in.h\n");

        }

       }

}


附录5:课程设计报告封面

计算机网络

课程设计报告

题目:                         

名:               

号:               

级:               

指导老师:               

湖南科技大学计算机科学与工程学院

20## 

更多相关推荐:
计算机网络课程设计心得体会

课程设计是每一个大学生在大学生涯中都不可或缺的,它使我们在实践中了巩固了所学的知识、在实践中锻炼自己的动手能力;实习又是对每一位大学生所学专业知识的一种拓展手段,它让我们学到了很多在课堂上根本就学不到的知识,不…

计算机网络课程设计心得

两周的课程设计结束了在这次的课程设计中不仅检验了我所学习的知识也培养了我如何去把握一件事情如何去做一件事情又如何完成一件事情在设计过程中与同学分工设计和同学们相互探讨相互学习相互监督学会了合作学会了运筹帷幄学会...

计算机网络课程设计心得 (1)

计算机网络课程设计心得在进行计算机网络课程设计实验开始之前,由于计算机网络的课程才刚刚开始,我对TCP/IP、路由、VLAN等等的概念只是听说过,甚至连路由器和交换机都分不清楚,就这样从零基础开始了做课设的日子…

计算机网络课设总结

课设总结通过此次短暂的课程设计,我深有感触。从一开始的构思再到今天写下这份总结。期间时间虽短但是也学到了不少的知识。在实训的刚开始构想只有大体的思路,忽略了一些细节,因此在我真正做设计网络方案时发现有很多错误,…

计算机网络课程设计报告

计算机网络课程设计20xx20xx第二学期第19周指导教师XXX班级姓名学号一实验目的及要求本课程设计须安排在计算机网络课程结束之后主要目的是通过实际操作和实验以及编程等加深学生对课堂所学知识的理解提高学生对网...

计算机网络课程设计报告

计算机网络课程设计报告基于UDP的即时通信工具的设计开发学院一课设任务任务一设计一个基于UDP的点对点通信工具实现点对点的通信如下图所示用户发送的信息将在对方用户中显示显示内容包括用户名发送时间发送内容计算机网...

计算机网络课程设计

计算机网络课程设计课程名称计算机网络题目名称企业网组建与设计专业班级07信管学号20xx163019姓名王余指导教师马彦图计算机网络课程设计目录一课程设计的目的和意义二设计对象和设计要求三设计内容31需求分析3...

计算机网络课程设计报告

计算机网络课程设计报告一.课程设计的题目、目的及要求.........................................................2二.课程设计的内容(分析和设计).....…

计算机网络课程设计报告

淮海工学院计算机工程学院课程设计报告设计名称:计算机网络课程设计姓名:学号:专业班级:系(院):计算机工程学院设计时间:20XX.01.04~20XX.01.07设计地点:多媒体机房 1.…

计算机网络课程设计

南京工程学院课程设计任务书课程名称计算机网络院系部中心计算机工程学院专业网络工程计算机多媒体软件工程班级K软件工程121122起止日期20xx61620xx620指导教师王琦

计算机网络原理课程设计报告

上海应用技术学院课程设计任务书指导教师(签名):陈颖教研室主任(签名):荣祺20##年12月28日20##年12月30日目录1.设计目的及要求~~~~~~~~~~~~~~~~~~~~~~~~~~~41.1.设计…

计算机网络课程设计实验报告

实验报告课程名称计算机网络实验名称ARQ协议模拟实现系院信息科学与技术学院信息安全专业实验日期20xx420班级0405403实验报告日期20xx510姓名乔来阳学号040540312同组人无老师批阅签字实验内...

计算机网络课程设计心得(38篇)