实验5 掌握简单的网络通讯技术实验6 掌握基于典型协议的的网络通讯技术一实验目的1.熟悉Socket的通讯机制,了解网络程序的设计方法。
2. 熟悉典型协议的通讯机制,掌握基于它们的网络系统设计方法。
如TCP、UDP。
二实验环境1.Windows72.Visual C++ 6.0三实验程序设计1.实验原理1.C/S模式C/S模式是指一个服务器同时为多个客户端服务。
并发性是C/S模式的的基础,正是由于并发性的支持,多个客户端能够获得同一种服务,而不必等待服务器完成对上一个请求的处理。
在C/S模式中,当请求到达服务器时,服务器将它交给一个控制线程,它能与已有的线程并发的执行。
在设计并发服务器时,一般服务器代码由两部分组成,第一部分代码负责监听并接收客户端的请求,还负责为客户端请求创建新的服务线程;另一部分代码负责处理单个客户机请求,如与客户机交换数据,提供具体服务。
下图所示为服务器创建多个线程来为多个客户机服务。
2.基于TCP的socket通信服务器与客户端是通过套接字来进行通信的。
在基于TCP的网络通信程序中,首先服务器端通过socket()函数创建套接字,然后通过bind()函数来命名套接字,调用listen()函数开启服务器端的监听。
接着执行accept()函数来接收客户的连接,服务器阻塞等待客户连接请求,直到有客户请求连接。
客户端创建套接字,接着通过connect()函数请求连接服务器,连接成功后,就可以与服务器进行通信了。
服务器与客户机通过send()各recv()调用来进行信息的发送和接收。
只有当有信息可接收时,recv()才返回,否则将阻塞。
通信完后,服务器和客户机各自通过closesocket()函数来关闭套接字。
注:以上讲的是阻塞模式。
下图所示的是基于TCP的网络通信模式。
服务器端对于连接套接字2.系统设计实验5要求在客户机和服务器传输图片和文件,如果是图片则显示,是文件则保存。
这个实验中的客户机与服务器可以没有区别,即可以采用对等的模式,双方既可以是服务器也可以是客户机。
因为没有要求一个对多个服务。
实现实验5中的要求最简单的可以选用基于UDP的对等模式,但是我们小组决定将实验5和实验6的功能集成在一个程序中,所以最后还是选用了基于TPC的网络通信。
由客户机发送文件或图片到服务器,服务器检测到有文件可接收时,提示用户是否要接收来自己客户机的文件或图片,如果接收,则根据文件后缀名来判断文件类型,是图片就打开。
实验5中,难点在于怎么传递和接收文件,怎么判断一个文件是否传或者接收成功了。
在传文件的过程中,我们先组装数据包,在数据包中,我们先传文件标识符,文件标识符是为了和聊天时的消息标识符以及其他自定义的标识符区分接收。
然后再传文件名和文件的大小,最后是文件的内容。
服务器接收时,依次解析出文件标识,文件名,文件大小,文件内容。
图片也是文件,通过后缀名可以识别是图片还是非图片。
实验6要求编写一个多人聊天室系统。
聊天室程序是一个服务器接收来个多个客户端服务请求,这些请求是并发。
采用C/S模式设计,服务器开启一个监听线程来接受来自一个客户端的连接请求,在收到请求后,在这个线程中为这个客户提供交互服务,同时开启另一个监听线程来监听其他客户的连接请求。
这样就实现了服务器为多个不同的客户端提供并发服务。
在设计C/S模式,当客户端很多时,或者并发很大时,阻塞模式是行不通的,因为可能造成多个客户机同时争夺服务器时,有的客户机不能及时得到服务而影响用户体验。
我们小组曾尝试采用非阻塞模式,用了Select模型。
但是由于以前没有网络编程经验、时间有限以及非阻塞模式很复杂,没有成功实现,最后还是采用了阻塞模式。
不过,在实验中,还是没有出现性能问题。
怎么实现多人聊天室通信?首先客户机发送聊天信息到服务器,服务器接收到聊天信息后,在服务器的显示信息,并转发给所有的其他客户机。
服务器可以发送公告信息给所有的客户机。
当客户机下线时,该客户机发送下线标识消息到服务器,由服务器广播到其他客户机。
设计了一个在线客户列表,来同步时实显示在线的客户。
实验中一个要点在于怎么实现同步显示在线客户。
下面将在系统实现中讲述。
3.系统实现这部分主要结合关键代码来讲解系统的实现思路及具体实现,详细代码见附录。
我们小组实验5和实验6是在一个程序中实现的。
(1)服务器与客户端通信服务器开启监听,这部分主要实现创建套接字、命名套接字、套接字监听,然后开启一个监听线程。
m_serversock=socket(AF_INET,SOCK_STREAM,0);//创建ret=bind(m_serversock,(LPSOCKADDR)&addr,sizeof(addr));//绑定ret=listen(m_serversock,10);//监听AfxBeginThread(ListenThread,this); //开启一个监听线程在ListenThread线程中,接收客户端请求,接收阻塞解除后再开启另一个监听线程来监听其他客户机的连接请求。
Clientsock[k].clientsock=accept(pDlg->m_serversock,(sockaddr*)&clientaddr,&clientlen);//接收客户端请求AfxBeginThread(ListenThread,p); //开启另外一个监听线程,监听其他用户连接接下来,接收信息标识符,并根据不同的消息标识符来判断采用相应的处理函数。
char recvflag[3];memset(recvflag,0,3);int flagc=recv(clientsock[k].clientsock,recvflag,2,0);recvflag[2]='\0';CString strflag;strflag.Format("%s",recvflag);自己定义的消息标识符有“QQ”表退出,“MM”表聊天信息,“FF”表文件,“CC”表连PDlg->RecvMessage(clientsock[k].clientsock);//接收聊天消息pDlg->RecvFile(clientsock[k].clientsock);//接收文件消息客户端先创建套接字,然后主动连接服务器,开启一个接收线程来接收来自服务器的信息,在RecvFromServer线程中也是根据消息标识符来作不同的处理的。
m_clientsocket=socket(AF_INET,SOCK_STREAM,0);//创建ret=connect(m_clientsocket,(LPSOCKADDR)&addr,sizeof(addr));//主动连接服务器AfxBeginThread(RecvFromServer,this);//开启接收线程(2)文件发送与接收文件发送的关键代码while (1){ilen = file.Read(temp, 10000);if (ilen==0)break;iEnd = send(pDlg->m_clientsocket,temp,ilen,0);if (iEnd==SOCKET_ERROR){strError.Format("Send File:%d",error=WSAGetLastError());AfxMessageBox(strError);break;}iTmp +=iEnd;}文件接收关键代码long iTemp = 0;while (1){if (lFileSize==iTemp)//结束条件{break;}rcv = recv(s, buffer, 1024, 0);file.Write(buffer, rcv);iTemp += rcv;}打开图片。
用了win7自带的“画图”软件打开图片CString strdll="C:\\Windows\\System32\\mspaint.exe ";strdll+=strPathName;WinExec(strdll,SW_SHOWNORMAL);4.运行界面运行服务器启动监听运行客户端客户端上线客户端与服务器聊天客户端与服务器发送文件和图片多客户聊天客户下线,同步显示四实验感想这两次实验花了大概三个星期的时间,感受很多,学到了很多。
在刚看到实验题目时,我们小组就去学校图书馆借了很多关于计算机网络编程的书来看,查看了一些例子,但是很多例子都不怎么完整,后来,我们开始学习socket编程的理论知识,从对socket通信的基本步骤的学习到对TCP、UDP的比较,以及阻塞与非阻塞模式的比较。
学习了这些理论之后,再来看实例好懂多了,比如理解了accept()函数要阻塞直到有客户端连接请求才返回。
最先是尝试用mfc封闭好的CSocket类来实现网络通信,但是程序写了一些,才发现由于存在各种消息响应,对于了解socket通信的基本原理很不好,封装了一些基本步骤,让我们看不到一些过程。
最后我们还是定用原始的win32 socket调用来实现程序,这样虽然写程序比较复杂,因为要自己按照步骤一步一步实现,自己开线程,但是对于我们深入理解socket通信是很有帮助的。
看了windows socket的程序设计知识,后来我们又找了本Linux程序设计,看了其中的Linux socket,socket最初是在Linux 下应用的,后来windows才有的,所以看了Linux下的更好理解了。
从最开始的基本上写程序都要参照书本,到后来自己不看书实现各种通信,感觉收获了很多。
在系统的设计过程中,我们最初关注的是到底用tcp还是udp,tcp有连接,可靠,udp快易,后来还是决定用tcp,因为tcp可以更好理解通信过程,比如tcp的三次握手。
还有我们的程序是个实验程序,就应用量来说不用考虑tcp和upd的性能问题。
接着我们在设计采用C/S模式时,考虑是用阻塞模式还是非阻塞模式,书上说了,C/s模式在高并发时,阻塞模式是行不通的,会造成大量客户等待服务器,得不到及时的服务。
我们尝试过用Select非阻塞模式,但是太复杂了,没能实现,最后考虑到阻塞模式简单,我们还不怎么熟悉通信程序设计,再者我们的程序并发客户不多,所以还是采用的阻塞模式。
在具体的代码实现过程中,在传送文件过程中,遇到了文件接收是否成功的判断问题,最后仔细调试,发现了问题,在判断结束条件之前阻塞在了recv(),调换位置就好了。
在实现监听不同的客户连接请求时,由于监听一个请求之后,代码就进入该用户与服务器的消息处理循环中,只要这个客户不下线,将不会退出循环,这样将导致不能监听其他客户,后来采用了在监听线程中,当接受一个请求后,再开启一个一样的监听线程,这样就可以不断的监听了。