网络多人聊天系统的设计与实现【毕业论文】

网络多人聊天系统的设计与实现【毕业论文】

ID:464480

大小:606.00 KB

页数:42页

时间:2017-08-05

上传者:U-944
网络多人聊天系统的设计与实现【毕业论文】_第1页
网络多人聊天系统的设计与实现【毕业论文】_第2页
网络多人聊天系统的设计与实现【毕业论文】_第3页
网络多人聊天系统的设计与实现【毕业论文】_第4页
网络多人聊天系统的设计与实现【毕业论文】_第5页
资源描述:

《网络多人聊天系统的设计与实现【毕业论文】》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库

本科毕业设计(20届)网络多人聊天系统的设计与实现 摘要网络多人聊天系统,属于即时通讯的范畴,是一种能为多人提供即时交流的聊天平台。网上聊天系统作为一种方便人与人之间联系的实用系统,为我们的生活和学习带来了极大的方便,也提高了工作的效率。目前人们交流方式多种多样,聊天软件凭其友好的外观、使用的便利等特点博得现代人的青睐。本毕业设计论文介绍以C++语言设计和实现一个网络聊天系统的过程。本系统采用C/S架构设计,程序主要分为两部分:客户端和服务器端。客户端会显示在线的所有成员,服务器提供成员之间的私聊以及群聊功能。程序界面简洁,布局合理,操作方便。利用Socket网络开发技术实现客户端和服务器端的连接访问,实现网络功能;通过对数据流操作的合理设计,实现信息传递、接受和数据保存。同时,采用多线程、多任务的设计思想,开发出性能稳定的服务器。完整地实现系统的功能。关键字:聊天系统;C++Builder;Socket AbstractNetworkmultiplayerchatsystem,belongingtothescopeofInstantMessaging,isakindofcommunicationplatformforprovidingpeopletoinstantchat.Networkchatsystemasapracticalsystemmakethecommunicationbetweenpeoplemoreconvenientandmakeourlivingandlearningmoreconvenient,alsoimproveworkefficiency.Presently,therearemanywaysforpeoplecommunicate,chattingsoftware,relyingonit’sfriendlyappearance,easeofuseandsowonthefavorofthemodernhuman.ThisgraduationthesisofdesignintroductionhowtousetheC++languagedesignandcarryoutnetworkchatroom,thesystemadoptiontheC/Sstructuredesign,theentireprojectisdividedintoclientandserver,theprograminterfaceissimple,reasonablelayoutandconvenientoperation.Theclientwillshowallthemembersonline,theserverprivatechatbetweentwomembersandagroupchat.WemakeuseoftheSockettechnologytointerconnecttheserverandclientbynet-work;organizethedatabygooddesignofdatastreamtosentoracceptthemessagebetweenusersandtorecordthemessageforever;wealsousethemulti-threadmulti-actiontomaketheprogramwithmanyfunction.Keywords:Chat;C++Builder;Socket 目录1引言-1-1.1课题的来源-1-1.2本课题的目的、内容、要求-1-1.2.1目的-1-1.2.2内容-2-1.2.3要求-2-2系统的开发和运行环境-3-2.1C++Builder6简介-3-2.2开发技术的选择-3-3需求分析-5-3.1项目概述-5-3.1.1产品描述-5-3.1.2产品功能-5-3.2功能需求-7-3.2.1用户登录-7-3.2.2公聊-7-3.2.3私聊-7-3.2.4聊天记录-8-3.2.5用户注销-8-3.3质量性能需求-9-3.3.1容错性需求-9-3.3.2稳定性需求-9-3.3.3软件设计可靠性-9-3.3.4安全性-9-3.3.5扩展性-9-3.3.6易用性-9-4概要设计-10-4.1目的-10- 4.2运行环境-10-4.3条件与限制-10-4.4网上信息传播模式-10-4.5C++Builder中常用的Socket控件-11-4.6程序体系结构-11-5详细设计-12-5.1设计流程-12-5.1.1客户端与服务器端之间传递的主要消息-12-5.1.2服务器端程序设计流程-12-5.1.3客户端程序设计流程-13-5.2服务器端程序模块-13-5.2.1建立连接程序模块-13-5.2.2服务器监听模块-16-5.2.3服务器端读取及发送信息模块-19-5.2.4断开连接模块-22-5.3客户端程序模块-23-5.3.1建立连接程序模块-23-5.3.2输入交谈信息模块-26-5.3.3客户端读取及发送信息模块-27-5.3.4断开连接模块-28-6系统测试-30-7结论-36-致谢-37-参考文献-38- 1引言1.1课题的来源信息化是目前世界发展的局势,是推动经济社会变革的重要力量。大力推进信息化,是覆盖我国现代化建设全局的战略举措,是贯彻落实科学发展观、全面建设小康社会、构建社会主义和谐社会和建设创新型国家的迫切需求和必然选择。而作为信息传递的一个重要平台,互联网扮演着举足轻重的角色。网络聊天工具,作为一种方便、即时的信息交流方式,渐渐地受到越来越多人的青睐。同时,各种各样的网络聊天服务程序也大量出现,比较著名的有NetMeeting、OICQ、ICQ、MSN-Messenger,以及国内最流行的腾讯QQ等[1]。腾讯无疑是国内即时通讯市场的霸主,自从99年进入即时通讯领域并迅速占市场之后,其在国内用户数量始终高居榜首,即使近几年面对微软MSN的强大攻势,腾讯QQ的时常占有率依然稳步增长。腾讯的成功与其对QQ的不断创新和完善是分不开的。网络聊天室也是属于即时通信软件的范畴,在网络应用中出现的较早。网络聊天室以其特有的多人即时聊天方式被大家喜欢,很多时候能给使用者们在工作或学习中带来更高的效率。其开发技术和设计思想都已经得到成熟的发展,并且已经具有许多成功的案例[2]。在开发技术上已经没有什么技术难题,并且具有广泛的应用和市场空间。目前,国内各大门户网站都架设了大型聊天室。如:新浪聊天室、中华网社区聊天室、搜狐等都成为网民们的热门聊天室。1.2本课题的目的、内容、要求1.2.1目的网络聊天室是一项应用广泛,并且实用性强的一个应用,虽然其功能简单,但是在其开发技术和过程中,能够充分学习和使用C++的技术,开发各界面,包括登入、注册、注销、公聊、私聊等界面;实现网络的功能使用,包括通信协议、网络端口等[3]。并且在开发过程当中,能充分的使用和体会面向对象技术的原理和方法。 所以该项目可以使自己在C++技术方面得到全面应用,使自己项目开发能力的一个很好体现。1.2.2内容一个网络多人聊天室,必须有一个稳定的服务器,以及客户的的登录端。客服端要具有用户连接、登录等功能,在登入成功后,要显示出公聊界面,在新用户上线和用户下线时要更新每个客户端在线用户列表名,并且可以和在线的用户实现一对一的私聊功能。在服务器端实现不断的监听客户的连接,并且根据客户端发送过来的不同信息,如登入信息、公聊信息、私聊信息、退出信息等,服务器端要作出不同的响应。1.2.3要求利用TServerSocket和TClientSocket两个控件,创建网上多用户聊天的程序,创建的应用程序实现了目前市场上一些商用对话程序的大部分功能,如广播和对多个用户选择性发送消息、允许用户选择自己喜欢的服务器等。在代码的编写上要充分体现C++面向对象开发思想和多态性,提高代码的重用性和高效性。 2系统的开发和运行环境2.1C++Builder6简介Borland公司在推出DOS环境下的C语言编译器TurboC及TurboC++后成为第二个在PC领域推出商业化C++的编译器厂商。现今的BorlandC++Builder是一种快速应用程序开发(RAD,RapidApplicationDevelopment)工具,基于面向对象的C++语言。可以说,C++Builder是集C++语言的高效性和RAD开发工具的快速性等优点完美结合的产物,也是基于Pascal程序设计语言的Delphi强大功能的合理扩展。用户可以利用C++Builder提供的IDE(IntegratedDevelopmentEnvironment)开发环境来帮助完成整个应用程序的设计,而不需要再依靠其他工具,以便使程序开发环境能够简单一致,提高整体的工程开发效率。总之,C++Builder是一种全新的软件开发工具,它的优点是不言而喻的。2.2开发技术的选择本课题用TServerSocket控件处理客户端发来的socket连接。Active属性决定程序运行后,该组件是否马上启动服务器套接字进行监听。ServiceType决定了与服务器的链接是非阻塞还是为每个连接创建一个线程。Service属性决定了该服务器组件提供什么服务。常见的服务有FTP、WWW、SMTP、HTTP等,不过设置了端口属性,就不需要设置此属性了。TClientSocket控件是一个简单的套接字(socket)组件,使用这个控件可以实现与服务器建立连接并完成简单的套接字通信。除此之外,使用此控件还可以按照流的方式发送数据给服务器,轻松地完成表中数据的传送。Socket是一种用于表达两台机器之间连接“终端”的软件抽象。对于一个给定的连接,在每台机器上都有一个Socket,可以想象一个虚拟的"电缆"工作在两台机器之间,“电缆”插在两台机器的Socket上[4]。简单的说,一台计算机上的socket同另一台计算机通话创建一个通信信道,程序员可以用这个信道在两台机器之间发送数据。当你发送数据时,TCP/IP协议栈的每一层都给你的数据里添加适当的报头。socket 像电话听筒一样在电话的任意一端,两人通过一个专门的信道来进行通话和接听。会话将一直进行下去直到两人决定挂断电话,否则各自的电话线路都会占线[4]。TCP和UDP扮演同样的角色,但是实现是不同的。两者都接收传输协议数据包并把它们传递到表示层。TCP把信息分解成数据包(datagrams)并在接收端重新组装起来。它还对丢失的数据包进行重新传输的请求。TCP减少了上层的担忧。UDP没有组装和重传请求的功能。它只是传输数据包。更高层的层必须确保信息的完整性以及组合顺序的正确性。 3需求分析3.1项目概述3.1.1产品描述一个操作简单、界面友好、运行稳定的网络聊天室对于小型局域网内的用户可以起到很好的交流作用。本聊天室是一个可以让许多用户同时互相通信的网上园地,个人用户可以通过网上聊天室将自己融入整个主流社会中。由于目前上网的一般都是具有高学历的新一代创业者,因此,在网上聊天室这个虚拟的社会中,可以结交各层次各地方的朋友,从而为自己获取更多的知识和更多的阅历。人们日常生活中越来越多地使用这项新的技术来为自己的工作和学习服务。该聊天室是为许多喜爱上网聊天的朋友开发设计的,希望能在现有的众多聊天室中给大家多一种的选择。和许多大型的网络聊天室相比该聊天室比较简单,但能实现聊天的基本功能,点对多聊天,点对点聊天等,基于时间和技术的原因,很遗憾传送文件的功能尚未实现。本聊天室主要由登录界面、聊天室界面、私聊界面、查看聊天内容界面等构成。整个设计简洁明了、实用,使用简单。3.1.2产品功能本项目具有以下主要功能客户端:1用户登录功能,实现用户登录服务器,自定义昵称。2群聊功能,实现用户与在线所有成员聊天方式。3私聊功能,实现用户与在线成员一对一的聊天方式。4查看聊天记录,实现用户查看历史聊天内容。服务端:1可以不断地监听来自客户端的连接请求2显示用户的登录信息3显示用户的注销信息根据上述功能,得到如图3-1的系统功能模块图。 启动启动等待客户端登陆客户端用户创建子套接字Message服务端客户端Message登陆成功连接上发送message显示找到客户端并显示登陆失败发送请求图3-1网络多人聊天系统功能模块图 3.2功能需求3.2.1用户登录需求描述:用户填写主机名和昵称,进入聊天室主界面执行者:用户前置条件:无后置条件:无正常过程:1向服务器发送连接请求。2返回客户端一个响应,提示登录成功,弹出聊天界面。3刷新其他在线用户的用户列表信息。3.2.2公聊需求描述:用户在聊天室公聊界面的发言内容,聊天室中的其他在线用户可以正常接受并正确显示(包括发送的表情和用户设置的字体颜色)。执行者:登入用户前置条件:用户登录成功后置条件:无正常过程:1客户端用户登入成功后,启动客户端功能线程。2客户端将用户发送的信息打包传送给服务端。3服务端进行数据分析后,并将用户的信息进行正确的处理。4传给处在聊天室的所有在线其他用户并正常显示。3.2.3私聊需求描述:用户可选择聊天室的在线用户列表中其他用户聊天。并且对方有消息提示,双方发送的信息对于聊天室的其他用户是不可见的。执行者:用户前置条件:用户登录成功,私聊的对方在线 后置条件:无正常过程:1客户端与服务端建立通讯。2客户端将用户发送的信息打包传送给服务端。3服务端进行数据分析,将用户的信息传给目的用户。4目的用户收到服务器的信息后,正确的显示并保存。3.2.4聊天记录需求描述:用户可以查询聊天的历史纪录,包括公聊记录和私聊记录。执行者:用户前置条件:用户正确登入后后置条件:无正常过程:1发送、接收信息的同时将信息保存在本地文件中。2信息发送框中可以查询历史聊天记录。3从本地文件中读取信息,并显示在记录显示匡中。3.2.5用户注销需求描述:用户在正常情况下退出聊天室。执行者:用户前置条件:用户成功登录聊天室后置条件:无正常过程:1向服务器发送用户登出消息。2服务器进行用户退出的数据处理。3服务器向聊天室其他用户发送登出消息。 3.3质量性能需求3.3.1容错性需求当出现代码异常时,出现正确的提示,不会影响整个系统的运行。用户之间信息传输不会因为数据封装问题出现丢失或乱码。因用户的误操作产生的异常应做出及时的处理或信息提示。3.3.2稳定性需求软件应正常完成所有功能需求。服务器端应长时间运行无故障,故障修复时间一般控制在3小时内,除非出现恶性攻击、病毒破坏、系统资源不足、硬件故障、操作系统或数据库系统崩溃网络中断等情况,要求系统连续运行一周,不允许出现内存泄露。要求系统可以承受告警风暴和大数据量的冲击,不出现内存溢出等现象。3.3.3软件设计可靠性要确定合适的模块粒度,各模块之间要求松耦合,高内聚;从而使整个系统的可靠性和稳定性得到提高,并且便于系统维护,和新模块的添加。3.3.4安全性安全性是防止网络方面的攻击3.3.5扩展性系统建成后,应在现行系统上不需要做大的改动或不影响整个系统结构,就可以增加功能模块,这就必须在系统设计时留有接口,使其具有可扩展性和维护性。3.3.6易用性系统操作的界面必须简洁、操作方便易用、设计合理 4概要设计4.1目的该阶段目的在于明确网络聊天室系统的软件总体结构,各个软件模块的功能说明,系统运行配置与应用方式以及使用的关键技术。4.2运行环境系统的开发环境为C++Builder6,测试和运行环境为WindowsVista,2GB内存。4.3条件与限制软件设计应当表现出层次结构,它应巧妙地利用各个软件部件之间的控制关系。设计应当是模块化的,即该软件应当从逻辑上被划分成多个部件,分别实现各种特定功能和子功能。设计最终应当给出具体的模块(例如子程序或过程),这些模块就具有独立的功能特性。应当应用在软件需求分析期间得到的信息,采取循环反复的方法来获得设计。4.4网上信息传播模式根据信息接收用户数目的不同,可以将网上信息传播模式分成广播、多播和单播三中模式[5]。广播即把信息传送给所有已连接用户。在这种模式下,如果向各个用户逐个发送消息,网络开销就非常大。在实际应用中,为了节约网络资源,提高效率,一般要先根据用户IP地址将其分组,然后对每个分组只发送一条信息到该分组对应的服务器,然后再由该服务器在其对应的局域网内进行广播。广播模式因为需要消耗大量网络资源,一般用于传输系统信息[6]。 多播即只对多个用户而不是所有用户传送消息。在接收用户数目较少时,可以采用逐个发送的方式;在接收用户数目较大时,也需要采用广播模式中类似的分组方式以节约网络资源。在多播模式下,未分组前需要再传送信息的头部添加上墓地IP地址列表,分组后海需要添加分组传送墓地服务器的IP地址,传送到服务器后由服务器取出下级地址列表进行多播。多播模式常用于网络会议[7]。单播是多播的一种特例,只将信息发送给一个单独的用户。因此,在这种模式下不需要对用户进行分组,直接将目的IP地址添加在信息头部即可发送。网络聊天中的单了就是采用这种模式。4.5C++Builder中常用的Socket控件一个IP地址和一个端口号的组合称为一个套接字(Socket),一个套接字可以唯一地标识整个Internet中的一个网络进程[8]。属于套接字和端口号有时是可以互相转换的,实际上,知名服务器经常称为知名套接字。一对套接字,一个用于接收主机,另一个用于发送主机,可以定义面向连接协议的一次连接[9]。网络聊天客户端和服务端要进行消息相互传递,就必须先通过Socket来建立网络连接,在C++Builder中有两个常用的Socket控件ClientSocket和ServerSocket,他们封装了WinSocketAPI函数[10],可以用于建立客户端和服务端的Socket连接。4.6程序体系结构本程序采用Client/Server体系结构,即客户机服务器客户端体系结构。C/S结构广泛应用于构造数据库系统,它包括连接在一个网络中的多台计算机。处理应用程序并请求另一台计算机服务的计算机称为客户机,处理数据库的计算机称为服务器[11]。其特点是客户端只装载应用软件,而把数据库放在服务器上,这样就可以使客户端不需要存储数据,从而简化客户端软件。借鉴这种设计思路,本课题也采用C/S结构。聊天服务器专门用于监控用户状态和转发消息,当客户端进行多播或广播时,客户端只需把接收消息的用户序列和消息文本发送到服务器,然后由服务器转发到各个用户,而不需要向每个用户建立连接后发送消息,这样就大大减轻了客户端的任务[12]。 5详细设计5.1设计流程5.1.1客户端与服务器端之间传递的主要消息客户端向服务器端:1登录时将用户昵称传给服务器。2在广播模式下发送消息时将消息正文直接传送给服务器。3在多播模式下发送消息时将目的用户名序列附加在消息正文前,并且在用户名序列前加上多播标记后将整个字符串作为消息传送给服务器。服务器端向客户端:1用户登录时向用户发送欢迎信息,并在欢迎信息后面附加已经登录的用户名序列。2用户登录时向其他所有已登录用户发出通知。3收到用户的广播消息则向所有登录用户转发消息。4收到用户的多播消息则向用户列表中的用户转发消息。5有用户注销时向所有在线用户发出通知。5.1.2服务器端程序设计流程按照服务器打开到关闭的顺序,服务器端程序设计流程如下:1打开服务器,使其处于监听状态。2某用户登录,显示欢迎信息,并将在线用户列表发送给该用户,在日志列表中记录该用户的登录信息,将该用户调价到在线用户列表中。3转发消息:有用户发送广播消息,则向所有用户转发该消息;若是发送多播消息,则只对目的用户转发。4某用户注销,则向所有在线用户发送通知,在日志列表中记录该用户的注销信息并从登录用户列表中将该用户删除。5所有用户注销,服务器可正常关闭,若仍有在线用户,则关闭前询问是否真的要关闭。 5.1.3客户端程序设计流程按照用户从登录到注销的顺序,客户端程序设计流程如下:1连接服务器,把用户昵称发送给服务器,建立连接2接收服务器消息,对不同的消息采用不同的处理方法:⑴收到服务器欢迎登录喜爱系,从中提取出已登录用户列表,并逐个添加到用户列表中。⑵收到用户发送来的消息,显示在消息框中。⑶收到其他用户注销的消息,从用户列表中删除该用户。3发送消息,对不同的消息发送方式采用不同的处理方法:⑴广播消息,则直接将消息发送给服务器。⑵多播消息,首先在消息正文前添加目的用户名序列,然后再将多播消息标志添加到用户名序列前。4断开连接。5.2服务器端程序模块5.2.1建立连接程序模块1.相关成员函数和变量的定义在窗体的头文件chatserveru.h中,加入如下代码。(为节省篇幅,没有列出C++Builder自动产生的代码)。enumServerStatus{SS_NOT_RUNNING,SS_RUNNING};enumLogEntryType{LET_WARNING,LET_ERROR,LET_SIGNON,LET_SIGNOFF};enumServerNotification{SN_LOGON,SN_LOGOFF,SN_PUBLIC_MSG,SN_PRIVATE_MSG};#defineCHAT_SERVER_PORT5790private://UserdeclarationsServerStatusCurrentServerStatus;voidSetServerStatus(ServerStatus_serverstatus);public://Userdeclarations__fastcallTChatServerForm(TComponent*Owner); 在上述代码中,首先定义了三个枚举类型来增加程序的可读性。第一个ServerStatus用于表示服务器当前的状态,因为在程序中,服务器只有两种状态,一种是运行,另一种是没有运行,因此两个可能的枚举值分别为SS_NOT_RUNNING何SS_RUNNING;第二个枚举类型是LogEntryType,在程序中我们用它来调用函数以便在服务器日志中增加一条日志信息。所有可能的类型包括警告、错误、登录提示和注销提示;第三个枚举类型是ServerNofificafion,它用来发送服务器必须处理的事件,这种事件包括登录、注销、广播消息和多播消息。我们通过将每个可能的状态和提示用相应的枚举类型来表示,就可以创建一个中心程序,并减少程序中重复的代码。变量CurrentServerStatus用于保存服务器的状态,并验证“开始/停止”菜单项,函数SetServerStatus用来设置该变量。2在窗体的构造函数中初始化变量在chatserveru.spp文件中,在窗体的构造函数中加入如下代码,用于设置变量的初始值。__fastcallTChatServerForm::TChatServerForm(TComponent*Owner):TForm(Owner){CurrentServerStatus=SS_NOT_RUNNING;MainServerSocket->Port=CHAT_SERVER_PORT;}在上述代码中,首先将表示服务器状态的CurrentServerStatus变量设置成SS_NOT_RUNNING,表示服务器没有进入监听状态。3启动服务器单击“开始”菜单,创建次菜单的OnClick时间相应函数,在其中加入如下代码。void__fastcallTChatServerForm::StartStopServerClick(TObject*Sender){switch(CurrentServerStatus){caseSS_NOT_RUNNING:{ MainServerSocket->Open();SetServerStatus(SS_RUNNING);AddLogEntry(LET_WARNING,"服务器已启动");StartStopServer->Caption="停止";break;}caseSS_RUNNING:{MainServerSocket->Close();SetServerStatus(SS_NOT_RUNNING);AddLogEntry(LET_WARNING,"服务器已停止");StartStopServer->Caption="开始";break;}}UpdateStatusBar();}在上述时间相应函数中,首先判断CurrentServerStatus变量值。如果CurrentServerStatus变量等于SS_NOT_RUNNING,表示服务器套接字没有启动,这时就调用MainServerSocket组件的Open方法,启动套接字,使服务器进入监听状态,调用Open方法等同于将该组件的Active属性设置为true,然后调用SetServerStatus函数将CurrentServerStatus变量设置成SS_RUNNING,接着调用AddLogEntry函数,在服务器窗口中显示服务器已启动,并在开始菜单显示“停止”。如果CurrentServerStatus变量等于SS_RUNNING,表示服务器套接字已经启动,这时候调用MainServerSocket组件的Close方法,关闭套接字,调用Close方法等同于将该组件的Active属性设置false,然后调用SetServerStatus函数将CurrentServerStatus变量设置成SS_NOT_RUNNING,接着调用AddlogEntry函数,在服务器窗口中显示服务器已停止,并在开始菜单显示“开始”。最后通过调用UpdateStatusBar函数,在状态栏上现实有多少个用户连接。4相应客户的连接 当客户连接到服务器上时,触发TServerSocket的OnClientConnect时间。在对象监视器中双击MainServerSocket的OnClientConnect事件,创建此事件相应函数,在其中加入如下代码。void__fastcallTChatServerForm::MainServerSocketClientConnect(TObject*Sender,TCustomWinSocket*Socket){Socket->Data=0;AddConnectionToListView(Socket);UpdateStatusBar();}每当一个客户请求连接时就激活TServerSocket控件的OnClientConnect事件。在该事件中,我们启动一个包含Windows套接字的TCustomWinSocket对象。然后初始化此对象的数据成员,使它在以后使用时成为一个已知的数值,在这种情况下应保证它的Data为0,TCustomWinSocket的成员变量Data对于创建多线程网络程序非常有用。接下来我们调用AddConnectionToListView在列表中添加一个新的连接。最后我们调用UpdatStatusBar函数来更新服务器的状态栏,表明有一个新的客户连接。至此,服务器打开后有用户连接上时,才真正建立了服务器和客户端的连接。5.2.2服务器监听模块1相关成员函数的定义在窗体的头文件chatserveru.h中,加入如下代码。private://UserdeclarationsServerStatusCurrentServerStatus;voidSetServerStatus(ServerStatus_serverstatus);voidAddConnectionToListView(TCustomWinSocket*ClientSocket);voidRemoveConnectionFromListView(TCustomWinSocket*Socket);voidUpdateStatusBar(boolDecUse=false);voidAddLogEntry(LogEntryTypelet,AnsiStringEntryText);voidSetUserBySocket(TCustomWinSocket*Socket,constAnsiString&UserNickName);voidGetUserBySocket(TCustomWinSocket*Socket,AnsiString&UserNickName);intGetSocketNoByUser(AnsiStringUser);TListItem*ListItemBySocket(TCustomWinSocket*Socket);EntryTypeToText函数用于将入口类型转变为入口信息文本。 2监听内容服务器打开后,就处于监听状态,主要监听内容有以下几项:⑴用户登录,将其用户名加到已登录用户列表voidTChatServerForm::AddConnectionToListView(TCustomWinSocket*ClientSocket){//客户第一个要发送的就是他的绰号AnsiStringUserNickName="未知";TListItem*TempItem=ConnectionsListView->Items->Add();TempItem->Caption=UserNickName;TDateTimeCurrentDT(TDateTime::CurrentDateTime());TempItem->SubItems->Add(ClientSocket->RemoteHost);TempItem->SubItems->Add(CurrentDT);TempItem->Data=ClientSocket;}⑵将用户登录信息加入日志列表voidTChatServerForm::AddLogEntry(LogEntryTypelet,AnsiStringEntryText){TListItem*item=LogEntryListView->Items->Add();AnsiStringentrytype;EntryTypeToText(let,entrytype);item->Caption=entrytype;item->SubItems->Add(EntryText);item->SubItems->Add(TDateTime::CurrentDateTime());}voidEntryTypeToText(LogEntryTypelet,AnsiString&text){switch(let){caseLET_WARNING:{text="提示";break;}caseLET_ERROR:{ text="错误";break;}caseLET_SIGNON:{text="登录";break;}caseLET_SIGNOFF:{text="注销";break;}default:{text="未知";break;}}}⑶用户注销,将其用户名从已登录用户列表中删除voidTChatServerForm::RemoveConnectionFromListView(TCustomWinSocket*Socket){TListItem*item=ListItemBySocket(Socket);if(item)ConnectionsListView->Items->Delete(item->Index);}TListItem*TChatServerForm::ListItemBySocket(TCustomWinSocket*Socket){for(inti=0;iItems->Count;i++){if(reinterpret_cast(ConnectionsListView->Items->Item[i]->Data)==Socket){returnConnectionsListView->Items->Item[i];}}returnNULL;}⑷更新状态栏显示voidTChatServerForm::UpdateStatusBar(boolDecUse/*=false*/){ switch(CurrentServerStatus){caseSS_NOT_RUNNING:{ChatServerStatusBar->SimpleText="选择开始菜单进入聊天";break;}caseSS_RUNNING:{if(DecUse)ChatServerStatusBar->SimpleText=IntToStr(MainServerSocket->Socket->ActiveConnections-1)+"个客户已连接";elseChatServerStatusBar->SimpleText=IntToStr(MainServerSocket->Socket->ActiveConnections)+"个客户已连接";break;}}}5.2.3服务器端读取及发送信息模块1相关成员函数和变量的定义在窗体的头文件chatserveru.h中,加入如下代码。private://UserdeclarationsServerStatusCurrentServerStatus;voidSetServerStatus(ServerStatus_serverstatus);voidAddConnectionToListView(TCustomWinSocket*ClientSocket);voidRemoveConnectionFromListView(TCustomWinSocket*Socket);voidUpdateStatusBar(boolDecUse=false);voidAddLogEntry(LogEntryTypelet,AnsiStringEntryText);voidSetUserBySocket(TCustomWinSocket*Socket,constAnsiString&UserNickName);voidGetUserBySocket(TCustomWinSocket*Socket,AnsiString&UserNickName);intGetSocketNoByUser(AnsiStringUser);TListItem*ListItemBySocket(TCustomWinSocket*Socket);voidBroadcastMessage(AnsiStringMessage,TCustomWinSocket*ExcludeSocket);voidMulticastMessage(AnsiStringMessage,AnsiStringUserList);//*!voidSendNotification(ServerNotificationsn,AnsiStringadditional,TCustomWinSocket*ExcludeSocket); 2服务器端读取及发送信息程序当合客户端建立连接护,接下来是TServerSocket组件的OnClientRead事件,当客户得知他已经成功地与服务器建立连接后,将发送他希望在绘画进程中使用的别名给服务器,这时将触发OnClientRead事件。此是将相应函数代码如下。void__fastcallTChatServerForm::MainServerSocketClientRead(TObject*Sender,TCustomWinSocket*Socket){AnsiStringUserNickName,UserLogin;intpos1,pos2;if(!Socket->Data){UserNickName=Socket->ReceiveText();intindex=UserNickName.Pos(" ");if(index>1)UserNickName.SetLength(index-1);elseUserNickName="未知或无效"+IntToStr(Socket->SocketHandle);SetUserBySocket(Socket,UserNickName);Socket->Data=(void*)1;//添加用户序列:格式:xxx,欢迎登录服务器/user1/user2/.../usern*AnsiStringloginresponse=UserNickName+",欢迎登录到服务器";for(inti=0;iSocket->ActiveConnections;i++){GetUserBySocket(MainServerSocket->Socket->Connections[i],UserLogin);if(UserLogin!=UserNickName){loginresponse+="/";loginresponse+=UserLogin;//MainServerSocket->Socket->Connections[i]->SendText(Message);}}loginresponse+="*";Socket->SendText(loginresponse);SendNotification(SN_LOGON,"",Socket);}else//转发信息!{AnsiStringrecvtxt=Socket->ReceiveText(),sendtxt; if(recvtxt.AnsiPos("Multicast"))//多播信息{//多播格式:Multicast$user1/user2/.../usern*信息正文pos1=recvtxt.Pos("$");pos2=recvtxt.Pos("*");//提取用户序列格式:user1/user2/.../usern*MuticastUserList=recvtxt.SubString(pos1+1,pos2-pos1);//提取信息正文sendtxt=recvtxt.SubString(pos2+1,recvtxt.Length()-pos2);//发送信息正文SendNotification(SN_PRIVATE_MSG,sendtxt,Socket);}else//广播SendNotification(SN_PUBLIC_MSG,recvtxt,Socket);}}在上述代码中用到了存储在TcustomWinSocket的Data属性中的数值。首先把Data作为一个布尔类型的值,判断从套接字读取的数据时用户别名还是对话的具体内容。当用户刚连接时,服务器向用户发送一条欢迎消息,并把套接字的Data设置为1,这样在接下来的对话中就不再进行这些操作了。最后我们要做的就是调用SendNotification函数向所有在线的用户发送一条消息,告诉他们一个新的客户已经成功地登录。所有以后的OnClientRead事件的处理,都将调用SendNotification函数把接收的数据以广播或多播的方式发送给指定的客户,即在接到消息时,首先检测该消息中有没有包含多播标记“Multicast”,如果包含则从中提取出多播用户列表和消息正文,然后调用SenNotification函数,其中第一个参数为SN_PRIVATE_MSG,在SenNotification函数中将调用MulticastMessage函数进行多播发送消息;如果不包含多播标记,也将调用SenNotification函数,其中第一个参数为SN_PUBLIC_MSG,在SenNotification函数中将调用BroadcastMessage函数广播消息。SenNotification函数中的第一个参数是SN_PNBLIC_MSG,第二个参数是所要发送的数据,第三个参数是不希望将数据发送到的套接字,这个参数是防止将键入的数据发送给自己。此函数的代码如下。voidTChatServerForm::SendNotification(ServerNotificationsn,AnsiStringadditional,TCustomWinSocket*ExcludeSocket){AnsiStringUserNickName; GetUserBySocket(ExcludeSocket,UserNickName);switch(sn){caseSN_LOGON:{BroadcastMessage(UserNickName+"刚登录",ExcludeSocket);AddLogEntry(LET_SIGNON,UserNickName);break;}caseSN_LOGOFF:{BroadcastMessage(UserNickName+"刚注销",ExcludeSocket);AddLogEntry(LET_SIGNOFF,UserNickName);break;}caseSN_PUBLIC_MSG:{AnsiStringmsgtosend="<"+UserNickName+">"+additional;BroadcastMessage(msgtosend,ExcludeSocket);break;}caseSN_PRIVATE_MSG:{AnsiStringmsgtosend="<"+UserNickName+">"+additional;MulticastMessage(msgtosend,MuticastUserList);break;}}}5.2.4断开连接模块当客户断开与服务器的连接时,触发TserverSocket的OnClientDisconnect事件。此事件响应函数的代码如下。void__fastcallTChatServerForm::MainServerSocketClientDisconnect(TObject*Sender,TCustomWinSocket*Socket){SendNotification(SN_LOGOFF,"",Socket);RemoveConnectionFromListView(Socket);UpdateStatusBar(true);} 在上述代码中,首先调用SendNotification函数向所有与服务器连接的客户发送一个有客户下线的消息,然后将此消息从服务器的窗口总已出。最后调用UpdateStatusBar更新状态栏。5.3客户端程序模块5.3.1建立连接程序模块1相关成员函数和变量的定义在窗体的头文件chatclient.h中,加入如下代码。enumUserListStatus{ULS_HIDE,ULS_SHOW};enumLinkStatus{LS_START,LS_STOP};private://UserdeclarationsAnsiStringNickName;AnsiStringUserList;UserListStatusCurrentUserListStatus;LinkStatusCurrentLinkStatus;voidEnableClient();voidDefaultSBText();voidDisableClient();TCustomWinSocket*OutSocket;void__fastcallValidateControls(TObject*Sender,bool&Done);public://Userdeclarations__fastcallTChatForm(TComponent*Owner);在上述代码中,首先定义了两个枚举类型来增加程序的可读性。第一个UserListStatus用于表示已登录用户列表的状态,因为在程序中,已登录用户列表的状态只有两种,一种是隐藏,另一种是显示,因此两个可能的枚举值分别为ULS_HIDE、ULS_SHOW;第二个枚举类型是LinkStatus,用于表示本用户的连接状态,因为在程序中,本用户的连接状态只有两种,一种是断开,另一种是连接,因此两个可能的枚举值分别为LS_START、LS_STOP。变量CurrentUserListStatus用于保存已登录用户列表的状态,并验证“显示用户列表/隐藏用户” 菜单项,函数SetServerStatus用来设置该变量。变量NickName用于表示客户的名称,UserList表示已经登录服务器的用户名序列,OutSocket表示和客户建立连接的服务器的套接字。2在窗体的构造函数中初始化变量在Chatclient.cpp文件,窗体的构造函数中加入如下代码,中加入如下代码,用于设置变量的初始值。__fastcallTChatForm::TChatForm(TComponent*Owner):TForm(Owner){OutSocket=NULL;Application->OnIdle=ValidateControls;NickName="无名氏";UserList="";CurrentUserListStatus=ULS_SHOW;CurrentLinkStatus=LS_STOP;//默认广播模式UserCheckListBox->Enabled=false;}函数ValidateContruls用于处理应用程序的OnIdle事件。此函数的具体代码如下。void__fastcallTChatForm::ValidateControls(TObject*Sender,bool&Done){if(CurrentLinkStatus==LS_START)Settings->Enabled=true;elseSettings->Enabled=false;Memo2->ReadOnly=!OutSocket;}该函数中,根据内部变量的状态来决定菜单是否可以选择。当CurrentLinkStatus被设置成LS_STOP时,MenuConnect(连接)菜单变灰,不能被选择,Settings(参数设置)菜单项不能被选择。在套接字为空时,用户不能输入消息。3和服务器建立连接单击“连接”菜单,创建此菜单的OnClick事件响应函数,在其中加入如下代码。void__fastcallTChatForm::ConnectDisconnectClick(TObject*Sender){switch(CurrentLinkStatus){ caseLS_START:{EnableClient();ConnectDisconnect->Caption="断开连接";CurrentLinkStatus=LS_STOP;break;}caseLS_STOP:{if(CurrentLinkStatus==LS_START){DisableClient();}CurrentLinkStatus=LS_START;ConnectDisconnect->Caption="连接";break;}}}单击“文件”菜单中的“参数设置”,响应函数如下。void__fastcallTChatForm::SettingsClick(TObject*Sender){SettingsForm=newTSettingsForm(this);SettingsForm->HostEdit->Text=ClientSocket1->Host;SettingsForm->NickNameEdit->Text=NickName;if(SettingsForm->ShowModal()==mrOk){ClientSocket1->Host=SettingsForm->HostEdit->Text;NickName=SettingsForm->NickNameEdit->Text;}deleteSettingsForm;}在SettingsForm的setting.cpp文件中添加防止输入空用户昵称的函数,代码如下。void__fastcallTSettingsForm::NickNameEditExit(TObject*Sender){if(NickNameEdit->Text.Length()<1){ShowMessage("请输入一个有效的绰号!");NickNameEdit->SetFocus();}} 接着调用EnableClient函数建立和服务器的连接。EnableClient函数代码如下。voidTChatForm::EnableClient(){CurrentLinkStatus==LS_START;StatusBar1->SimpleText="正在进行连接...";ClientSocket1->Open();ChatForm->Caption="网络聊天程序-"+NickName;}上述代码将CurrentLinkStatus设置为LS_START,表示已和服务器建立连接,然后在状态栏上现实“正在进行连接…”,最后调用ClientSocket1的Open方法和服务器建立连接。5.3.2输入交谈信息模块与服务器建立连接以后,ValidateControls函数设置用户消息输入栏可用,此时就可以输入消息了。用户有两种消息发送方式可选择,即广播和多播。广播方式不必再消息上附加接收消息用户列表,处理起来比较简单;多播方式在用户大于3时才有用,只有两人的话和广播无异,在登录用户较多的时候,一个用户可以有选择性地将消息发送到所选用户,当所选用户只有一个时,实际上就是单播了。在选择多播模式的时候,需要动态更新接收消息用户序列UserList,代码如下。void__fastcallTChatForm::UserCheckListBoxClickCheck(TObject*Sender){//动态更改所选用户序列inti,index;AnsiStringusername;UserList="";for(i=0;iItems->Count;i++){if(UserCheckListBox->Checked[i]==true){UserList+=UserCheckListBox->Items->Strings[i];//singleusernameisdividedby"/"UserList+="/";} }}5.3.3客户端读取及发送信息模块当和服务器建立了连接后,就会从服务器读回数据,这时就出发组件的OnRead事件。在ClientSocket1的OnRead事件响应函数中加入如下所示的代码。void__fastcallTChatForm::ClientSocket1Read(TObject*Sender,TCustomWinSocket*Socket){//接收信息!AnsiStringrcvtxt,user,userlist;intpos,i,index;rcvtxt=Socket->ReceiveText();StatusBar1->SimpleText="从服务器上获取数据...";//从欢迎信息中提取已登录用户,修改UserCheckListBox中的项目if(rcvtxt.AnsiPos("欢迎登录")){//添加本机UserCheckListBox->Items->Add(NickName);//rcvtxt的格式:xxx,欢迎登录服务器/user1/user2/.../usern*pos=rcvtxt.Pos("/");if(!pos)Memo1->Lines->Add(rcvtxt);else//有后续用户序列{//显示欢迎信息Memo1->Lines->Add(rcvtxt.SubString(1,pos-1));//提取已登录用户序列并修改UserCheckListBox中的项目userlist=rcvtxt.SubString(pos+1,rcvtxt.Length()-pos);while(userlist.Pos("*")){pos=userlist.Pos("/");if(pos)//非最后一个{user=userlist.SubString(1,pos-1);UserCheckListBox->Items->Add(user);userlist=userlist.SubString(pos+1,userlist.Length()-pos);}else//最后一个{user=userlist.SubString(1,userlist.Length()-1);UserCheckListBox->Items->Add(user); userlist="";}}}//默认广播模式,故设置checked属性为truefor(inti=0;iItems->Count;i++){UserCheckListBox->Checked[i]=true;}}//收到登录和注销信息时修改UserCheckListBox中的项目://注册则添加,注销则删除elseif(rcvtxt.AnsiPos("刚登录")){pos=rcvtxt.AnsiPos("刚登录");user=rcvtxt.SubString(1,pos-2);//注意汉字位置减!!if(user!=NickName)UserCheckListBox->Items->Add(user);Memo1->Lines->Add(rcvtxt);//默认广播模式,故设置checked属性为truefor(inti=0;iItems->Count;i++){UserCheckListBox->Checked[i]=true;}}elseif(rcvtxt.AnsiPos("刚注销")){pos=rcvtxt.AnsiPos("刚注销");//注意汉字位置减!!user=rcvtxt.SubString(1,pos-2);index=UserCheckListBox->Items->IndexOf(user);UserCheckListBox->Items->Delete(index);Memo1->Lines->Add(rcvtxt);}elseMemo1->Lines->Add(rcvtxt);}5.3.4断开连接模块1响应“连接/断开”菜单单击事件单击“连接/断开”菜单,创建此菜单的OnClick事件响应函数。在断开连接时,需要用到DisableClient函数,代码如下。voidTChatForm::DisableClient(){ DefaultSBText();OutSocket=NULL;CurrentLinkStatus==LS_STOP;ClientSocket1->Close();}2响应和服务器断开连接事件当ClientSocket1组件的Active属性设置为false,或调用Close方法后,在连接真正断开前,出发ClientSocket1组件的OnDisconnect事件。创建此事件响应函数,然后在其中加入如下代码。void__fastcallTChatForm::ClientSocket1Disconnect(TObject*Sender,TCustomWinSocket*Socket){DisableClient();}3响应用户关闭窗口事件void__fastcallTChatForm::ExitClick(TObject*Sender){if(CurrentLinkStatus==LS_START){DisableClient();}Close();) 6系统测试首先打开服务端主界面,点击“开始”启动监听,如图6-1所示:图6-1服务端主界面服务器已处于监听状态,接着打开客户端主界面,如图6-2所示;图6-2客户端主界面 点击“文件”,进行客户端的参数设置。如图6-3所示:图6-3客户端参数设置服务器是本地,主机名写localhost,用户昵称可以自己填写。点“确定”。客户端界面显示如图6-4所示:图6-4用户1登录服务器端界面显示如图6-5所示: 图6-5用户登录后服务端界面接下来,再打开一个客户端登录,用户2的客户端界面如图6-6所示:图6-6用户2登录 为了便于演示广播和多播,我们再以用户3的身份登录,如图6-7所示:图6-7用户3登录当用户1以广播的形式发送信息时,用户2和用户3都能收到这条信息,如图6-8和6-9所示:图6-8用户2收到广播消息 图6-9用户3收到广播消息当用户1以选择性方式,发送消息给用户2时,用户2能看到消息,用户3不能看到,如图6-10和图6-11所示:图6-10用户 图6-11用户3无法收到单播消息 7结论本文阐述了如何以C++语言设计和实现一个网络聊天系统的过程。本方案界面简洁,使用方便,为多人聊天提供了一个在线的平台。通过这次的毕业设计,使自己在C++编程语言的使用方面得到了一个全面锻炼自己的机会。应用Socket技术实现网络连接功能,对网络的实现和通信方面有了进一步的理解。在整个开发过程当中应用软件工程的开发步骤,使自己专业理论知识的到进一步的巩固,项目开发经验有不少的收获。由于技术水平的原因,本系统在许多方面的工作还有待完善。比如在服务端可以采用数据库,以更好地管理客户的资料等。希望该系统在使用中能得到进一步的完善。 参考文献[1]刘丽,梁对.即时通信系统的设计与实现[C].通信指挥学院,2006.[2]朱和平.即时通信研究综述[J].现代计算机,2006.[3]孙玉钰.基于P2P网络的聊天系统的研发[J].长春大学学报,2006.[4]章韵,姚子阳.IPv4和IPv6下的P2P即时通信[J].现代计算机,2006.[5]罗军舟等.TCP/IP协议及网络编程技术.清华大学出版社,2008.[6]梅成刚,马进德.C++Builder项目开发实践[M].中国铁道,2003.[7]青软实训组,钟岱晖.C++开发之路.北京:电子工业出版社,2009.[8]张虹.软件工程与软件开发工具.清华大学出版社,2009.[9]刁成嘉主编,面向对象C++程序设计,机械工业出版社,2004.[10]SameerPatil,AlfredKobsa.InstantMessagingandPrivacy[J].ProceedingsofHCI,2004.[11]Anumba,C.J.DatastructuresandDBMSforcomputer-aideddesignsystems[J].AdvancesinEngineeringSoftware,1996.[12]Baloukas,C.,Risco-Martin,J.L.,Atienza,D.,etal.Optimizationmethodologyofdynamicdatastructuresbasedongeneticalgorithmsformultimediaembeddedsystems[J].JournalofSystemsandSoftware,2009.

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭