基于局域网的聊天软件的设计与实现(毕业论文内容摘要:

lass libraries),以 C++类的形式封装了 Windows 的 API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。 其中包含的类包含大量 Windows 句柄封装类和很多 Windows 的内建控件和组件的封装类。 的确 , MFC 给我们提供了很便捷的编程方式,我们几乎可以不需要写任何代码就能生成一个带有菜单,工具栏和视图的窗口, MFC也提供了丰富的控件类 ,在设计对话框的时候,我们也只需要直接将控件拖进对话框里,而不用写任何代码。 iMessaging 就是典型的MFC应用程序, 采用了MFC对话框设计框架,但是 为了设计一个友好 美观的操作界面,仅用微软提供的控件类库达不到预期的 基于局域网的聊天软件的设计与实现 4 效果, 因此,就必 须重新绘制其对话框和控件 的界面。 而正由于微软将这些控件封装得很好,对于要修改它的属性,派生一些美观的子控件难度就显得比较大。 一般来说,实现对控件的重绘技术主要有子类化,自绘和重绘技术,由于MFC框架太过于标准化,因此实现这些技术也是有一定的难度,这必须要对框架的结构有一定的了解。 iMessaging 采用了的窗体背景重绘, listctrl 自绘,按钮贴图等方式实现了操作界面的美观。 主要 核心 技术 是 GDI双缓冲绘图。 双缓冲绘图就是将待 绘制的图片保存到内存里,当需要绘制到屏幕上时,再一次性投递到设备描述表里,这样既避免了窗口重绘时的闪烁,又提高了绘制的速度和效率。 BOOL CDC::CreatieCompatibleDC(CDC* pDC)函数用来创建一个兼容 DC,即在内存里创建一个 DC,以后的任何绘图 所需的 操作 ,如选用何种画笔,画刷,字体等,都将 在内存中进行,而不是在 真实 设备上下文里。 随后可以创建一张兼容位图,需要绘制的 图像将 ,函数原型如下 BOOL CBitmap::CreateCiompatibleBitmap(CDC* pDC,int nWidth,int nHeight)。 最后需要将兼容 DC里的东西投射到真实的 DC里, BOOL BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc,DWORD dwRop )函数可以实现该功能,具体用法可参考 MSDN。 对话框的绘制比较容易, MFC 为对话框提供了 OnPaint 消息相应函数,但 是在该函数下绘制并不能从根本上重绘对话框,因为该函数还会调用CDialog::OnPaint 函数, 在内部还 会 再去 重绘背景,因此要实现 完全意义上的重绘背景,就必须响应 WM_ERASEBKGND 消息, 在消息响应函数里实现我们自己的绘制方法,然后直接返回 TRUE,不再调用 CDialog::OnEraseBkgnd()函数。 为了实现像 那样的界面,必须将对话框的属性设置为 None 属性,即没有标题栏,因此只有自己去实现标题栏的功能。 CListCtrl重绘的方法有两种 ,一种是采用微软提供的 OWNERDRAW属性 ,通过重载 DrawItem 虚函数来进行重绘。 另外一种就是 Customdraw 属性,通过定义自绘消息响应函数来进行重绘。 两种方式有不同之处,前者需要对整个 ListCtrl绘制,必须考虑到每一个 item 项的绘制情况。 而后者相对比较简便,系统会在四个状态的时候通知重绘,这四个状态分别是绘制前,绘制后,擦除前,擦除后,我们就可以按照自己的需要去绘制,并且我们只用考虑其中一项 item 的绘制方 基于局域网的聊天软件的设计与实现 5 法,就可以应用于所有项。 iMessaging 采用了后者进行对列表控件的重绘,实现了好友列表控件,群列表控件类。 对于 VC6 开发环境,微软提供的通用 button 控件的外观从现在看来的确不太美观,无法实现动态状态的效果。 那么因此就必须重新 创建一个 派生 于CButton 的 按钮 类,由于该类将应用于本软件的所有按钮控件,因此做成可贴状态 图的按钮控件最为合适。 状态分为移出按钮,悬浮按钮,按下按钮,禁用按钮四种状态,我们只需要提供相应的状态的 Bitmap 就可以。 具体重绘方法就是采用前面提到的 OWNERDRAW属性,重载虚函数 DrawItem。 而捕获鼠标的移入移出消息可以使用 _TrackMouseEvent 函数,通过设置 TRACEMOUSEEVENT 结构体的值来捕捉该消息。 其移出消息对应的是 WM_MOUSELEAVE,移入按钮消息是WM_MOUSEHOVER,分别定义其消息相应函数,这样就能实现状态的更替,再进行相应的绘制。 Winsock 编程 Windows 下网络编程的规范- Windows Sockets 是 Windows 下得到广泛应用的、开放的、支持多种协议的网络编程接口。 它实现了标准 socket 编成的函数,提供了一套属于 windows 下的套接子 API。 其 通信的基础是套接 字 ( Socket),一个套接 字 是通讯的一端。 在这一端上你可以找到与其对应的一个名字。 一个正在被使用的套接字都有它的类型和与其相关的进程。 套接字存在于通讯域中。 通讯域是为了处理一般的线程通过套接字通讯而引进的一种抽象概念。 套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。 Windows Sockets 规范支持单一的通讯域,即 Inter域。 各种进程使用这个域互相之间用 Inter协议族来进行通讯( Windows Sockets 以上的版本支持其他的域,例如Windows Sockets 2)。 套接字可以根据通讯性质分类;这种性质对于用户是可见的。 应用程序一般仅在同一类的套接字间通讯。 不过只要底层的通讯协议允许,不同类型的套接字间也照样可以通讯。 用户目前可以使用两种套接字,即流套接字和数据报套接字。 流套接字提供了双向的,有序的,无重复并且无记录边界的数据流服务。 数据报套接字支持双向的数据流, 基于局域网的聊天软件的设计与实现 6 但并不保证是可靠,有序,无重复的。 也就是说,一个从数据报套接字接收信息的进程有可能发现信息重复了,或者和发出时的顺序不同。 数据报套接字的一个重要特点是它保留了记录边界。 对于这一特点,数据报套接字采用了与现在许多包交换网络(例如以太网)非常类似的模型。 其中 TCP协议就是基于流套接字,而 UDP 协议基于数据报套接字, iMessaing 聊天程序主要基于 UDP 协议,而在特殊操作下又采用 TCP 协议保证其数据的可靠性。 在设计阶段,需分析该软件适用于什么样的地协议进行通讯,还必须分析该系统应该适合于何种套接字 IO 操作。 目前, windows 提供了多种套接字 IO模型,如阻塞式 IO,非阻塞式 IO,事件 IO,重叠 IO(可提醒 IO),完成端口等,每一种 IO 模型都具有自身的优势和缺陷,其中完成端口可以达到目前 windows 平台下最高 IO 性能。 由于是基于 UDP 协议,并且考虑到在同一时间处理得命令并不会很大的情况,服务器和客户端均采用 基于非阻态的 IO模型实现命令的传输。 windows 提供了一个 CAysncSocke 异步 套接字类,它是基于 非阻塞 模型,对 Socket 函数进行了良好的 封装 , CAsyncSocket::Create()有一个参数指明了你想要处理哪些Socket事件,你关心的事件被指定以后,这个 Socket默认就被用作了异步方式。 CAsyncSocket 的 Create()函数,除了创建了一个 SOCKET 以外,还创建了个CSocketWnd 窗口对象,并使用 WSAAsyncSelect()将这个 SOCKET 与该窗口对象关联,以让该窗口对象处理来自 Socket 的事件 (消息 ),然而 CSocketWnd 收到Socket 事件之后,只是简单地回调 CAsyncSocket::OnReceive()等虚函数。 所以CAsyncSocket 的派生类,只需要在这些虚函数里添加发送和接收的代码。 因此介于微软提供的强大类方法,就很容易实现数据的通信。 基于 UDP 的文件传输 要实现文件传输的方式有很多种,常用的方式就是像 MSN 那样基于 TCP 的文件传输,这样的传输很稳定,可靠,容易实现,但传输的速度 和效率会比较慢。 为了提高传输速度,我们可以采用 UDP 协议来传输文件,由于 UDP 协议传输的数据不具有可靠性,在网络环境差的地方很容易出现丢包,乱序等现象,直接采用 基于局域网的聊天软件的设计与实现 7 UDP 来传输文件是不行的,那么就必须采用某种算法对其传输的可靠性进行保证。 我们可以模仿 TCP 的通讯机制来实现可靠的 UDP 传输。 实际上, TCP的三次握手的确让通讯数据得到了有序,有效地保证,但是正是因为这样繁琐的数据验证,导致它传输大量数据 效率低下,在 TCP 实现内部,实际上是采用了窗口大小为 1的滑动窗口算法进行数据验证,因此, UDP 可以借于此思想,采用滑动窗口算法和 CRC 冗余效验来保证数据的有效性,同时也会提高传输的速度。 由于基于 UDP的文件传输在网络环境差的情况下可能会大量丢包,因此不断重复发包会导致网络拥塞( TCP 在内部实现了流量控制,因此发生网络拥塞可能性很小),这是其最大的缺陷。 为了保证速度,就会造成网络拥塞,为了网络的畅通,就不能保证速度,两种传输方式的各据优劣势。 多线程编程 多线程编成是网络编成的基础,几乎所有的网络编 程都将涉及到多个线程的数据传输 ,并且还要实现界面和数据处理互不影响。 多线程编成虽说是基础,但是却是一个难点,因为线程同步的确比我们想象的复杂得多。 要理解多线程编成就必须从以下几个方面理解:一、操作系统多任务概念;二、 线程和进程的联系与区别;三、 如何创建并管理 销毁 一个 线程 ;四、何谓线程同步,怎样同步。 什么是多任务概念。 用通俗易懂的话就是几个运行的任务“同时”进行,比如 Windows 就是典型的多任务操作系统,我们可以同时进行听歌,打游戏,聊,在基于这种理念的系统感觉就像所有的程序 在 同时运行 一样。 但实际上在单CPU 的机子上,这些程序并不是同时在运行(即便是在多 CPU 的电脑上,也不可能所有程序同时运行),所有的程序都以某种调度算法,按照优先级,依次获取CPU 时间,每当一个程序获得 CPU 时间后,它就会得到运行,而当运行一段时间后,操作系统会将其暂停,再接换新的线程来运行。 由于电脑的速度很快,这种非并发的模式却可以达到类似并发的效果。 实际上,正如前面所说,操作系统并非是给某一程序分配时间片,准确的说是给这个程序里的某一 线 程分配了时间片。 因此,在多任务操作系统中(若有线程的话),线程将是运行的最小单位。 在 windows 系统中,进程和线程是最为重要的两个概念,因为整个系统就是基于进程和线程的。 那么进程和线程有什么区别和联系呢。 进程实际就是一个运 基于局域网的聊天软件的设计与实现 8 行的程序,该程序有自己的堆栈空间,有自己的线程(至少有一个主线程),并且可以创建其它进程或线程。 有一个概念容易搞错,就是进程是否能获得 CPU时间片。 实际上,进程是无法获取 CPU 时间片,只有该进程里的线程才能,所以说,真正执行代码的是线程,而进程只能管理线程,管理内存等。 在 iMessaging 程序里,涉及了诸多对线程的创建, 管理和 销毁 工作 ,特别是在服务器部分,为了 保证数据响应的即时性 ,必须对每一个处理创建一个线程。 常用的创建线程函数为 CreateThread 和 _beginthreadex,两函数功能几乎一样(在某些特殊地方, 两者用法需注意,推荐用后者 ),在创建前必须定义一个按照线程函数原型的函数, 将其地址传给创建线程函数,让操作系统知道即将从哪个地方运行线程。 除此之外,还可以传递参数,供线程使用。 若创建成功,该函数会返回一个 HANDLE,这个句柄标示了刚创建的线程内核对象,当线程创建成功后,必须调用 CloseHandle 函数将其关闭,这是因为如果不关闭该内核句柄,当 线程退出后,操作系统并没有销毁该线程,造成资源浪费,这是因为获取一个实内核句柄都会增加内核使用计数,只有当该内核对象的使用计数为 0时,才会销毁该内核对象,线程也是如此。 常用的销毁线程函数是 ExitThread,TermelateThread 函数,但推荐使用函数返回的方式结束进程,因为操作系统会自动调用 ExitThread 函数,并回收分配的内存资源。 由于多个线程在同时运行的时候,会对共享数据进行访问或修改,那么就必须要注意 数据同步, windows 给。
阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。