东南大学自动化学院实验报告课程名称:信息通信网络概论第3次实验实验名称:实验三基于客户/服务器模式的网络通信编程实现院(系):自动化专业:自动化姓名:学号:实验室:金智楼实验组别:同组人员:实验时间:2016年12月13日评定成绩:审阅教师:2目录一.实验目的和要求·····································3二.实验原理···········································3三.实验方案与实验步骤·································4四.实验设备与器材配置·································5五.实验记录···········································5六.实验总结··········································10七.思考题或讨论题····································11附录:部分代码3一.实验目的和要求1.进一步了解网络编程的过程;2.掌握Windows环境下基于WinSock的编程方法和通信实现;3.熟悉客户/服务器模式的网络通信编程实现,编写一个聊天工具,即以客户端和服务器端的模式进行互发消息。二.实验原理一个在建立分布式应用时最常用的范例便是客户机/服务器模型。在这种方案中客户应用程序向服务器程序请求服务。这种方式隐含了在建立客户机/服务器间通讯时的非对称性。客户机/服务器模型工作时要求有一套为客户机和服务器所共识的惯例来保证服务能够被提供(或被接受)。这一套惯例包含了一套协议。它必须在通讯的两头都被实现。根据不同的实际情况,协议可能是对称的或是非对称的。在对称的协议中,每一方都有可能扮演主从角色;在非对称协议中,一方被不可改变地认为是主机,而另一方则是从机。一个对称协议的例子是Internet中用于终端仿真的TELNET。而非对称协议的例子是Internet中的FTP。无论具体的协议是对称的或是非对称的,当服务被提供时必然存在“客户进程”和“服务进程”。一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“惊醒”并且为客户提供服务-对客户的请求作出适当的反应。这一请求/相应的过程可以简单的用图2-1表示。虽然基于连接的服务是设计客户机/服务器应用程序时的标准,但有些服务也是可以通过数据报套接口提供的。图2-1客户机/服务器模型4三.实验方案与实验步骤1、客户机/服务器工作流程建立服务端的Socket绑定端口监听服务器端接受客户端的连接请求结束socket连接建立客户端的Socket提出连接申请数据的传送(a)服务端工作流程(b)客户端工作流程图3.1客户机/服务器工作流程2、聊天室的工作流程5客户机Socket()Bind()Connect()Send()Recv()Closesocket()服务器侦听套接字连接套接字Socket()Bind()Listen()Accept()创建连接套接字Recv()Send()Closesocket()Closesocket()图3.2聊天室工作流程首先,服务器要创建一个用于侦听的套接字,为该套接字分配地址之后,调用listen()函数使它处于侦听状态;客户机在创建套接字完毕后,为套接字分配地址,然后调用connect()函数,请求与服务器套接字连接;服务器套接字在收到客户机的连接请示后,调用accept()函数,该函数创建一个用于连接的套接字。应用该套接字和客户机上的连接套接字,用户就可以在服务器和客户机之间进行数据传输了。在结束传输之后,客户机调用closesocket()函数关闭套接字,服务器也调用该函数关闭用于侦听和连接的套接字。3、套接字事件处理相关函数OnAccept():监听套接字在断口听到连接请求时,应用程序框架调用该函数以告之可以接受或拒绝连接请求,调用Accept来接受请求,建立连接。OnConnect():当客户端套接字连接请求完成后,应用程序框架调用该函数,告之是6否成功。OnSend():框架调用该函数通知该套接字现在可以调用Send函数开始传送数据。OnReceive():框架调用该函数通知套接字可以调用Receive函数从数据缓冲区中取数据。OnClose():框架调用该函数告之与其相连的对方套接字已被关闭。4、系统实现利用MFCAppWizard生成程序框架增加控件对象从CAsyncSocket类继承建立客户机与服务器之间的连接发送和接收数据终止连接图3.3系统实现过程四.实验设备与器材配置电脑、VC6.0五.实验记录1、界面(1)选择:客户机、服务器;(2)输入:服务器名称、服务器端口、发送的消息;(3)输出显示:发送的消息、接收的消息、发送和接收消息数目;(4)按钮:连接/侦听、断开、发送、清空。7图5.1界面设计2、功能描述(1)服务器端i.建立Socket,绑定端口,并监听;ii.接受客户端的连接;iii.与客户端进行通讯;iv.断开连接。8图5.2服务器端(2)客户端i.建立Socket,并申请连接到服务器;ii.与服务器进行通讯;iii.断开连接。图5.3客户机端93、改进(1)获取对方主机信息通过函数gethostname(hostname,sizeof(hostname))得到主机名,客户机获得服务器主机名为2013-20140524WQ,服务器获得客户机主机名为2013-20140524WQ(实验时使用同一台电脑进行调试,所以主机名是一样的),添加到接收信息列表中信息前面。(2)添加发送接收时间通过函数time()、localtime()获得当前系统时间,能够显示发送接收时间,格式为年/月/日时/分/秒,添加到接收信息列表中信息首部。(3)显示发送、接收消息总数通过m_list.GetCount()函数实现对列表中发送和接收到的信息计数,并显示在界面上,清空列表后,相应的重置为0。(4)清空列表通过函数m_list.ResetContent()实现清空列表。(5)添加背景图片、改变控件背景色、字体等。上传bmp图片到Bitmap资源文件夹中,在OnPaint()中添加代码,通过不同图片的ID来设置不同的背景图片。通过加入WM_CTLCOLOR消息,自动生成OnCtlColor()函数,利用SetBkMode()、SetTextColor()、SetBkColor()、CreateSolidBrush()、SetFont()等函数设置背景格式、改变控件背景色和字体大小颜色等。10图5.4改进功能六.实验总结在原demo程序的基础上,增加了获取对方主机名、添加发送时间、清空列表等功能,并且尝试添加背景图片,利用WM_CTLCOLOR消息实现对MFC界面的修改。通过本次实验,我对“点对点”模型有了非常深刻的认识,进一步了解了网络编程11的过程,掌握了Windows环境下基于WinSock的编程方法和通讯实现,并且实现了客户端和服务器的连接通信。对相关套接字事件处理函数OnAccept()、OnConnect()、OnSend()、OnReceive()、OnClose()有了一定了解,熟悉了服务器和客户端不同的工作流程。七.思考题或讨论题1、结合所学知识,谈谈自己对TCP三次握手的理解。1)主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;2)主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;3)主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。2、TCP协议有什么优点,根据你的理解说说为什么http要采用TCP作为底层协议?(1)优点:面向连接、传输可靠、无拥塞、无乱序、无丢包、完整性检查、可重传;(2)TCP协议对应于传输层,而HTTP协议对应于应用层,Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开。12附录:(部分代码)1、获取对方主机信息添加发送接收时间voidCTalkDlg::OnReceive(){CStringmDay;CStringmTime;CStringttime;time_tt1;t1=time(NULL);//机器时间structtm*p;p=localtime(&t1);//转换为本地时间mDay.Format(%4d/%2d/%2d,1900+p-tm_year,1+p-tm_mon,p-tm_mday);mTime.Format(%3d:%2d:%2d,p-tm_hour,p-tm_min,p-tm_sec);char*pBuf=newchar[1025];//charpBuf[1025];intnBufSize=1024;intnReceived;charhostname[100];CStringstrReceived;nReceived=m_sConnectSocket.Receive(pBuf,nBufSize);//接收消息//判断消息接收是否成功?if(nReceived!=SOCKET_ERROR){gethostname(hostname,sizeof(hostname));//获取对方主机信息pBuf[nReceived]=NULL;//保留接收消息的有效部分strReceived=pBuf;//将消息转化为CString对象//添加主机信息和发送时间strReceived=mDay+mTime++hostname+:+strReceived;//将消息添加到“已接收的消息”列表框m_listReceived.AddString(strReceived);m_Countb=m_listReceived.GetCount();//显示接收信息数UpdateData(FALSE);//更新对话框}else{AfxMessageBox(信息接收错误!,MB_OK|MB_ICONSTOP);}deletepBuf;}2、显示发送、接收消息总数voidCTalkDlg::OnSendMsg(){13CStringmDay;CStringmTime;CStringttime;time_tt1;t1=time(NULL);//机器时间structtm*p;p=localtime(&t1);//转换为字符串时间mDay.Format(%4d/%2d/%2d,1900+p-tm_year,1+p-tm_mon,p-tm_mday);mTime.Format(%3d:%2d:%2d,p-tm_hour,p-tm_min,p-tm_sec);intnLen;//消息长度intnSent;//已发送消息的长度//从对话框取回数据UpdateData(TRUE);//判断要发送的消息是否为空if(!m_strMsg.IsEmpty()){nLen=m_strMsg.GetLength();//获取消息的长度nSent=m_sConnectSocket.Send(LPCTSTR(m_strMsg),nLen);//发送消息if(nSent!=SOCKET_ERROR)//发送是否成功{//添加主机信息和发送时间m_strMsg=mDay+mTime+send:+m_strMsg;//将消息添加到“已发送列表框”m_listSent.AddString(m_strMsg);m_Counta=m_listSent.GetCount