您的当前位置:首页正文

网络程序设计实验报告

2020-07-15 来源:步旅网
网络程序设计 实验报告 实验名称:_ __UDP通信实验 _______ 实验类型: ____ _验证型实验 指导教师:_______ _____ ______ 专业班级:_____ _ ____ _ 姓 名:_____ ___ _______ 学 号: ____ 电子邮件:___ __ 实验地点:__ 实验成绩:__________________________ 一、实验目的 1、进一步理解Winsock API的调用方法 2、了解UDP协议的工作原理 3、掌握UDP服务端程序和客户端程序的编写流程 4、熟悉程序的调试方法。 二、实验设计 1、数据报套接字编程模型时序和流程 服务端 socket() 客户端 bind() recvfrom阻塞,等待客户连接处理服务请sendto() closesocket服务应服务请socket() bind() sendto() recvfromclosesocket 图1:流套接字编程时序图 2、用到的Winsock API函数有: 1)、创建套接字函数socket() SOCKET socket(int af,int type,int protocol); 由于采用数据报套接字进行数据传输,因此type参数必须设置为SOCK_DGRAM,protocol参数必须设置为IPPROTO_UDP 2)、绑定本地地址到所创建的套接字函数bind() int bind(SOCKET s,const struct sockaddr* name,int namelen); 3)、接收数据函数recvfrom() int recvfrom(SOCKET s,char* buf,int len,int flags, struct sockaddr* from,int* fromlen); 4)、发送数据函数sendto() int sendto(SOCKET s,const char* buf,int len,int flags, const struct sockaddr* to,int* tolen); 5)、关闭套接字函数closesocket() int closesocket(SOCKET s); 3、服务端的程序流程图如下: 说明:服务端的程序首先要调用函数socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)创建一个监听套接字,用于监听是否有客户端发送数据过来,然后调用bind(s,(LPSOCKADDR)&sin,sizeof(sin)) 绑定该监听套接字到一个本地地址,接下来是一个循环语句,循环跳出的条件是客户端发来的数据是‘bye’或自己发送给客户端的数据为‘bye’,当接收到数据为‘bye’或自己发出‘bye’时关闭套接字,结束程序,否则就接受数据,输出接受到的数据,然后输入发送给客户端的数据,以此循环。 图2:服务端收发数据的流程图 4、客户端的程序流程图如下: 说明:客户端的程序首先要调用函数socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)创建一个套接字,然后声明一个sockaddr_in类型的变量addr,存放服务端的地址信息,接下来是一个循环语句,循环跳出的条件是服务端发来的数据是‘bye’或自己发送给服务端的数据为‘bye’,当接收到数据为‘bye’或自己发出‘bye’时关闭套接字,结束程序,否则就输入发送给服务端的数据,接受数据,输出收到的数据,以此循环。 图2:客户端收发数据的流程图 三、实验过程(包含实验结果) 1、遇到的错误有: 1)、当客户端发送‘bye’时,服务端收到数据,但是客户端和服务端都未结束通话 错误原因:当判断接受数据是否为‘bye’时,用的代码是if (recMsg==”bye”),未用判断字符串的函数strcmp(recMsg,\"bye\"),当客户端发送‘bye’后,未判断自己是否发送的‘bye’导致客户端未退出。 2、实验结果: 1)、服务端运行结果如下: 2)、客户端运行结果如下: 四、讨论与分析 1)、能否在接收数据之间不进行bind()调用?如果能,请说明可能的情况。 答:创建套接字之后,如果首先调用的是sendto函数,则可以不调用bind函数显式地绑定本地地址,系统会自动为程序绑定,但是如果创建套接字后,直接调用recvfrom就会失败,因为套接字还没有绑定。 2)、能否使用connect()连接对方?为什么? 答:UDP中可以使用connect系统调用,UDP中connect操作与TCP中connect操作有着本质区别,TCP中调用connect会引起三次握手,client与server建立连结,UDP中调用connect内核仅仅把对端ip和port记录下来,UDP中可以多次调用connect,TCP只能调用一次connect。采用connect的UDP发送接受报文可以调用send,write和recv,read操作.当然也可以调用sendto,recvfrom.调用sendto的时候第五个参数必须是NULL,第六个参数是0.调用recvfrom,recv,read系统调用只能获取到先前connect的ip&port发送的报文。 3)、能否在不调用sendto()函数之前调用recvfom()函数。 答:服务端可以在不调用sendto()函数之前调用recvfom()函数,因为服务端要在接受到客户端数据后才发送数据到客户端,但是客户端必须先调用sendto函数后才能调用recvfom函数,因为,当没有客户端发来数据时,服务端一直处于监听状态,客户要先调用sendto函数才能让服务端不处于阻塞模式,然后再调用recvfom接受服务端发来的数据。 五、实验者自评 由于本次实验是验证型实验,代码是书上现成的,但是通过自己看代码,分析代码并画出了程序的流程图,我进一步理解了Winsock API的调用方法,也了解了UDP协议的工作原理,并掌握了UDP服务端程序和客户端程序的编写流程,在做实验的过程中也出现了许多问题,通过调试代码,我更加熟悉了程序的调试方法,把遇到的问题都一一解决了,而且通过实际分析代码,修改代码,对老师课堂上将的理论知识理解更加深刻,体会也更加深刻。通过此次的实验,我达到了本次实验的目的,也巩固了课堂上老师讲授的基础知识,是一次非常有意义的实验。 六、附录:关键代码(给出适当注释,可读性高) 1、服务端程序代码: #include #include \"initsock.h\" CInitSock initSock;//初始化Winsock库 int main() { //创建套接字 SOCKET s=::socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); if(s == INVALID_SOCKET) { printf(\"Failed socket()\\n\"); return 0; } //填充sockaddr_in结构 sockaddr_in sin; sin.sin_family=AF_INET; sin.sin_port=htons(4567); sin.sin_addr.S_un.S_addr=INADDR_ANY; //绑定这个套接字到一个本地地址 if (::bind(s,(LPSOCKADDR)&sin,sizeof(sin))==SOCKET_ERROR) { } printf(\"\\n\\n\\n ************************服务端printf(\"Faild bind()\\n\"); return 0; **************************\\n\\n\"); char recMsg[1024]; char sendMsg[1024]; sockaddr_in addr; int nLen=sizeof(addr); printf(\"等待数据的到来:\\n\"); while(TRUE) { int nRecv=::recvfrom(s,recMsg,1024,0,(sockaddr*)&addr, &nLen); if (nRecv>0) { recMsg[nRecv]='\\0'; printf(\"接收到数据(%s):%s\\n\ if (strcmp(recMsg,\"bye\")==0) { } printf(\"请输入发送数据:\\n\"); gets(sendMsg); printf(\"关闭套接字,结束对话!\"); ::closesocket(s); return 0; ::sendto(s, sendMsg, strlen(sendMsg), 0, (sockaddr*)&addr, sizeof(addr)); } 2、客户端程序代码: #include #include \"initsock.h\" CInitSock initSock;//初始化Winsock库 int main() { } ::closesocket(s); } if (strcmp(sendMsg,\"bye\")==0) { } printf(\"关闭套接字,结束对话!\"); ::closesocket(s); return 0; SOCKET s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(s == INVALID_SOCKET) { } // 填写远程地址信息 sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(4567); printf(\"\\n\\n\\n ************************客户端printf(\"Failed socket() %d \\n\ return 0; **************************\\n\\n\"); char recMsg[1024]; char sendMsg[1024]; int nLen=sizeof(addr); while(TRUE) { printf(\"请输入发送数据:\\n\"); gets(sendMsg); ::sendto(s, sendMsg, strlen(sendMsg), 0, (sockaddr*)&addr, sizeof(addr)); if (strcmp(sendMsg,\"bye\")==0) { } int nRecv=::recvfrom(s,recMsg,1024,0,(sockaddr*)&addr,&nLen); if (nRecv>0) { recMsg[nRecv]='\\0'; printf(\"接收到数据(%s):%s\\n\ if (strcmp(recMsg,\"bye\")==0) { printf(\"关闭套接字,结束对话\"); ::closesocket(s); printf(\"关闭套接字,结束对话!\"); ::closesocket(s); return 0; } } } return 0; ::closesocket(s); return 0;}

因篇幅问题不能全部显示,请点此查看更多更全内容