C++聊天室系统源代码-课程设计(1).doc_第1页
C++聊天室系统源代码-课程设计(1).doc_第2页
C++聊天室系统源代码-课程设计(1).doc_第3页
C++聊天室系统源代码-课程设计(1).doc_第4页
C++聊天室系统源代码-课程设计(1).doc_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C+聊天室系统源代码-课程设计C+聊天室系统源代码一、课程设计(论文)题目 基于客户/服务器模型的套接字编程 二、课程设计(论文)工作:自 2009 年 12 月 28 日起至 2010年 1 月 2 日止。三、课程设计(论文)的内容要求:1、 1)分析winsock编程技术,特别是Winsock使用; 2) 分析聊天程序的功能及其相关网络协议; 3) 给出V程序流程图和设计方案; 4) 选择开发环境,编译源程序; 5) 给出软件测试文档; 2、1)资料提交形式:课程设计报告一份、电子文档一份; 2)课程设计报告不少于3000字; 3)电子文档中必须至少有5份参考资料,压缩后以班为单位交电子版给老师,时间为第18周的周六(即:2010年1月2日) 4)成绩:出勤占10%、课程设计报告占40%、答辩占20%、参考资料占30%。 5)课程设计报告模板写附录。 目录一、需求分析 5二、系统结构 6三、网络通信 6四、程序代码 12五、运行截图 19六、测试结果及改进 20七、参考文献 21一、需求分析随着网络的普及,人与人之间的交也更多的是在网络上进行,于交流的实时性,即时通讯系统也被越来越多的人所使用。即时通讯系统除了普通的生活上的交流,也在商业交流中越来越受到重视,它可以是个很好的与客户之间即时交流的平台,在时间上它要比电子邮件更加具有实时性,而费用相对电话交流也要经济的多。在这种环境下,聊天软件作为一种即时通讯工具,得到了很好的发展。聊天室作为即时聊天软件的一种,已经在网络上得到广泛的使用。聊天室需要实现的功能:1、用户选择感兴趣的房间加入。2、输出聊天信息。3、显示聊天信息。4、自动显示聊天室内的所有在线成员。5、能够对所有人聊天。6、能够进行私聊。905C+聊天室系统源代码二、系统结构为了保证聊天信息的实时性和系统的效率,聊天室系统使用C/S结构,由服务器端程序和客户端程序两部分组成。服务器端和客户端通过TCP协议进行通信。其中,客户端用于显示来自服务器端的聊天信息,接收用户的输入信息并发送给服务器端,服务器端用于接受来自客户端的信息,处理之后转发给聊天室中的其他用户。三、网络通信1)Winsock I/O模型在Windows环境中,可以使用Windows 提供Winsock API来实现客户端与服务器端直接的通信。Winsock提供的 Recv和Send 函数是堵塞的,在程序调用这些函数时,如果网络IO未结束,线程将被堵塞起来,从而影响程序其它部分的正常工作。在服务器端,如果要实现同时处理来自多个客户端的连接,就必须为每一个连接到服务器端的客户端创建独立的线程来处理网络通信,从而不会互相影响。但如果这样设计,当有大量用户并发连接到服务器时,服务器会创建大量的线程,这时操作系统在调度这些线程的时候会占用大量的CPU资源,导致系统的效率非常低下。可见,并发量高的情况下,使用这种方来是非常不合适的。为了提高网络IO效率,Windows操作系统提供了选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O(Overlapped I/O)和完成端口(Completion Port)共五种I/O模型 select 模型: select模型是WinSock中应用最广泛的模型之一,核心就是select函数,它可用于判断套接字上是否存在数据,或者能否向一个套接字写入数据。这个函数可以有效地防止应用程序在套接字处于阻塞模式中时,send或recv进入阻塞状态;同时也可以防止产生大量的WSAEWOULDBLOCK错误select的优势是能够从单个线程的多个套接字上进行多重连接及I/O。这就避免了伴随阻塞套接字和多重连接的线程剧增。 WSAAsyncSelect 模型: 因为它是以消息为基础的,关键就是WSAAsyncSelect函数,将socket消息发送到hWnd窗口上,然后在那里处理相应的FD_READ、FD_WRITE等等消息。优点:WSAAsyncSelect和WSAEventSelect模型提供了读写数据能力的异步通知,但他们不提供异步数据传送,而重叠及完成端口提供异步数据的传送。而且它可以在系统开销不大的情况下同时处理很多连接,而select模型还需要建立fd_set结构。 缺点:必须要使用一个窗口接收消息,如果处理成千上万的套接字就力不从心了。 WSAEventSelect 模型: 这个也是以时间为基础的网络事件通知,但是与WSAAsyncSelect不同的是,它主要是由事件对象句柄完成的,而不是通过窗口。优点:不需要窗口。缺点:每次只能等待64个事件,所以处理多个套接字时有必要组织一个线程池;所以伸缩性就不如后面的完成端口了。 重叠模型: 这个模型可以使程序能达到更佳的系统性能。基本设计原理就是让应用程序使用重叠的数据结构,一次投递一个或多个I/O请求。针对这些提交的请求,在他们完成之后,应用程序可为他们提供服务。它又分为两种实现方法:在事件中使用,还有就是完成例程。 完成端口: 完成端口提供了最好的伸缩性,往往可以使系统达到最好的性能,是处理成千上万的套接字的首选。从本质上说,完成端口模型要求创建一个windows完成端口对象,该对象通过指定数量的线程,对重叠I/O请求进行管理,以便为已经完成的重叠I/O请求提供服务。在上面五个网络IO模型中,完成端口的效率最高,可同时处理数万个并发连接,要利用“完成端口”实现一个高效,稳定的网络通信模块,需要解决四个基本问题:1、粘包。2、多线程导致接受和发送包时乱序。3、缓存区满。4、客户端断开连接时指针异常解决上面4个问题需要编写一定量的代码,由于时间匆忙,本次编写的聊天室程序使用较为简单Select模型来实现网络通信。2)Select模型通信的使用int select( int nfds,fb_set FAR * readfds, fb_set FAR * writefds, fb_set FAR * exceptfds, const struct timeval FAR * timeout); 其中,第一个参数n f d s会被忽略。之所以仍然要提供这个参数,只是为了保持与早期的B e r k e l e y套接字应用程序的兼容。三个f d _ s e t参数:一个用于检查可读性(readfds),一个用于检查可写性(writefds),另一个用于例外数据(exceptfds)。从根本上说,fb_set数据类型代表着一系列特定套接字的集合。其中, readfds集合包括符合下述任何一个条件的套接字: 有数据可以读入。 连接已经关闭、重设或中止。 假如已调用了listen,而且一个连接正在建立,那么accept函数调用会成功。 writefds集合包括符合下述任何一个条件的套接字: 有数据可以发出。 如果已完成了对一个非锁定连接调用的处理,连接就会成功。 最后,exceptfds集合包括符合下述任何一个条件的套接字: 假如已完成了对一个非锁定连接调用的处理,连接尝试就会失败。 有带外(out-of-band,OOB)数据可供读取。 例如,假定我们想测试一个套接字是否“可读”,必须将自己的套接字增添到readfds集合,再等待select函数完成。s e l e c t完成之后,必须判断自己的套接字是否仍为readfds集合的一部分。若答案是肯定的,便表明该套接字“可读”,可立即着手从它上面读取数据。在三个参数中(readfds、writefds和exceptfds),任何两个都可以是空值( N U L L);但是,至少有一个不能为空值!在任何不为空的集合中,必须包含至少一个套接字句柄;否则, select函数便没有任何东西可以等待。最后一个参数timeout对应的是一个指针,它指向一个timeval结构,用于决定select最多等待I / O操作完成多久的时间。如timeout是一个空指针,那么select调用会无限期地“锁定”或停顿下去,直到至少有一个描述符符合指定的条件后结束。对timeval结构的定义如下: Struct timeval Long tv_sec; Long tv_usec; 其中,tv_sec字段以秒为单位指定等待时间; tv_usec字段则以毫秒为单位指定等待时间。若将超时值设置为( 0 , 0),表明select会立即返回,允许应用程序对select操作进行“轮询”。出于对性能方面的考虑,应避免这样的设置。select成功完成后,会在fd_set结构中,返回刚好有未完成的I / O操作的所有套接字句柄的总量。若超过timeval设定的时间,便会返回0。不管由于什么原因,假如select调用失败,都会返回socket_error。用select对套接字进行监视之前,在自己的应用程序中,必须将套接字句柄分配给一个集合,设置好一个或全部读、写以及例外FD_SET结构. 主要涉及到几个宏操作: FD_CLR(s, *set):从s e t中删除套接字s。 FD_ISSET(s, *set):检查s是否s e t集合的一名成员;如答案是肯定的是,则返回T R U E。 FD_SET(s, *set):将套接字s加入集合s e t。 FD_ZERO ( * set ):将s e t初始化成空集合。C+聊天室系统源代码3)数据包和通信协议的定义1、数据包格式在使用TCP协议进行网络通信是,一个需要解决的最基本的问题就是粘包。 TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。和粘包类似,发送方一次发送的数据,接受方也可能分成多次来接收。为了解决上述问题,实现准确的接受数据包,需要为数据包设置一定的机构,如下:1、数据包大小占4字节,值为 4+n 字节2、数据包类型占4字节,值为包的类型3、数据占n字节,内容内传输的数据在接受数据包时,先请求接受4个字节,当4个字节全收到时,可得出数据包类型和数据的总长度, 然后请求接受4+n字节,当4+n字节全收到时,就可以把这8+n个字节的时间拼成一个完整的数据包。2)通信协议(数据包分析)接受到一个完整的数据包后,就对其进行分析,取出,数据包的类型,然后根据不同的类型进行处理,主要包类型结构如下:包类型包内容登陆用户名长度 + 用户名用户列表用户数量 + 用户名若干发送消息消息长度 + 消息内容私聊目标用户名 + 消息登录结果标志(T/F)+ 原因心跳包为了判断用户是否断开连接,可以设定一心跳包,即服务器每隔一段时间向客户端发一次,若连续多次未收到回应,则断定此客户端中断了连接。 四、程序代码1)开发环境操作系统:Windows 7IDE:Visual Studio 2008语言:C+框架:MFC2)基本流程图C+聊天室系统源代码数据包类:class CBufferpublic:CBuffer(void);CBuffer(void);void SetType(int type);void SetHead(); /设置包头,即包大小void AddInt(int data);void AddData(char* data,int len); /向buf中添加各种数据void AddString(string & str);void AddCString(CString& str);int GetType(); /获取包类型int GetPackageSize();/获取包大小int GetInt();void GetData(char* buf,int len); /从buf中获取各种数据string GetString();CString GetCString();int Recv(SOCKET s);int Send(SOCKET s);private:char* m_buf; /缓存区int m_write_index; /写入游标int m_readIndex; /读取游标int m_used;网络通信线程工作函数:DWORD CServer:WorkThread( LPVOID param )CServer* pServer = (CServer*)param;fd_set read_set;while (true)FD_ZERO(&read_set);FD_SET(pServer-m_server_socket,&read_set);for (list:iterator p = pServer-m_users.begin();p != pServer-m_users.end(); +p)FD_SET(*p)-GetSocket(),&read_set);if (select(0,&read_set,NULL,NULL,NULL) 0)if(FD_ISSET(pServer-m_server_socket,&read_set) /有连接加入sockaddr_in addrRemote;int nAddrLen = sizeof(addrRemote);SOCKET client_socket = accept(pServer-m_server_socket,(sockaddr*)&addrRemote,&nAddrLen);if (client_socket) /为客户端分配CUser类CUser* pUser = new CUser(client_socket);pServer-m_users.push_back(pUser);for (list:iterator p = pServer-m_users.begin();p != pServer-m_users.end(); +p)int ret = (*p)-Recv();switch(ret)case RECV_ERROR:(*p)-Delete(); /发生异常,删除break;case RECV_FINISH:(*p)-AnalyseBuffer(); /完成,分析数据包break;return 0; 接收数据:int CBuffer:Recv( SOCKET s )if (m_used 4) /接受数据包头,即数据包大小int len = recv(s,m_buf + m_used,4 - m_used,0);if (len = -1)return RECV_ERROR;m_used += len;if (m_used = 4)int need = GetPackageSize();if (need 512 - 4) /错误的数据包大小return RECV_ERROR;elseint need = GetPackageSize(); /接受数据类型和数据int len = recv(s,m_buf + m_used,need - m_used + 4,0);if (len = -1)return RECV_ERROR;C+聊天室系统源代码m_used += len;if (m_used = need + 4)return RECV_FINISH;return RECV_UNFINISH;广播消息:void CUser:BroadcastMsg( CString& msg )for (map:iterator p = m_users.begin();p != m_users.end(); +p)CBuffer* buf = new CBuffer();buf-SetType(BROADCAST_MSG);buf-AddCString(msg);int len = buf-GetPackageSize();p-second-Send(buf);处理数据包enum /数据包类型LOGIN = 1,SEND_MESSAGE,SEND_WHISPER,LOGOUT,LOGIN_S,LOGIN_F,BROADCAST_MSG,WHISPER_MSG,GET_USERS,SEND_USERS;void CUser:AnalyseBuffer()if (m_buf)int type = m_buf-GetType();switch(type)case LOGIN:m_name = m_buf-GetCString();CBuffer* buf = new CBuffer();if (AddUser(m_name,this)buf-SetType(LOGIN_S);elsebuf-SetType(LOGIN_F);buf-AddCString(CString(重名);Send(buf);break;case SEND_MESSAGE:CString msg = m_buf-GetCString();msg = m_name + CString(: ) + msg;BroadcastMsg(msg);:CSt

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论