服务器端聊天程序
聊天工具
功能说明:
聊天程序由两大部分组成:服务器端聊天程序和客户端聊天程序。服务器端聊天程序负责接收来自客户端的聊天信息,并且根据客户端的要求把这些信息转发给另外一个人或多个聊天客户。客户段聊天程序则负责建立和维护与服务器端的连接,向服务器发送本客户的聊天内容,同时从服务器接收对方的响应。
先启动服务器端聊天程序,这时聊天程序需要制定一个端口号,客户端则根据这个端口号以及服务器的网址与服务器进行通信。在这里,把端口号称为“聊天频道”。
在后面的程序代码分析中将会看到,端口号并不等同于聊天频道,而是在聊天频道上增加一个固定的偏移值,使得这个聊天频道不会和系统保留的端口发生冲突。
服务器启动后将在这个指定的端口中等待客户的链接。对于公共聊天室,服务器对客户的数目不做任何限制。而对于私人聊天室,每个聊天频道则只能允许两个客户互相连接,使得一方发送的信息只能到达对方的主机中。这里的服务器提供的是公共聊天服务。
客户端聊天程序启动时,首先需要与聊天服务器建立连接,这就需要知道聊天服务器的网址及聊天频道。聊天服务器都有公开的服务器地址供用户连接。聊天频道则类似于一个个分组,一群爱好相同的人可以选择相同的聊天频道,因而不会受到自己不想要的信息。客户段聊天程序的主窗口分为两个部分:上半部分用来显示来自对方的聊天信息,下半部分用作信息输入窗口。
第一部分 服务器端聊天程序
一、设计思想和功能
服务器端聊天程序必须要能做三件事:
1、服务器端聊天程序要在特定的端口上等待来自聊天客户的连接请求,并且需要维护一个客户连接表,已记录所有成功的链接。
2、服务器端聊天程序要及时接收从各个聊天客户发来的信息,然后把这些信息转发到一个或多个客户连接。对于公共聊天室,服务器将把接收到的信息向除源端外的所有客户发送过去。
3、服务器还要监控这些连接的状态,在客户主动离开或发生故障时从列表中删除相应表项,并及时更新连接表。
这些要求可以通过CSocket类提供的功能实现。从CSocket派生出两个类:CListenSocket和CClientSocket,他们分别用来侦听客户的连接请求和建立与客户的连接。服务器只需要一个侦听套接字CListenSocket,然后根据客户的连接请求动态创建客户套接字CClientSocket。客户套接字的数量是不可预知的,因此需要一个列表来记录。,,,的CPtrList类就能实现这种功能。
二、程序设计与框架
(1)、用MFC AppWizard(exe)创建一个新工程,命名为聊天工具
(2)、在MFC AppWizard中按照下述步骤设置工程属性:
1)、设置应用程序类型为单文档模式,选择“文档/查看体系结构支持”复选框。
2)、不选择数据库支持。
3)、不选中“ActiveX 控件”复选框。
4)、不选中“隐藏工具栏”和“打印和打印预览”复选框,选择Windows Sockets复选框。
5)、默认。
6)、设置CView类为CEditView,单击“完成”按钮,应用程序聊天工具就创建完成了。 (3)、插入一个对话框IDD_DIALOG_CHANNEL,编辑各个控件的属性如下(用该资源创建对话框类CChannelDlg):
资源标识 标题 相关属性/事件 IDD_STATIC 聊天频道
IDC_EDIT_CHANNEL CString m_channel IDCANCEL 取消
IDOK 确定
IDD_DIALOG_CHANNEL 设置 CChannelDlg (4)、打开菜单编辑器菜单IDR_MAINFRAME,设置菜单命令如下: 资源标识 标题 命令处理函数 ID_FILE_START 启动服务(&S)……\tCtral+S CMyDoc::OnFileStart 三、程序代码
1、CChanneiDlg类的定义和实现部分
1)头文件ChannelDlg.h,定义CChanneiDlg的基类CDialog。
class CChannelDlg : public CDialog {
public:
CChannelDlg(CWnd* pParent = NULL); // standard constructor
enum{IDD=IDD_DIALOG_CHANNEL};
CString m_channel;
protected:
virtual void DoDataExchange(CDataExchange* pDX); //DDX DDV support
protected:
DECLARE_MESSAGE_MAP()
};
2、在ChannelDlg.cpp中,类的CChannelDlg的实现部分。在这里主要定义CChannelDlg的构造函数,初始化聊天频道。
//CChannelDlg的构造函数
CChannelDlg::CChannelDlg(CWnd* pParent /*=NULL*/)
: CDialog(CChannelDlg::IDD, pParent) {
//{{AFX_DATA_INIT(CChannelDlg)
m_channel = _T("1");
//}}AFX_DATA_INIT
}
2、类CListenSocket的定义和实现。
1)头文件ListenSocket.h,定义类CSocket的派生类CListenSocket。 class CMyDoc;
class CListenSocket:public CSocket {
public:
CListenSocket(CMyDoc* pDoc);
virtual ~CListenSocket();
public:
virtual void OnAccept(int nErrorCode);
protected:
private:
//于该套接字相对应的文档类
CMyDoc* m_pDoc;
};
2)ListenSocket.cpp, CListenSocket的实现部分,主要用来监控这些链接的状态,在客户主
动离开或发生故障时从列表中删除相应表项,并及时更新连接表。 #include "stdafx.h"
#include "聊天工具.h"
#include "ListenSocket.h" #include "聊天工具Doc.h"
//CListenSocket 构造函数
CListenSocket::CListenSocket(CMyDoc* pDoc)
{
//保存指向文档类的指针
m_pDoc=pDoc;
}
//相应连接请求
void CListenSocket::OnAccept(int nErrorCode)
{
//首先调用基类函数
CSocket::OnAccept(nErrorCode);
//确保文档类指针有效
ASSERT(m_pDoc);
//通知相应的文档类有连接请求
m_pDoc->AcceptClient(); }
3、CClientSocket类的定义和实现部分
1)、头文件Clientsocket.h,定义CSocket的派生类CClientSocket。 class CMyDoc;
class CClientSocket:public CSocket {
public:
CClientSocket(CMyDoc* pDoc);
virtual ~CClientSocket(); public:
virtual void OnReceive(int nErrorCode);
protected:
private:
CMyDoc* m_pDoc;
};
2)ClientSocket.cpp文件,CClientSocke的实现部分。
#include "stdafx.h"
#include "聊天工具.h"
#include "ClientSocket.h"
#include "聊天工具Doc.h"
//CClientSocket 构造函数
CClientSocket::CClientSocket(CMyDoc* pDoc) {
//保存对应的文档类指针
m_pDoc=pDoc;
}
//响应信息到达事件
void CClientSocket::OnReceive(int nErrorCode) {
CSocket::OnReceive(nErrorCode);
m_pDoc->ReadMessage(this);
}
4、CMainFrame类的定义和实现部分
1)CMainFrame类的接口,在头文件MainFrm.h中,定义类CFrameWnd的基类CMainFrame. class CMainFrame : public CFrameWnd {
protected: // create from serialization only
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)
virtual BOOL PreCreateWindow(CREATESTRUCT& cs); // Implementation
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const; #endif
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
5、类C聊天工具Doc的定义和实现。
1)定义文档类的头文件MyDoc.h,定义类C聊天工具Doc的接口。 //添加如下文件包含语句
#include "ListenSocket.h"
#include "ClientSocket.h"
//发送缓冲区最大长度
#define MAX_BUFFER_SIZE 100 //聊天频道偏移
#define BASE_CHANNEL 2000
class CMyDoc : public CDocument {
protected: // create from serialization only
CMyDoc();
DECLARE_DYNCREATE(CMyDoc) public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar); public:
//从客服套接字中读取信息
void ReadMessage(CClientSocket* socket);
//从侦听套接字中接受客户连接请求
void AcceptClient();
virtual ~CMyDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
afx_msg void OnFileStart();
DECLARE_MESSAGE_MAP()
private:
//向客户套接字发送信息
void SendMessage(CClientSocket* socket,char* buffer);
//显示从客户套接字中接收到的信息
void ShowMessage(CClientSocket* socket,char* buffer);
//客户套接字列表
CPtrList m_list;
//客户套接字
CClientSocket* m_clientSocket;
//侦听套接字
CListenSocket* m_listenSocket; };
2)聊天工具Doc.cpp文件,C聊天工具Doc类的实现部分。 #include "stdafx.h"
#include "聊天工具.h"
#include "聊天工具Doc.h"
#include "聊天工具View.h"
#include "ChannelDlg.h"
//构造函数
CMyDoc::CMyDoc()
{
// TODO: add one-time construction code here
m_clientSocket=NULL;
m_listenSocket=NULL;
}
//析构函数
CMyDoc::~CMyDoc()
{
//关闭所有的客户套接字
POSITION pos=m_list.GetHeadPosition();
while (pos!=NULL)
{
CClientSocket* client=(CClientSocket*)m_list.GetNext(pos);
delete client;
}
//关闭侦听套接字
if (m_listenSocket!=NULL)
{
m_listenSocket->Close();
delete m_listenSocket;
}
}
BOOL CMyDoc::OnNewDocument() {
if (!CDocument::OnNewDocument())
return FALSE;
((CEditView*)m_viewList.GetHead())->SetWindowText(NULL);
return TRUE;
}
void CMyDoc::Serialize(CArchive& ar) {
// CEditView contains an edit control which handles all serialization
((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
}
void CMyDoc::AssertValid() const {
CDocument::AssertValid(); }
void CMyDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
//“启动服务”菜单命令响应函数
void CMyDoc:: OnFileStart() {
//服务器频道选择对话框
CChannelDlg dlg;
if (dlg.DoModal()!=IDOK) return;
//如果此前已经有一个侦听套接字,则先把他关闭
if (m_listenSocket!=NULL)
{
m_listenSocket->Close();
delete m_listenSocket;
}
//创建一个新的侦听套接字
m_listenSocket=new CListenSocket(this);
//根据用户选择的服务器频道设置套接字端口
//端口号服务器频道加上频道偏移 BASE_CHANNEL
if (!m_listenSocket->Create(atoi(dlg.m_channel)+BASE_CHANNEL))
{
AFxMessageBox("创建Socket失败~");
m_listenSocket=NULL;
return;
}
//启动侦听套接字侦听连接
if (!m_listenSocket->Listen())
{
AFxMessageBox("Socket侦听异常~");
delete m_listenSocket;
m_listenSocket=NULL;
return;
}
//删除列表中所有客户套接字
POSITION pos=m_list.GetHeadPosition();
while (pos!=NULL)
{
CClientSocket* client=(CClientSocket*)m_list.GetNext(pos);
delete client;
}
}
//AcceptClient:接收一个客户的套接字
// 参数:无
// 返回值:无
void CMyDoc:: AcceptClient() {
//创建一个新的客户套接字
m_clientSocket=new CClientSocket(this);
//用这个新的套接字从侦听套接字中接收请求
if(!m_listenSocket->Accept(*m_clientSocket))
{
AFxMessageBox("Socket接收出现异常~");
delete m_clientSocket;
m_clientSocket=NULL;
return;
}
#ifdef _DEBUG
//在正式发行版本中不弹出对话框
//如果不及时关闭对话框,就会丢失部分客户连接
AFxMessageBox("有用户加入~");
#endif
//将新创建的客户套接字添加到列表中
m_list.AddTail(m_clientSocket);
}
//Readmessage:从一个客户端接字
// 参数:无
// 返回值:无
void CMyDoc:: ReadMessage(CClientSocket *socket)
{
//接受信息缓存区
char buffer[MAX_BUFFER_SIZE];
//检查接收到的信息长度是否有效
int len=socket->Receive(buffer,MAX_BUFFER_SIZE-1);
//检查接收到的信息长度是否有效
if (len<1)
{
AFxMessageBox("接收信息出现异常~");
return;
}
//在字符串末尾设置字符串结束标志位“0”
buffer[len]=0;
//在服务器端显示接收到的信息
ShowMessage(socket,buffer);
//向其他客户套接字转发信息
SendMessage(socket,buffer);
}
//ShowMessage:在服务器端显示接收到的信息
// 参数Socket:接受到信息的客户套接字
// 参数Buffer:信息缓存区
// 返回值:无
void CMyDoc::ShowMessage(CClientSocket *socket,char* buffer)
{
//获取对应的视图类
POSITION pos=GetFirstViewPosition();
while (pos!=NULL)
{
CMyView* eView=(CMyView*)GetNextView(pos);
//根据视图类获取对应的编辑控件
CEdit& edit=eView->GetEditCtrl();
//获取与该客户套接字对应的对方套接字的名称
CString addr;
UINT port;
socket->GetPeerName(addr,port);
//格式化显示信息
char msg[MAX_BUFFER_SIZE];
wsprintf(msg,"%d[%s]:%s\r\n",port,addr,buffer);
//将显示信息添加到编辑控件的末尾
int len=eView->GetWindowTextLength();
edit.SetSel(len,len);
edit.ReplaceSel(msg);
}
}
//SendMessage:向其他客户套接字转发接收到的信息
//参数:socket:接受到客户信息的套接字
//参数:buffer:信息缓冲区
//返回值:无
void CMyDoc::SendMessage(CClientSocket *socket,char* buffer)
{
//以此获取列表中的客户套接字
POSITION pos=m_list.GetHeadPosition();
while(pos!=NULL)
{
CClientSocket* client=(CClientSocket*)m_list.GetNext(pos);
//不向接收到的信息的套接字发送本信息
if(client==socket) continue;
//通知客户套接字发送信息
client->Send(buffer,strlen(buffer));
}
}
6、C聊天工具View的定义,定义CEditView派生类CC聊天工具View的类定义。 class CMyView : public CEditView {
protected: // create from serialization only
CMyView();
DECLARE_DYNCREATE(CMyView)
public:
CMyDoc* GetDocument();
public:
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
public:
virtual ~CMyView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const; #endif
protected:
DECLARE_MESSAGE_MAP()
};
四、
总结
初级经济法重点总结下载党员个人总结TXt高中句型全总结.doc高中句型全总结.doc理论力学知识点总结pdf
在这个服务器端聊天程序中,聊天服务器能够检和显示所有聊天用户的聊天内容,以及各个聊天客户的网络地址和连接端口。对于正规的聊天程序来说,每个聊天客户还会有自身的详细信息,如:昵称,编号,身份,个人简历等。所有这些功能的实现都是很简单的,可以通过服务器的数据库来保存这些用户的信息,然后给每个用户一个唯一的编号,在每条信息头部加上发送人的编号,对方客户再通过服务器数据库就能获取聊天对象的详细信息。
第二部分 客户端聊天程序
一、程序设计思想
相应的客户端必须要能够与服务器端相互匹配才能进行通信。客户段聊天程序需要完成以下功能:
1)客户端聊天程序要负责建立和维护与服务器的链接,通过获取用户的设置尝试与服务器的链接,并且随时检测连接的状态。
2)客户端聊天程序要把用户输入的信息及时的发送到聊天服务器。一般情况下,当用户输入一行信息并且按下回车键之后,聊天程序就要把这一行信息发送出去,才能及时的满足用户的交互需求。
3)客户端聊天程序要随之准备好来自服务器的信息,随时把接收到的信息显示出来,让用户及时看到对方的响应。
4)在用户退出聊天程序时要关闭与服务器的链接。比较好的做法是提前通知服务器,或者直接给服务器发送一条退出通知,使得服务器能够及时掌握客户端的连接状态,把对方客户的退出信息及时发送到对等实体上。
客户端聊天程序需要使用一个通过CSocket类派生出来的CChatSocket类来实现。该套接字用来发送一个与服务器的连接请求和维护与服务器的链接,发送和接收聊天信息,完成上述各种功能。
二、程序设计框架和步骤
(1)、用MFC AppWizard(exe)创建一个新工程,命名为客户端。
(2)、在MFC AppWizard中按照下述步骤设置工程属性:
1)、设置应用程序类型为单文档模式,选择“文档/查看体系结构支持”复选框。
2)、不选择数据库支持。
3)、不选中“ActiveX 控件”复选框。
4)、不选中“隐藏工具栏”和“打印和打印预览”复选框,选择Windows Sockets复选框。
5)、默认。
6)、设置CView类为CEditView,单击“完成”按钮,应用程序客户端就创建完成了。 (3)、插入一个对话框IDD_DIALOG_SETUP,编辑各个控件的属性如下(用该资源创建对话框类CSetupDlg):
资源标识 标题 相关属性/事件 IDD_STATIC 聊天服务器地址
IDC_EDIT_ADDRESS CString m_address IDC_STATIC 聊天频道 IDC_EDIT_CHANNEL Int m_port IDOK 确定
IDCANCEL 取消
IDD_DIALOG_SETUP 设置 CSetupDlg (4)、打开菜单编辑器菜单IDR_MAINFRAME,设置菜单命令如下:
资源标识 标题 命令处理函数 ID_FILE_CONNECT 连接服务器(&C)……\tCtral+C CMyDoc::OnFileConnect 三、程序代码
1、CSetupDlg类的定义和实现。
1)在SetupDlg.h头文件中定义类CSetupDlg。
#pragma once
class CSetupDlg : public CDialog {
public:
CSetupDlg(CWnd* pParent=NULL);
enum {IDD=IDD_DIALOG_SETUP};
CString m_address;
int m_port;
protected:
virtual void DoDataExchange(CDataExchange* pDX);
protected:
DECLARE_MESSAGE_MAP()
};
2)SetupDlg.cpp文件,实现CSetupDlg.类
#include "stdafx.h"
#include "客户端.h"
#include "SetupDlg.h"
//CSetupDlg 构造函数
CSetupDlg::CSetupDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSetupDlg::IDD,pParent) {
m_address=_T("");
//初始化聊天频道
m_port=1;
}
void CSetupDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX,IDC_EDIT_ADDRESS,m_address);
DDX_Text(pDX,IDC_EDIT_CHANNEL,m_port);
}
2、CChatSocket类的定义和实现
1)CChatSocket通过头文件ChatSocketh类定义
#pragma once
//提前声明文档类
class CMyDoc;
class CChatSocket : public CSocket {
public:
CChatSocket(CMyDoc* pDoc);
virtual ~CChatSocket();
public:
//数据到达响应函数
virtual void OnReceive(int nErrorCode);
protected:
private:
//添加一个新成员,并使其指向相应的文档类
CMyDoc* m_pDoc;
};
2)ChatSocket.cpp文件,CChatSocket类的实现部分
#include "stdafx.h"
#include "客户端.h"
#include "ChatSocket.h"
#include "客户端Doc.h"
//CChatSocket 构造函数
CChatSocket::CChatSocket(CMyDoc* pDoc) {
//保存文档类指针
m_pDoc=pDoc;
}
//相应数据到达信号
void CChatSocket::OnReceive(int nErrorCode) {
//先调用基类函数进行处理
CSocket::OnReceive(nErrorCode);
//通知文档类接收数据
m_pDoc->ReceiveMessage();
}
CChatSocket::~CChatSocket()
{
}
3、MainFrame类的定义和实现
1)MainFrame类的定义,通过头文件MainFrm.h定义类的接口。
class CMainFrame : public CFrameWnd {
protected: // create from serialization only
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)
public:
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs,CCreateContext* pContext);
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const; #endif
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
private:
//添加一个窗口分隔栏
CSplitterWnd m_splitter;
};
#endif
2)MainFrm..cpp文件,CMainFrm类的实现部分。
#include "stdafx.h"
#include "客户端.h"
#include "MainFrm.h"
#include "客户端Doc.h"
#include "客户端View.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//消息响应定义
ON_WM_CREATE()
END_MESSAGE_MAP()
//响应子窗口创建消息
//用一个分割栏创建两个分割窗体
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs,CCreateContext* pContext)
{
//创建一个两行一列的静态分割栏
if (!m_splitter.CreateStatic(this,2,1))
return FALSE;
//获取客户窗口大小
CRect rect;
GetClientRect(&rect);
//设置第一个分割窗口大小
CSize size=rect.Size();
size.cy -=150;
//以CEditView为基础创建第一个分割窗体
if(!m_splitter.CreateView(0,0,RUNTIME_CLASS(CEditView),size,pContext))
return FALSE;
//以C客户端View为基础创建第二个分割窗体
if (!m_splitter.CreateView(1,0,RUNTIME_CLASS(CMyView),CSize(0,0),pContext))
return FALSE;
//将第二个窗口设置为激活窗口
m_splitter.SetActivePane(1,0,(CView*)m_splitter.GetPane(1,0));
return FALSE;
}
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
CMainFrame::CMainFrame()
{
// TODO: add member initialization code here }
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
return 0;
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) {
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
return TRUE;
}
#ifdef _DEBUG
void CMainFrame::AssertValid() const {
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const {
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
4、C客户端Doc类的定义与实现部分
1)在头文件MyDoc.h中定义C客户端Doc类,定义C客户端Doc类的接口。 #include "ChatSocket.h"
//定义发送缓冲区的最大长度
#define MAX_BUFFER_SIZE 100 //定义聊天频道偏移
#define BASE_CHANNEL 2000
#if !defined(AFX_DOC_H__05851AB0_9BA8_4964_BAF9_6D6F1EFA4E33__INCLUDED_)
#define AFX_DOC_H__05851AB0_9BA8_4964_BAF9_6D6F1EFA4E33__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CMyDoc : public CDocument {
protected: // create from serialization only
CMyDoc();
DECLARE_DYNCREATE(CMyDoc) public:
public:
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar); public:
//发送聊天内容
void SendMessage(LPSTR msg);
//接收数据
void ReceiveMessage(void);
virtual ~CMyDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
protected:
//相应菜单命令
afx_msg void OnFileConnect();
DECLARE_MESSAGE_MAP()
private:
//在视图中显示信息
void ShowMessage(LPSTR msg);
//连接套接字
CChatSocket* m_socket;
};
#endif
2)客户端Doc.cpp文件中,CMyDoc类的实现部分。
#include "stdafx.h"
#include "客户端.h"
#include "SetupDlg.h"
#include "客户端View.h"
#include "客户端Doc.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
IMPLEMENT_DYNCREATE(CMyDoc, CDocument) //消息映射
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
ON_COMMAND(ID_FILE_CONNECT, OnFileConnect)
END_MESSAGE_MAP()
//C客户端Doc 构造函数
CMyDoc::CMyDoc()
{
//将套接字设置为空
m_socket=NULL;
}
CMyDoc::~CMyDoc()
{
}
BOOL CMyDoc::OnNewDocument() {
if (!CDocument::OnNewDocument())
return FALSE;
((CEditView*)m_viewList.GetHead())->SetWindowText(NULL);
return TRUE;
}
void CMyDoc::Serialize(CArchive& ar) {
((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
}
#ifdef _DEBUG
void CMyDoc::AssertValid() const {
CDocument::AssertValid();
}
void CMyDoc::Dump(CDumpContext& dc) const {
CDocument::Dump(dc); }
#endif //_DEBUG
//相应服务器连接命令
void CMyDoc::OnFileConnect()
{
//显示连接设置对话框
CSetupDlg dlg;
if (dlg.DoModal()!=IDOK) return;
//先关闭上次的有效连接
if(m_socket!=NULL)
{
m_socket->Close();
delete m_socket;
}
//创建一个新的套接字
m_socket=new CChatSocket(this);
if(!m_socket->Create())
{
AfxMessageBox("创建socket失败~");
delete m_socket;
m_socket=NULL;
return;
}
//连接服务器
if(!m_socket->Connect(dlg.m_address,dlg.m_port+BASE_CHANNEL))
{
AfxMessageBox("连接服务器失败~");
delete m_socket;
m_socket=NULL;
return;
}
//显示连接成功信息
AfxMessageBox("成功连接到聊天服务器~"); }
//ReceiveMessage:接收服务器发送的数据
// 参数:无
// 返回值:无
void CMyDoc::ReceiveMessage()
{
//确认套接字的可用性
ASSERT(m_socket);
//通过套接字接受信息
char buffer[MAX_BUFFER_SIZE];
int len=m_socket->Receive(buffer,MAX_BUFFER_SIZE-1);
//检查接收到的数据的长度是否有效
if (len<1)
{
AfxMessageBox("接受信息异常~");
return;
}
//剪掉缓冲区末端的无效数据
buffer[len]=0;
//显示接收到的数据
ShowMessage(buffer);
}
//ShowMessage:在视图中显示信息
//参数msg:要显示的内容
//返回值:无
void CMyDoc::ShowMessage(LPSTR msg) {
//以此获取与本文档对应的视图
POSITION pos=GetFirstViewPosition();
while(pos!=NULL)
{
CEditView* view=(CEditView*)GetNextView(pos);
//只把信息显示到CEditVIew视图中
if (view->IsKindOf (RUNTIME_CLASS(CMyView))) continue;
//获取与视图相关联的编辑控件
CEdit& edit=view->GetEditCtrl();
//格式化显示信息
char buffer[MAX_BUFFER_SIZE];
wsprintf(buffer,"%s\r\n",msg);
//将显示信息添加到编辑控件内容的末端
int len=edit.GetWindowTextLength();
edit.SetSel(len,len);
edit.ReplaceSel(buffer);
}
}
//SendMessage:向服务器发送信息
//参数msg:要发送的内容
//返回值:无
void CMyDoc::SendMessage(LPSTR msg) {
//如果没有创建有效的链接,则直接返回
if (m_socket==NULL) return;
//通过套接字发送信息
if(!m_socket->Send(msg,strlen(msg)))
{
AfxMessageBox("发送消息失败~");
return;
}
}
5、CView类的定义和实现
1)、在头文件C客户端View.h中定义CView类的接口。
class CMyView : public CEditView {
protected: // create from serialization only
CMyView();
DECLARE_DYNCREATE(CMyView) public:
CMyDoc* GetDocument();
public:
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
public:
virtual ~CMyView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
protected:
//字符消息响应
afx_msg void OnChar(UINT nChar,UINT nRepCnt,UINT nFlags);
DECLARE_MESSAGE_MAP()
};
2)、CView.cpp文件,实现CView类。
#include "stdafx.h"
#include "客户端.h"
#include "客户端Doc.h"
#include "客户端View.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
IMPLEMENT_DYNCREATE(CMyView, CEditView) //消息映射
BEGIN_MESSAGE_MAP(CMyView, CEditView)
ON_WM_CHAR()
END_MESSAGE_MAP()
CMyView::CMyView()
{
// TODO: add construction code here }
CMyView::~CMyView()
{
}
BOOL CMyView::PreCreateWindow(CREATESTRUCT& cs) {
BOOL bPreCreated = CEditView::PreCreateWindow(cs);
cs.style &= ~(ES_AUTOHSCROLL|WS_HSCROLL); // Enable word-wrapping
return bPreCreated;
}
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here }
#ifdef _DEBUG
void CMyView::AssertValid() const
{
CEditView::AssertValid();
}
void CMyView::Dump(CDumpContext& dc) const {
CEditView::Dump(dc);
}
CMyDoc* CMyView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyDoc)));
return (CMyDoc*)m_pDocument;
}
#endif //_DEBUG
void CMyView::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)
{
//如果用户按下了回车键则发送数据
if(nChar==VK_RETURN)
{
//获取对应的文档对象
CMyDoc* pDoc=GetDocument();
//确保文档对象有效
ASSERT_VALID(pDoc);
//获取与本试图对应的编辑控件
CEdit& edit=GetEditCtrl();
//从编辑控件中获取最后一行文本内容
char buffer[MAX_BUFFER_SIZE];
memset(buffer,0,sizeof(buffer));
int index=edit.GetLineCount()-1;
edit.GetLine(index,buffer,MAX_BUFFER_SIZE-1);
//通过文档类发送文本内容
pDoc->SendMessage(buffer);
}
//调用基类处理函数
CEditView::OnChar(nChar,nRepCnt,nFlags); }