(强烈推荐)VC++网络聊天软件 - 毕业论文

更新时间:2024-05-22 01:40:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

VC++网络聊天软件

摘 要

网络聊天室,其实质就是基于Internet的一种网络聊天软件。它可以在网络环境下进行实时的一对多或多对多的匿名交谈。网络聊天室软件的制作方法有很多种,比如用MFC 和Java等语言设计的就有很多,本次课程设计主要是利用MFC所提供的各种控件尤其是WinSock控件,基于CS模式,设计了一个网络聊天室,圆满地实现在网络上实时聊天及多种人性化的辅助功能。

关键词:WinSock;CS ;Visual C++ 6.0;聊天室

目录

1.查找资料 ....................................................... 4 1.1 网络聊天程序的开发背景 ....................................... 4 1.2 网络聊天程序的设计目标 ....................................... 4 2.相关知识 ....................................................... 5 2.1 WINSOCK介绍 .................................................. 5 2.2 MFC 的CASYNCSOCKET类和CSOCKET类 .............................. 6 2.3 利用CSOCKET进行有连接的通

信..................................8 2.4

择...............................................9 3.详细设

计........................................................14 3.1聊天程序

现....................................................15

4.程序流程图 .................................................... 62 4.1 建立连接的流程图 ............................................ 62 4.2 客户关闭或者注销时的程序流程图 .............................. 63

5.程序运行结果 .................................................. 64 5.1 客户端 ...................................................... 64 5.2服务端 ...................................................... 64 6、讨论及进一步研究建

议...........................................65 7

得..................................................66 8

献.......................................................67

1.查找资料

当前是数字信息时代,网络时代,获得信息的渠道做种多样。而最为快速的当然就是网络了。所以在课程设计动员之后,我们就马不停蹄的通过互联网收缩有关网络编程和MFC编程的有关资料。找到大量资料之后,我们还有根据课程设计的要求进行筛选资料,最后选定方案和确定实现方法。这也是我们第一阶段的工作。

1.1 网络聊天程序的开发背景

近年来,互连网发展日新月异,网络使空间的距离不再成为人们沟通的障碍,世界各个角落的人们可以通过Internet收发邮件、实时聊天、获取最新的资讯。所以网络聊天的程序早已被前人所实现,而且实现的方

法多种多样。因此,本次课程设计,我们主要借鉴前人创立的方法和编程经验来制作一个CS聊天室程序,通过制作该程序达到的学习网络socket编程和使用MFC编程的相关知识。

1.2 网络聊天程序的设计目标

本课题是设计一个网络聊天的程序,包括服务器端和客户端,主要功能为: 客户端部分:

1、输入服务器端IP地址和端口号进行连接 2、发送消息给服务器端并显示服务器端回传的消息 3、在客户端增加历史聊天记录和当天聊天记录

4、可以设置个性昵称、增添快捷表情、更换个性图像和背景设计

服务器端部分:

1、 立服务器端与客户端的连接请求 2、接收所有用户发送的消息 3、向所有在线用户群发消息

4、在客户端增加历史聊天记录和当天聊天记录

5、可以设置个性昵称、增添快捷表情、更换个性图像和背景设计 通过这次课程设计,可以比较深入的了解和掌握WINSOCK控件基本属性、方法和事件,熟悉VC++的开发环境。理解网络聊天通信的概念,输控制协议(TCP)进行数据交流,初步掌握网络聊天通信程序的设计方法,以及WINDOWS编程的一些方法。并能巩固和扩展之前学过的知识,进行项目的设计开发训练,更好的适应社会的需求。

2.相关知识

2.1 Winsock介绍

Windows Sockets规范以U.C. Berkeley大学BSD UNIX中流行的Socket接口为范例定义了一套Micosoft Windows下网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。

Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。此外,在一个特定版本Windows的基础上,Windows Sockets也定义了一个二进制接口(ABI),以此来保证应用Windows Sockets API的应用程序能够在任何网络软件供应商的符合Windows Sockets协议的实现上工作。因此这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。

遵守这套Windows Sockets规范的网络软件,我们称之为Windows Sockets兼容的,而Windows Sockets兼容实现的提供者,我们称之为Windows Sockets提供者。一个网络软件供应商必须百分之百地实现Windows Sockets规范才能做到现Windows Sockets兼容。

任何能够与Windows Sockets兼容实现协同工作的应用程序就被认为是具有Windows Sockets接口。我们称这种应用程序为Windows Sockets应用程序。

Windows Sockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCPIP)连接,尤其要指出的是所有的Windows

Sockets实现都支持流套接口和数据报套接口.

应用程序调用Windows Sockets的API实现相互之间的通讯。Windows Sockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通讯工作。

2.2 MFC 的CAsyncsocket类和CSocket类

构造一个 CAsyncSocket 对象并使用该对象创建基础 SOCKET 句柄。 ①套接字的创建:遵循两阶段构造的 MFC 模式。 例如:

CAsyncSocket sock;sock.Create( ); Use the default parameters - 或 -

CAsyncSocket* pSocket = new CAsyncSocket;int nPort = 27; pSocket-> Create( nPort, SOCK_DGRAM );

上面的第一个构造函数在堆栈上创建一个 CAsyncSocket 对象,第二个构造函数在堆上创建 CAsyncSocket 。上面的第一个 Create 调用使用默认参数创建流式套接字,第二个 Create 调用创建具有指定端口和地址的数据文报套接字。(任一个 Create 版本都可以和任一种构造方法一起使用。)

Create 的参数有: “端口”:短整型。

对于服务器套接字,必须指定端口。对于客户端套接字,通常接受此参数的默认值,该值允许 Windows Sockets 选择端口。 套接字类型: SOCK_STREAM (默认值)或 SOCK_DGRAM 。

套接字“地址”,如“ftp1.scau.edu.cn”或“128.56.22.8”。 该地

址为网络上的网际协议 (IP) 地址。很可能要始终依赖此参数的默认值。

②如果套接字是客户端,则使用 CAsyncSocket::Connect 将此套接字对象连接到服务器套接字。 如果套接字是服务器,则将套接字设置为开始侦听(使用 CAsyncSocket::Listen)来自客户端的连接尝试。接收到连接请求时,用 CAsyncSocket::Accept 接受该请求。

③接受连接后,可以执行验证密码等任务。

注意 Accept 成员函数采用对新的空 CSocket 对象的引用作为它的参数。在调用 Accept 之前,必须构造该对象。如果此套接字对象超出范围,则连接关闭。不要对这个新套接字对象调用 Create 。

④通过调用 CAsyncSocket 对象的封装 Windows Sockets API 函数的成员函数,与其他套接字进行通信。

⑤如果在堆栈上创建了套接字对象,当包含函数超出范围时将调用此对象的析构函数。如果使用 new 运算符在堆上创建了套接字对象,则您必须负责使用 delete 运算符销毁此对象。 析构函数在销毁对象之前调用对象的 Close 成员函数。

2.3 利用CSocket进行有连接的通信

微软的MFC把复杂的WinSock API函数封装到类里,这使得编写网络应用程序更容易。CAsyncSocket类逐个封装了WinSock API,为高级网络程序员 提供了更加有力而灵活的方法。这个类基于程序员了解网络通讯的假设,目的是为了在MFC中使用WinSock,程序员有责任处理诸如阻塞、字节顺序和在Unicode与MBCS 间转换字符的任务。

为了给程序员提供更方便的接口以自动处理这些任务,MFC给出 了CSocket类,这个类是由CAsyncSocket类继承下来的,它提供了比

CAsyncSocket更高层的WinSock API接口。CSocket类和CSocketFile类可以与CArchive类一起合作来管理发送和接收的数据,这使管理数据收发更加便利。CSocket对象提供阻塞模式,这对于CArchive的同步操作是至关重要的。阻塞函数(如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept())直到操作完成后才返回控制权。

因此如果需要低层控制和高效率,就使用CasyncSock类;如果需要方便,则可使用CSocket类。

由于我们对网络底层的了解比较浅薄,所以这次课程设计我们选择了采用CSocket类来编程实现。下面主要针对这种方法进行讲述。

使用CSocket对象涉及CArchive和CSocketFile 类对象。以下介绍的针对字节流型(即基于TCPIP协议)套接字的操作步骤中,只有第3和第4步对于客户端和服务端操作是不同的,其他步骤都相同。 ① 构造一个CSocket对象。

② 使用这个对象的Create()成员函数产生一个socket对象。在客户端程序中,除非需要数据报套接字,Create()函数一般情况下应该使用默认参数。而对于服务端程序,必须在调用Create时指定一个端口。需要注意的是,CArchive类对象不能与数据报(UDP)套接字一起工作,因此对于数据报套接字,CAsyncSocket和CSocket 的使用方法是一样的。

③ 如果是客户端套接字,则调用CAsyncSocket ∷Connect()函数与服务端套接字连接;如果是服务端套接字,则调用CAsyncSocket∷Listen()开始监听来自客户端的连接请求,收到连接请求后,调用CAsyncSocket∷Accept()函数接受请求,建立连接。请注意Accept()成员函数需要一个新的并且为空的CSocket对象作为它的参数。

④ 重载CSocket类的部分函数。客户端需要重载OnReceive(int i)

和 OnClose(int i);服务器端需要重载OnAccept(int i) 、OnClose(int i) 和 OnReceive(int i);

⑤ 调用Send()的方法来发送数据,调用Receive()的方法来接受数据。

⑥ 调用Close()方法来关闭套接字以终止通信。 ⑦ 通讯完毕后,销毁CSocket对象。

2.4 方案比较与选择

系统分析与设计

1、系统构架方式如下图所示:

设计出一个完整的网络聊天程序,使之实现以上基本要求。 1、 服务端需要完成的三件事

1) 在特定端口等待连接请求,并需要维护一个客户连接表,已记

录所有成功连接。

2) 及时接受消息,然后转发到客户连接。

3) 监控连接状态,客户离开或故障时从列表中删除相应表项,并

及时更新连接表。

2、 客户端需要完成的三件事

1) 建立与维护服务器的连接,并随时监测连接状态。

2) 把用户输入的信息及时发送到服务端,同时准备好接受,并显

示信息。

3) 在用户退出时关闭连接。

程序方案比较:

方案一:基于TCP的socket编程

TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。

服务器端程序流程如下: 1) 创建套接字(socket);

2) 将套接字绑定到一个本地地址和端口上(bind); 3) 将套接字设为监听模式,准备接受客户请求(listen); 4) 等待客户请求到来;当请求到来后,接受连接请求,返回一个新的

对应于此次连接的套接字(accept);

5) 用返回的套接字和客户端进行通信(sendrecv); 6) 返回,等待另一客户请求; 7) 关闭套接字; 客户端程序流程如下: 1) 创建套接字(socket);

2) 向服务器发出连接请求(connect); 3) 和服务器端进行通信(sendrecv); 4) 关闭套接字。

在服务器端,当调用accept函数时,程序就会等待,等待客户调用connect函数发出连接请求,然后服务器端接受该请求,于是双方就建立了连接。之后,服务器端和客户端就可以利用send和recv函数进行通信了。因为服务器需要接受客户端的请求,所以必须告诉本地主机它打算在哪个IP地址和哪个端口上等待客户要求,因此必须调用bind函数来实现这一功能。而对客户端来说,当它发起连接请求,服务器端接受请求后,在服务端就保存了改客户端的IP地址和端口的信息。这样,对服务器端

来说,一旦建立连接之后,实际上它已经保存了客户端的IP地址和端口号的信息,就可以利用所返回的套接字调用sendrecv函数与客户端进行通信。

程序流程图如下:

方案二:基于UDP(面向无连接)的socket程序

UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去。UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境

服务器端也叫接收端,对于基于UDP(面向无连接)的套接字编程来说,它的服务器端和客户端这种概念不是很强化,我们也可以把服务器端,即先启动的一端称为接收端,发送数据的一端称为发送端,也称为客户端。

服务端程序编写流程如下: 1) 创建套接字(socket);

2) 将套接字绑定到一个本地地址和端口上(bind); 3) 等待接受数据(recvfrom); 4) 关闭套接字。

虽然面向无连接的socket编程无须建立连接,但是为了完成这次通信,对于接受端来说,它必须先启动以接受客户端发送的数据,因此接收端必须告诉主机它是在哪个地址和端口上等待数据的到来,接收端(服务器端)必须调用bind函数将套接字绑定到一个本地地址和端口上。

客户端程序编写流程如下: 1) 创建套接字(socket); 2) 向服务器发送数据(sengto);

3) 关闭套接字。

在UDP的套接字编程时,利用的是sendto和recvfrom这两个函数实现数据的发送和接收,而基于TCP的套接字编程时,发送数据是调用send函数,接受数据调用recv函数。

程序流程图如下:

方案比较结果

TCP与UDP最基本的区别在于基于连接与无连接,相比之下,第一种方案对系统的要求以及数据量都比较大,但是保证数据的正确性与数据顺序,在传输大量数据的时候具有更高的可靠性。至于第二种方案的优点在于传输的速度快,程序结构精简。

总的来说,我认为TCP协议更能满足目前各行业对远程数据传输的要求,它提供更稳定更便利的传输通道,满足了对安全性的要求以及远程数据传输的要求。

所以我们小组选择方案一。

3.详细设计

我们利用MFC提供的CSocket类来编程实现一个网络聊天室。那么就需要一个聊

天的服务器(即是服务端),它可以和很多客户端进行通信,从而把来自不同的客户的聊天信息转交到所有其他的客户端。当然也需要用户界面(客户端)。这样就形成了一个采用ClientServer结构的并可以多人同时在线的聊天室。同时,服务器端应该有一定的管理功能,如手动自动响应申请、设置服务器名称和服务器端口、在线名单统计、单独断开某人连接以及保持聊天记录等功能。对于客户端,因为是面向用户,所以外观和功能都应有更高的要求。实现诸如:手动输入IP 和用户名、头像切换、

心情书写、表情输入、软件皮肤切换、注销登录、保存聊天记录以及伸缩界面等功能。下面详细介绍各项功能是如何实现的。

3.1聊天程序的实现

(1)首先启动Visual C++ 6.0,利用MFC AppWizard[EXE]建立一个

新的MFC工程,工程名为chat,在MFC AppWizard Step1的时候选择Dialog based 即基于对话框,在Step4时勾选Windows Sockets选项(如图表 0-2),其他默认值下一步。

图表 0-1

图表 0-2

(2)客户端的界面如图图表 0-3所示。其中包含文档编辑框、按键控件、静态文本控件组成。左半边由上自下分别是:头像、昵称、当天聊天信息显示、12个表情按钮、消息输入框和发送按钮。右半边有历史记录显示框、历史记录显示按钮。

图表 0-3软件界面

图表 0-4

控件类型及各自ID 标题 昵称: 服务器IP: 端口号(默认5000): 头像 表情1 表情2 表情3 表情4 表情5 表情6 表情7 表情8 控件类型 Static Static Static Button Button Button Button Button Button Button Button Button 控件ID IDC_STATIC IDC_STATIC IDC_STATIC IDC_TouXiang IDC_BQ1 IDC_BQ2 IDC_BQ3 IDC_BQ4 IDC_BQ5 IDC_BQ6 IDC_BQ7 IDC_BQ8

表情9 表情10 表情11 表情12 发送 连接 建立 聊天纪录 聊天信息显示 信息输入 昵称输入 服务器IP输入 端口输入 聊天记录显示

Button Button Button Button Button Button Button Button Edit Edit Edit Edit Edit Edit IDC_BQ9 IDC_BQ10 IDC_BQ11 IDC_BQ12 IDC_SEND IDC_CONNECT IDC_SETSERVER IDC_LiaoTianJiLu IDC_SHOWTEXT IDC_INPUTTEXT IDC_NAME IDC_SERVERIP IDC_PORT IDC_ShowHistory 聊天程序总共有6个类。其中,CChatApp和CAboutDlg由AppWizard实现;另外,CServerSocket和CClientSocket分别负责服务端和客户端的网络通信功能;CChatDlg为程序的主控类,主界面、通信方式及程序逻辑均由该类实现,它继承自CDialog类;CMessgage是对消息的封装。

CClientSocket

通过相应的OnReceive消息来接收数据,响应OnClose消息来断开对话的处理,实现以CArchive对数据进行的串行化。

类定义代码如下:

#include\

CArchive *m_aSessionOut; CSocketFile *m_sfSocketFile; CChatDlg *m_dlg;

bool m_bInit; 是否进行了初始化 bool m_bClose; 连接是否关闭 public:

void Init(CChatDlg *dlg); BOOL SendMessage(CMessg *msg); void CloseSocket(); public:

static int GetLocalHostName(CString &sHostName); 获得本地计算机名称

static int GetIpAddress(const CString &sHostName, CString &sIpAddress); 获得本地IP

static int GetIpAddress(const CString &sHostName,BYTE &f0, BYTE &f1,BYTE &f2,BYTE &f3); 获得本地IP

static CString ErrorReason(int tag); public:

virtual void OnReceive(int nErrorCode); virtual void OnClose(int nErrorCode); public:

CClientSocket();

virtual ~CClientSocket(); protected: };

CClientSocket.CPP中的代码:

#include \

m_aSessionOut=NULL;

m_sfSocketFile=NULL; m_bInit=false; m_bClose=false; }

CClientSocket::~CClientSocket() {

if(m_aSessionIn)

delete m_aSessionIn; if(m_aSessionOut)

delete m_aSessionOut; if(m_sfSocketFile)

delete m_sfSocketFile; }

ClientSocket成员函数

void CClientSocket::OnReceive(int nErrorCode) {

CSocket::OnReceive(nErrorCode); OnReceive()函数的实现

do

{

CMessg temp;

temp.Serialize(*m_aSessionIn);

m_dlg->m_sMsgList+=temp.m_strText+\

m_dlg->SetDlgItemText(IDC_SHOWTEXT,m_dlg->m_sMsgList);

FILE* fp;

fp = fopen(\ fputs(m_dlg->m_tmpMsgList,fp); fclose(fp);

m_dlg->m_tmpMsgList=temp.m_strText+\

m_dlg->SetDlgItemText(IDC_SHOWTEXT,m_dlg->m_sMsgList);

int linenum=((CEdit*)

(m_dlg->GetDlgItem(IDC_SHOWTEXT)))->GetLineCount();

((CEdit*)

(m_dlg->GetDlgItem(IDC_SHOWTEXT)))->LineScroll(linenum);

if(!m_dlg->m_bClient) { for(POSITION

pos=m_dlg->m_connectionList.GetHeadPosition();

pos!=NULL;) {

CClientSocket *t=(CClientSocket*) m_dlg->m_connectionList.GetNext(pos);

if(t->m_->IsBufferEmpty()); }

void CClientSocket::Init(CChatDlg *dlg) {

m_sfSocketFile=new CSocketFile(this); m_aSessionIn=new

CArchive(m_sfSocketFile,CArchive::load);

m_aSessionOut=new

CArchive(m_sfSocketFile,CArchive::store);

m_bClose=false; this->m_dlg=dlg; } *

*SendMessage()函数的实现 *主要功能: *将信息串行化 *

BOOL CClientSocket::SendMessage(CMessg *msg) {

if(m_aSessionOut!=NULL) {

msg->Serialize(*m_aSessionOut);

本文来源:https://www.bwwdw.com/article/t527.html

Top