VCMFC网络聊天工具详细设计_第1页
VCMFC网络聊天工具详细设计_第2页
VCMFC网络聊天工具详细设计_第3页
VCMFC网络聊天工具详细设计_第4页
VCMFC网络聊天工具详细设计_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

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

文档简介

1、第2章系统分析及开发技术说明2.1节需求分析2.1.1节功能需求分析功能需求发送基本聊天信息传输音视频数据传输文件白板共享屏幕保持连接发送文本信息发送图片信息图2.1功能需求说明图(1)用户端的基本聊天信息发送,这些基本聊天信息包括文本和图片。文本和图片聊天是聊天软件最基础的功能。用户通过输入IP来查找用户,并申请加为好友,在对方同意加为好友后,在线用户列表就会更新用户,把加入的用户添加到用户列表中。这样,两个用户之前就可以实现通信了。在信息传输中,预计利用TCP/IP协议中的UDP协议,这是面向无连接的协议,但发送速度快,用于聊天信息传输用适合。(2)用户端的音视频数据传输,这是本设计的扩展

2、功能。用户可以正常通信后,就可以选择是否进行语音或视频聊天。本功能也将采用UDP协议,UDP协议可能会丢失数据,但对于音视频聊天需要传输大量数据但又允许丢失少量数据的情况下,UDP的快速发送信息的特点就得到很好的体现。(3)用户端文件传输的功能,用户之间可以断点续传文件。在传文件之前,首先创建一信息文件,记录文件传送的一些信息,并根据传输的数据量实时修改。如果没有传完,下次就可以继续打开这个信息文件,接着上次的进度传输。因为文件传输入要求数据不能出错,因此此模块采用TCP协议。(4)用户端之间白板和共享屏幕的功能,这个功能有些类似视频的传输,因些并不是很难,可以在视频传输的功能上加以修改。(5

3、)用户之间后台的连接,每个用户隔指定时间会向用户列表中的每一用户发送消息,查看用户是否在线,如果不在线,就更新用户列表,删除用户。2.1.2节数据需求分析(1)客户端之间聊天信息。在控件上显示时格式化,更易于用户的查看自己发送或接收到的信息。(2)在线用户列表信息。服务器端存放在适当的空间中,在发送给客户端时,对信息列表进行格式化,便于客户端提取信息。(3)客户端向服务器端发送的确认在线信息。包括客户端刚刚启动时的初始化信息和在使用过程中的确认在线信息。(4)其它程序内部可能需要设计的数据结构体。2.1.3节性能需求分析(1)可靠性高,能在由于系统问题或其它原因产生错误后,作出相对应处理,比如

4、网络初始化失败、服务器不在线等,可以提示用户安全退出本程序,在出现不可知的错误以后,可以尽量安全的退出程序。在程序的设计过程中,要求能尽可能多的设想到用户使用过程中可能发生的事件,并能在判断事件后做出相应的处理,使程序具有较高的容错性能。(2)宜操作性,程序简单易懂,容易上手使用。设计界面是,简化界面的复杂性,模拟QQ等现有即时通讯工具的界面,使用户能很容易看懂并使用。(3)开发文档易理解,保证以后自己二次开发或他人接手开发时,能够清晰的理解整个系统的设计思路和实现细节。(4)模块化设计此软件的功能,不同的模块实现不同的功能,使得软件易于以后的维护与扩展,在以后可以更好的完善本软件的功能,更方

5、便于在工作中的应用。2.1.4节运行需求分析(1)用户界面程序较小,启动速度快,无启动界面。在本地局域网中使用,所以无需进行用户登录,无需认证界面,启动后的应用界面要清爽,设计要简单明了,要具有较高的易用性。(2)故障处理在遇到可预知的故障与情况时,能提示用户并自动退出;在遇到不可预知的故障时能安全退出。2.4节Winsock网络编程Windows Sockets是从Berkeley Sockets扩展而来的,其在继承Berkeley Sockets的基础上,又进行了新的扩充。这些扩充主要是提供了一些异步函数,并增加了符合WINDOWS消息驱动特性的网络事件异步选择机制5。Windows So

6、ckets由两部分组成:开发组件和运行组件。开发组件:Windows Sockets 实现文档、应用程序接口(API)引入库和一些头文件。运行组件:Windows Sockets 应用程序接口的动态链接库(WINSOCK.DLL)。2.4.1节Socket套接字(Socket)最初是由加利福尼亚大学Berkeley分校为UNIX操作系统开发的网络通信接口,随着UNIX操作系统的广泛使用,套接字成为当前最流行的网络通信应用程序接口之一7。90年代初,由Sun Microsystems,JSB Corporation,FTP software,Microdyne和Microsoft等几家公司共同制

7、定了一套标准,即Windows Sockets规范。Windows Sockets API是Microsoft Windows的网络程序设计接口,它在继承了Berkeley Sockets主要特征的基础上,又对它进行了重要扩充。这些扩充主要是提供了一些异步函数,并增加了符合Windows消息驱动特性的网络事件异步选择机制。这些扩充有利于应用程序开发者编制符合Windows编程模式的软件,它使在Windows下开发高性能的网络通信程序成为可能。Socket实际上是指一个通信端点,借助于它,用户所开发的Socket应用程序,可以通过网络与其它Socket应用程序进行通信。近年来,随着计算机网络与W

8、indows 95的流行,许多用户所开发的应用程序需要实现网络间的数据通信。2.4.2节开发Windows Sockets网络通信程序的软、硬件环境所采用的操作系统软件可以是Windows 95,2000,XP,也可以是Windows NT,因为它们都支持Windows Sockets API,在以下的介绍中,我们将以在Windows XP环境下的开发为例。 所采用的编程语言一般可选目前较流行使用的可视化和采用面向对象技术的Microsoft Visual C+ 6.0。Visual C+ 6.0可在Windows XP或Windows NT环境下运行,其开发系统增加了全面集成的基于Windo

9、ws 的开发工具以及一个基于传统C/C+开发过程的“可视化”用户界面驱动模型。Visual C+ 6.0中的Microsoft基类(MFC,即Microsoft Foundation Class)库是一系列C+类,其中封装着为Microsoft Windows操作系统系列编写应用程序的各种功能 。在有关套接字方面,Visual C+ 6.0对原来的Windows Sockets库函数进行了一系列封装,继而产生了CSocket 、CSocketFile等类,它们封装着有关Socket的各种功能8。 所采用的网络通信协议一般是TCP / IP。Windows XP和Windows NT都带有该协议

10、。但是,所开发的网络通信应用程序并不能直接与TCP / IP核心打交道,而是与网络应用编程界面Windows Sockets API打交道。Windows Sockets API则可直接与TCP/IP核心进行沟通。TCP / IP核心协议连同网络物理介质(如网卡)一起,都是提供网络应用程序间相互通信的设施。2.4.3节CSocket类编程模型使用CSocket对象涉及CArchive和CSocketFile 类对象。以下介绍的针对字节流型套接字的操作步骤中,只有第三步对于客户方和服务方操作是不同的,其他步骤都相同13。 (1)构造一个CSocket对象。 (2)使用这个对象的Create()成

11、员函数产生一个socket对象。在客户方程序中,除非需要数据报套接字,Create()函数一般情况下应该使用默认参数。而对于服务方程序,必须在调用Create时指定一个端口。需要注意的是,Carchive类对象不能与数据报(UDP)套接字一起工作,因此对于数据报套接字,CAsyncSocket和CSocket 的使用方法是一样的。 (3)如果是客户方套接字,则调用CAsyncSocket Connect()函数与服务方套接字连接;如果是服务方套接字,则调用CAsyncSocketListen()开始监听来自客户方的连接请求,收到连接请求后,调用CAsyncSocketAccept()函数接受请

12、求,建立连接。请注意Accept()成员函数需要一个新的并且为空的CSocket对象作为它的参数,解释同上。 (4)产生一个CSocketFile对象,并把它与CSocket 对象关联起来。(5)为接收和发送数据各产生一个CArchive 对象,把它们与CSocketFile对象关联起来。切记CArchive是不能和数据报套接字一起工作的。 (6)使用CArchive对象的Read()、Write()等函数在客户与服务方传送数据。(7)通讯完毕后,销毁CArchive、CSocketFile和CSocket对象。2.4.4节用VC6.0进行Windows Sockets程序开发的技术要点(1)

13、同常规编程一样,无论服务器方还是客户方应用程序都要进行所谓的初始化处理,这部分工作仍可采用消息驱动机制来先期完成。 (2)一般情况下,网络通信程序是某应用程序中的一模块。在单独调试网络通信程序时,要尽量与采用该通信模块的其它应用程序开发者约定好,统一采用一种界面形式,即单文档界面SDI、多文档界面MDI和基于对话框界面中的一种(这在使用AppWizard形成项目Project文件时有提示),尽管这并非必须,但可使通信模块在移植到所需的应用程序时省时省力,因为Visual C+ 6.0这种可视化语言在给我们提供方便的同时,也给我们带来某些不便,譬如所形成的项目文件中的许多相关文件与所采用的界面形

14、式密切联系,许多消息驱动功能,随所采用的界面形式不同而各异。 当然,也可将通信模块函数化,并形成一个动态连接库文件(DLL文件),供主程序调用。 (3)以通信程序作为其中一个模块的应用程序往往不是在等待数据发送或接收完之后再做其它工作,因而在主程序中要采用多线程(Multithreaded)技术。即将数据的发或收,放在一个具有一定优先级(一般宜取较高优先级)的辅助线程中,在数据发或收期间,主程序仍可进行其它工作,譬如利用上一个周期收到的数据绘制曲线 。Visual C+ 6.0中的MFC提供了许多有关启动线程、管理线程、同步化线程、终止线程等功能函数14。(4)在许多情况下,要求通信模块应实时

15、地收、发数据。譬如调用之的主程序以0.5秒为一周期,在这段时间内 ,要进行如下工作:接收数据,利用收到的数据进行运算,将运算结果发送到其它计算机节点,周而复始。我们在充分利用Windows Sockets的基于消息的网络事件异步选择机制,用消息来驱动数据的发送和接收的基础上,结合使用其他措施,如将数据的收和发放在高优先级线程,在软件设计上,安排好时序,尽量避免在同一时间内,双方都在向对方发送大量数据的情况发生,保证网络要有足够的带宽等,成功地实现了数据传输的实时性2。第3章详细设计本章将从各个方面介绍本系统的设计。先从基本框架的设计出发,然后逐步介绍好友管理、聊天模块、聊天室模块、传送文件模块

16、、共享屏幕模块、白板模块、音、视频模块和调试模块,所以本章是本论文的重点。3.1节基本框架设计本节内容将介绍除各个功能模块外的设计,包括界面上的处理、保持好友在线列表等的处理。有些内容可能并不属性框架设计,但这些内容也不具有单独使用一节来介绍的必要,所以把这些内容也一并放到这些节来介绍。这也是为了区分设计周围的处理与各个功能模块的处理。3.1.1节宏和数据结构的定义程序中用到了很多宏和数据结构,这些宏和数据结构在多个模块中都有用到,因此程序中专门新建一个头文件Global.h,此头文件里是程序中很到的宏和数据结构的定义1。在StdAfx.h文件包含Global.h,在程序其他地方都可以使用Gl

17、obal.h中的宏和定义的数据结构。这样处理还有一个好处,如果需要修改某些宏的值,可以直接在Global.h中修改,而不用到处去找宏的定义,方便和快捷。3.1.2节程序配置文件程序中很多信息需要保存,比如用户名和热键,因些程序用到了配置文件,默认的配置文件名为conf.ini。程序用读取和写入配置文件系列函数来管理此配置文件。3.1.3节主界面初始化用过QQ的人都知道,QQ主面板总是处于其他程序的上面,而且QQ在任务栏没有图标,而是把图标放到了托盘区,另外,我们还可以按Ctrl+Alt+Z默认的快捷键隐藏和显示QQ主面板。不但QQ是这样处理的,很多聊天软件都采用此种处理方式。本设计也不例外,同

18、样也要达到这样的目的。下面从各个方面来说明本设计的处理方式:(1)不在任务栏显示图标CDialog dlgParent;dlgParent.Create( IDD_DIALOG_BG );dlgParent.ShowWindow( SW_HIDE );CInstantMessagingDlg dlg( &dlgParent );m_pMainWnd = &dlg;ModifyStyleEx( WS_EX_APPWINDOW, 0 );上面代码就达到了程序主界面不在任务显示的目的。首先,我们创建一个对话框,并隐藏此对放框,然后把这个对话框作为主界面对话框的父窗口,然后在主界面对话

19、框的初始化函数中修改其风格,去掉WS_EX_APPWINDOW风格。这样,主界面就不会出现在任务了3。(2)将主界面放在最上层将程序放到顶层,很多程序都有这功能,比如金山词霸等,实现起来其实很简单,只用一条语句就可以达到目的:SetWindowPos( &wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );第一个参数就是将程序放到所有非顶层窗口的顶层,如果有多个程序都是顶层窗口,那么他们谁在上面,就要看当前谁是激活的窗口。最后一个参数,是用位或组后起来的,从字面意思上我们就能理解到这是不移动不改变大小的意思,忽略了当中的4个参数6。(3)

20、热键的处理设计中默认的热键是Ctrl+Alt+Z,当然程序允许用户自己定义热键,自定义的热键将保存在conf.ini文件中。热键的功能可以隐藏、显示主界面,有消息到达时,按热键也可以打开聊天对话框。:RegisterHotKey( m_hWnd, IDHOTKEY, m_wModifiers, m_wVirtualKeyCode );使用全局函数RegisterHotKey可以注册热键,如果注册的热键没有被其他程序占用,那么注册成功。注册成功后,如果按热键,那么程序就会接受到WM_HOTKEY消息,因此我们还需要自己处理WM_HOTKEY消息12:void OnHotkey( WPARAM w

21、Param, LPARAM lParam );BEGIN_MESSAGE_MAP(CInstantMessagingDlg, CDialog)ON_MESSAGE( WM_HOTKEY, OnHotkey )/AFX_MSG_MAPEND_MESSAGE_MAP()在消息映射中,我们用OnHotkey()函数来处理WM_HOTKEY消息。void CInstantMessagingDlg:OnHotkey( WPARAM wParam, LPARAM lParam )if( this->IsWindowVisible() )ShowWindow( SW_HIDE );elseShowWi

22、ndow( SW_SHOW );:SetForegroundWindow( m_hWnd );在OnHotkey()函数中判断主界面是否是可见的,如果是可见的那么隐藏起来,否则显示,并且把主界面设为前景窗口。(4)最小化和关闭按钮的处理我们希望单击程序右上角的最小化按钮时,程序隐藏起来,而单击关闭按钮时,程序会提示是否退出,而不会悄无声息的退出。void CInstantMessagingDlg:OnSysCommand(UINT nID, LPARAM lParam)if( nID = SC_MINIMIZE )this->ShowWindow( SW_HIDE);elseCDialo

23、g:OnSysCommand(nID, lParam);void CInstantMessagingDlg:OnCancel() if( IDOK = MessageBox( "要退出吗?", "退出", MB_OKCANCEL | MB_ICONINFORMATION | MB_DEFBUTTON2 ) )DestroyWindow();在程序中处理OnSysCommand()函数和OnCancel ()函数就实现了我们要的功能。(5)托盘图标的显示至此,程序已不在任务栏显示图标,已是最顶层窗口,而且也已有热键功能,但是还没有实现托盘图标的显示。实现托

24、盘图标的代码如下:NOTIFYICONDATAm_nid;HICON hIcon= AfxGetApp()->LoadIcon( STATE_ONLINE );m_nid.hIcon= hIcon;m_nid.hWnd= m_hWnd;m_nid.cbSize= sizeof( NOTIFYICONDATA );m_nid.uCallbackMessage= WM_SHELLNOTIFY;m_nid.uFlags= NIF_ICON | NIF_MESSAGE | NIF_TIP;m_nid.uID= IDR_MENU1;sprintf( m_nid.szTip, "即时聊天软

25、件" );Shell_NotifyIcon( NIM_ADD, &m_nid );这样我们就在托盘区显示了STATE_ONLINE的图标,把鼠标移动到图标上一会,还会出现“即时聊天软件”的提示框。如果你们对图标有单击和双击等操作,程序会收到WM_SHELLNOTIFY消息,因此,我们还必须处理WM_SHELLNOTIFY消息。void OnShellNotifyProc( WPARAM wParam, LPARAM lParam );BEGIN_MESSAGE_MAP(CInstantMessagingDlg, CDialog)/AFX_MSG_MAP(CInstantMes

26、sagingDlg)ON_MESSAGE( WM_SHELLNOTIFY, OnShellNotifyProc )/AFX_MSG_MAPEND_MESSAGE_MAP()void CInstantMessagingDlg:OnShellNotifyProc( WPARAM wParam, LPARAM lParam )if( lParam = WM_LBUTTONDBLCLK )else if( lParam = WM_RBUTTONUP )与热键处理一样,OnShellNotifyProc()函数响应我们对图标的操作,其中lParam参数表示消息号,在本设计中只处理左键双击(显示主界面)和

27、右键单击(弹出菜单)。在托盘添加图标,退出程序前,如果没有从托盘删除图标,那么托盘区的图标会一直保留下来,直到鼠标移过托盘区引起托盘区的重绘,这当然不是我们所希望的结果。void CInstantMessagingDlg:OnDestroy() / 删除在托盘建立的图标:Shell_NotifyIcon( NIM_DELETE, &m_nid );CDialog:OnDestroy();以上代码在程序退出时调用,从托盘从删除图标。(6)只允许运行唯一实例这点与QQ不同,在一台机子上可以运行多个QQ,但本程序只允许运行一个实例。只允许运行一个实例,有多种方法,本设计采用的是创建命名事件的

28、方法:HANDLE hEvent = :CreateEvent( NULL, FALSE, FALSE, "InstantMessaging" );if( hEvent )if( ERROR_ALREADY_EXISTS = GetLastError() )return FALSE;事件与普通变量不一样,普通变量只在运行的当前程序中有效,而事件在整个系统中都有效。当首次运行程序时,会创建一个名为“InstantMessaging”的事件,这个事件在系统范围内有效,当再次运行程序时,程序会尝试着创建同名的事件,因为之前已经创建了这个事件,因此系统会返回之前创建事件的句柄,但G

29、etLastError()会返回ERROR_ALREADY_EXISTS,表明需创建的事件之前已经创建,为了保证只允许一个实例,这个实例就不再允许运行,直接返回,退出程序。3.1.3节主界面布局程序主界面如下:图3.1主界面左上角显示的是自己的头像、状态和昵称;右上角的列表框是查找IP输入框,下面是添加按钮;在下面一点的列表框是自己的址列表框,显示了自己的所有IP;主界面中央是用户列表框;最下面是4个功能按钮。用户列表框是一列一列的显示添加的好友,最左边是好友的头像;中部上边是好友的昵称,下面是好友的IP;右下角是删除好友按钮和摄像头按钮,当然好友必须有摄像头才会显示摄像头按钮。有两种方式添加

30、联系人:(1)在右上解的查找IP输入框里输入IP,然后单击下面的添加按钮。也可以从IP输入框里选择以前加过的好友IP。程序允许保存10个最近联系人的IP,当新添加联系人时,如果已保存了10个联系人的IP,程序会按照时间的先后顺序覆盖之前的IP。在列表框展开下拉列表后,可以按DELETE键删除选定的IP。(2)选定一个自己的IP,然后单击“网段”按钮。此功能可以向选定IP的IP段发送添加请求的消息,这相当于批量添加好友的功能。在主界面任何地点单击左键不放开,可以拖动程序;单击右键,会弹出菜单,用户选中相应的菜单项,可以执行相应的功能;在任何地点双击左键,可以打开“个人设置”对话框,如下:图3.2

31、设置对话框最后一项“允许别人直接将我加为联系”的意思是别人添加我为好友时,不会弹出请求对话框而直接加为好友。单击保存后,此对话框里的内容会保存到config.ini配置文件中。运行程序后,会从config.ini读取用户信息,并在主界面中作相应的设置。在主界面,添加、聊天室、传送文件、共享屏幕和白板按钮都是自绘按钮,可以显示图片,有提示能力,当鼠标移动到其上一会儿后,会弹出提示框,而且这些按钮都具有XP风格,既鼠标滑过时会显示不同的状态。用户列表框也是自绘的,普通的列表控件无法显示我们所需的信息。自绘按钮和自绘列表框会作为一个单独的模块来介绍,这儿就不作过多的介绍。3.1.4节自绘按钮VC+6

32、.0自带的按钮控件不具有XP风格,而且也不能显示图像,作为一款好的软件,应该有个好的界面。在程序的主界面上,主要的按钮都采用了自绘按钮,而不使用自带的按钮控件。AdvButton.h和AdvButton.cpp是自绘按钮类的头文件和实现文件。在自绘按钮类中定义了如下成员变量:intm_nState;/ 按钮的状态CBitmapm_bmpNormal;/ 正常图标CBitmapm_bmpHover;/ 焦点图标CBitmapm_bmpDown;/ 按下图标CBitmapm_bmpDisable;/ 无效图标CToolTipCtrlm_pToolTipCtrl;/ 提示类m_nState表示当前按

33、钮的状态,可以为宏:NORMAL,HOVER,DOWN,DISABLE,分别表示按钮正常状态、处于焦点状态、按下状态、无效状态,这4个宏的定义在实现文件。4个CBitmap的变量分别存储4种状态下的图像。m_pToolTipCtrl是提示工具控件类,既是鼠标在其上时,会弹出提示窗口。要实现按钮自绘,必须更新按钮的风格为自绘,可以在按钮的属性中更改,也可以使用代码更改。重载PreSubclassWindow(),在这个函数中更改按钮风格并初始化m_pToolTipCtrl。void CAdvButton:PreSubclassWindow() ModifyStyle( 0, BS_OWNERDR

34、AW );CButton:PreSubclassWindow();m_pToolTipCtrl.Create( this, TTS_ALWAYSTIP );m_pToolTipCtrl.SetDelayTime( 100 );CString strText;GetWindowText( strText );m_pToolTipCtrl.AddTool( this, strText ); VC+6.0中的ClassWizard不能为我们添加鼠标离开的消息,只能为我们添加鼠标移动、单击等消息,我们得自己为自绘按钮添加上鼠标离开的消息。TRACKMOUSEEVENT tme;tme.cbSize =

35、 sizeof( TRACKMOUSEEVENT );tme.hwndTrack = m_hWnd;tme.dwFlags = TME_LEAVE;:_TrackMouseEvent( &tme );以上代码告诉系统,当鼠标离开m_hWnd窗口时,向这个窗口发送一条WM_MOUSELEAVE消息。下面的处理方式与热键和托盘通知消息的处理方式一样,自定义这个消息处理函数就行了。自绘按钮必须重载DrawItem()函数,在DrawItem()函数中根据m_nState的值可以贴上不同的图,表示按钮的一不同状态。void CAdvButton:DrawItem(LPDRAWITEMSTRUC

36、T lpDrawItemStruct) if( lpDrawItemStruct->itemState & ODS_DISABLED )m_nState = DISABLE;switch( m_nState )case NORMAL:DrawNORMAL();break;case HOVER:DrawHOVER();break;case DOWN:DrawDOWN();break;case DISABLE:DrawDISABLE();break;default:break;DrawNORMAL() 、DrawHOVER ()、DrawDOWN() 、DrawDISABLE()分别

37、画按钮的4种状态。当鼠标滑过或单击按钮时,更改m_nState的值,然后调用Invalidate(),强制按钮重绘。要使按钮无效,必须调用EnableWindow( FALSE )函数来更改按钮的状态,我们也就无法更改m_nState的值。lpDrawItemStruct->itemState的值表示了当前按钮的状态,可以检测lpDrawItemStruct->itemState,如果按钮是无效状态,则设置m_nState为DISABLE,否则不作改变。在画按钮的状态时,使用到了TransparentBlt()函数,这个函数可以贴透明位图。在TransparentBlt()最后一个

38、参数中指定掩码色,贴图时掩码色就不会贴出来。要使用此函数,必须导入 msimg32.dll,在程序使用如下语句导入:#pragma comment( lib, "C:Program FilesMicrosoft VisualStudioVC98LibMSIMG32.LIB" )3.1.5节自绘好友列表框普通的列表控件无法满足程序的要求,程序要求好友列表框可以显示好友的头像、好友昵称、好友IP和删除、摄像头按钮。FriendsListCtrl.h和FriendsListCtrl.cpp是自绘列表框的头文件和实现文件。定义的成员变量如下:CInstantMessagingDlg

39、*m_pMainDlg;/ 主对话框CImageListm_imageList;/ 头像图像列表intm_nCamera;/ 摄像头激活的序号intm_nDelIcon;/ 删除按钮激活的序号intm_nCurSel;/ 当前选中用户序号与自绘按钮类似,在PreSubclassWindow()函数中更改列表框的风格为自绘:void CFriendsListCtrl:PreSubclassWindow() ModifyStyle( 0, LVS_OWNERDRAWFIXED );CListCtrl:PreSubclassWindow();重载MeasureItem()函数更改列表框每一项的高度:

40、void CFriendsListCtrl:MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct )lpMeasureItemStruct->itemHeight = DEFAULT_FRIENDSLIST_HEIGHT;宏DEFAULT_FRIENDSLIST_HEIGHT在Global.h文件定义,表示好友列表框每项的高度。列表框中的鼠标离开消息与自绘按钮的实现是同一个原理,这儿就不再赘述。在向好友列表框中添加好友时,主对话框调用好友列表框的AddUser()函数,参数为USER结构体,这个参数作为列表项的额外数据,这样重绘的时候再

41、读取出这个额外数据就可以得到这一项的用户信息。鼠标在好友列表框上移动时,判断鼠标是否在删除或摄像头(如果有摄像头)按钮范围内,如果在,就设置m_nDelIcon或m_nCamera为当前项的序号,否则就设置m_nCurSel为当前项的序号。用户双击鼠标时,调用主对框的相应函数,并把项的序号传给此函数。如果是单击,先判断m_nDelIcon、m_nCamera值,如果不为-1,则选中了删除或摄像头按钮,调主对框相应函数执行相应操作。在DrawItem()函数中,先得到额外的附加数据,既是添加项时作为参数传递的USER类型的变量,然后再根据m_nCurSel、m_nDelIcon和m_nCamer

42、a的值自绘。在自绘时,为了防止闪烁,程序用到了双缓冲技术。双缓冲技术,就是先创建一个与目标设备兼容的内存设备上下文,在内存设置上下文中画图或进行其他处理,操作完成了,再一并把内存设备上下文的内容贴到目标设备上,这样就可以有效的防止闪烁。3.2节好友管理好友管理包括添加好友、删除好友以及与好友保持连接。3.2.1节添加好友添加好友的两种方式4.1节已经介绍过了,这一节介绍的是具体的实现。首先然主对话框定义监听Socket并初始化:CListeningSocket*m_pLisSocket;m_pLisSocket = new CListeningSocket( this );m_pLisSock

43、et->Create( DEFAULT_PORT, SOCK_DGRAM );在添加按钮的响应函数中先判断IP地址是否合法,是否是自己的IP,是否已经添加此好友。如果可以添加此好友,则向此好友发起请求加为好友的请求:/ 定义数据包DATAPACKET dataPacket;dataPmand = REQUEST_ADD;/ 设置请求的用户结构USER user;user.bHasCamera= m_bCamera;user.nFace= m_wFace;memcpy( user.strName, m_strNickName.GetBuffer( MAXNICKNAMELENGTH + 1

44、 ), MAXNICKNAMELENGTH + 1 );m_strNickName.ReleaseBuffer();/ 分配空间UINT uDataLength = sizeof( DATAPACKET ) + sizeof( USER );BYTE *pSendData = new BYTE uDataLength ;memcpy( pSendData, &dataPacket, sizeof( DATAPACKET ) );memcpy( pSendData + sizeof( DATAPACKET ), &user, sizeof( USER ) );/ 发送请求m_pL

45、isSocket->SendTo( pSendData, uDataLength, DEFAULT_PORT, strFriendIP );delete pSendData;以上代码中的宏都可以在Global.h头文件中找到。向好友发起请求的数据中,还包括自己的USER数据。向对方发起请求后,对方的m_pLisSocket就会调用OnReceive()函数,程序中重载了CListeningSocket类的OnReceive()函数,在OnReceive()中调用主对对话框的OnListeningReceive()函数来接收网络数据。在OnListeningReceive()函数中,根据D

46、ATAPACKET的command值来进行相应的处理,这儿是添加为好友的请法度,先得到发起请求的USER,再调用AddRequest()函数做相应处理。在AddRequest()函数中,先进行相应的判断,如果具备加为好友的条件,根据是否允许直接加为好友的值是否弹出提示对话框。如果拒绝加为好友,则向发送者发送拒绝的消息。否则先把发起者的USER信息加入到好友列表中,再向请求者发送允许加为好友的消息,消息中包括自己的USER信息。请求者收到对方的拒绝消息,会弹出对话框提示对方拒绝加为好友;如果收到的是允许加为好友的消息,则再得到一同发送过来的USER信息,再加入到列表中。在InstantMessa

47、ging.h头文件中定了如下变量:CArray< USER, USER >m_arrFriends;/ 好友列表此数组保存的是已连接的好友,添加好友时会向这个变量里添加USER信息,并把USER添加到好友列表框中。请求者添加好友流程图如下:添加好友对方是否允许将好友添加到数组和列表框中弹出提示框TF图3.3添加好友流程图好友收到请求的流程图如下:收到请求是否允许将好友添加到数组和列表框中并发送允许消息发送拒绝消息TF图3.4好友收到请求流程图3.2.2节删除好友删除好友比较简单,在服务器端向要删除的好友发送命令为OFFLINE的消息,然后从好友数组和好友列表框中删除此好友。在客户端

48、接受到OFFLINE的网络消息时,得到发送此消息的IP,然后从好友数据和好友列表框中删除与此IP相等的好友。另外,当用户改变自己的状态为下线状态或关闭程序时,会调用SendOffLineMessage()函数向所有好友发送下线消息,并删除所有的好友。3.2.3节与好友保持连接此功能用到了定时器,间隔一定时间向所有好友发起请求保持连接的消息,并把发送过此消息的好友添加到一个临时好友列表中。好友收到请求保持连接的消息后,会发送回应保持连接的消息。程序收到回应保持连接的消息后,会从临时好友列表中删除对应的好友。在下一次定时器到的时候,程序检查临时好友列表,在临时好友列表中的好友都是没有回应的的好友,

49、这些好友可能是因为程序不正常关闭而未向其他好友发送下线通知,程序就可以将这些好友删除。在程序初始化时设置一定时器:SetTimer( TIMER_CONNECT, DEFAULT_REFRESH_TIME, NULL );在OnTimer()函数中进行如下处理:/ 保持接连if( TIMER_CONNECT = nIDEvent )/ 删除没有回应的联系人for( int nIndex = 0; nIndex < m_arrFriends.GetSize(); nIndex+ )USER userDel = m_arrFriends.GetAt( nIndex );if( m_strls

50、tKeepConnent.Find( userDel.strIP ) )m_listCtrlFriends.DeleteUser( nIndex );m_arrFriends.RemoveAt( nIndex );nIndex-;m_strlstKeepConnent.RemoveAll();/ 分别发送保持连接的消息,将发送过的IP加入到m_strlstKeepConnentfor( nIndex = 0; nIndex < m_arrFriends.GetSize(); nIndex+ )USER user = m_arrFriends.GetAt( nIndex );DATAPAC

51、KET dataPacket;dataPmand = REQUEST_KEEPCONNECT;/ 分配空间UINT uDataLength = sizeof( DATAPACKET );BYTE *pSendData = new BYTE uDataLength ;memcpy( pSendData, &dataPacket, sizeof( DATAPACKET ) );m_pLisSocket->SendTo( pSendData, uDataLength, DEFAULT_PORT, user.strIP );delete pSendData;m_strlstKeepCon

52、nent.AddTail( user.strIP );3.3节聊天模块聊天包括文字聊和图片聊天,本系统用到了Microsoft Rich Textbox Control 6.0控件,此控件支持RTF(Rich Text Format)格式的内容,包括不同颜色、不同字体字号的文本和图片。要使用此控件,系统中必须注册了此控件,如果未注册此控件,那么打开聊天对话框时程序会死掉。为了解决这个问题,本设计在程序中检查系统是否注册过richtx32.ocx控件,如果没有注册,程序会先注册,代码如下:HKEY hKey;if( RegOpenKeyEx( HKEY_CLASSES_ROOT, "R

53、ICHTEXT.RichtextCtrlCLSID", 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) HINSTANCE hLib = LoadLibrary( "RICHTX32.OCX" );/ 控件不存在if( !hLib )MessageBox( "RICHTX32.OCX控件未找到" );else/获取注册函数DllRegisterServer地址FARPROC lpDllEntryPoint;lpDllEntryPoint = GetProcAddress( hLib, "DllR

54、egisterServer" );/ 注册richtx32.ocx控件lpDllEntryPoint();聊天对话框如下:图3.5聊天对话框聊天对话框用的Socket是主对话框的m_pLisSocket,当要发送消息时,调用主对话框的SendPreChatMessage()函数,主对话框接收到聊天消息时调用聊天对话框的ReceiveMessage()函数。聊天对话框中定的发送和接收富文本框变量为:CRichTextm_rtReceived;CRichTextm_rtSend;发送消息的主要代码如下:void CChatDlg:OnSend() CTime time = CTime:G

55、etCurrentTime();CString strTime = time.Format( "%H:%M:%S" );CString strSend = m_rtSend.GetTextRTF();/ 发送聊天消息m_pMainDlg->SendPreChatMessage( m_userChat, strTime, strSend );单击“发送”按钮或CTRL+ENTER键,程序会调用OnSend()函数,首先得到发送的时间和发送的内容,得到的是RTF格式的内容,因此strSend中还包括了文字的格式和图片信息。然后调用主对话框的SendPreChatMessa

56、ge()函数向m_userChat好友发送聊天消息。接收到的消息主要代码如下:void CChatDlg:ReceiveMessage( LPCSTR szTime, LPCSTR szMessage )CString strText;strText.Format( "%s(%s) %srn ", m_userChat.strName,m_userChat.strIP,szTime );/ 设置接收框m_rtReceived.SetSelStart( m_rtReceived.GetTextRTF().GetLength() );m_rtReceived.SetSelText( strText

温馨提示

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

评论

0/150

提交评论