MFC+Winsock类cs聊天程序开发Word格式.docx
《MFC+Winsock类cs聊天程序开发Word格式.docx》由会员分享,可在线阅读,更多相关《MFC+Winsock类cs聊天程序开发Word格式.docx(16页珍藏版)》请在冰豆网上搜索。
5.用ClassWizard为控件对象定义相应的成员变量,如下表所示:
表2控件对应成员变量
成员变量名
CString
m_strMessage
CListBox
m_listMessage
m_listUsers
6.添加对话框,ID为IDD_DIALOG_LOGIN,Caption为“登录”。
7.在对话框中添加控件,如下图所示:
8.对话框中的控件属性如下:
表3登录对话框控件属性
IDC_EDIT_IP
IDC_EDIT_PORT
IDC_EDIT_USERNAME
IDC_EDIT_PASSWORD
选Styles->
Password
IDOK
连接
DefaultButton
IDCANCEL
取消
9.添加一个新类,类名为CLoginDlg,基类为CDialog,DialogID为IDD_DIALOG_LOGIN。
10.用ClassWizard为对话框的控件对象定义成员变量,如下表所示:
表4登录对话框控件对应成员变量
m_strIP
m_strPassword
UINT
m_nPort
m_strUserName
11.为CChatClientDlg类添加“登录”按钮的单击事件消息处理函数OnButtonLogin(),并添加如下代码:
CLoginDlgdlg;
intnRet=-1;
nRet=dlg.DoModal();
12.在ChatClientDlg.cpp中添加:
#include"
LoginDlg.h"
。
编译项目并运行,单击登录按钮,看看运行情况。
此时单击退出按钮无反应,tellmewhy?
13.添加一个从CSocket类派生的新类CMySocket,添加类型为CChatClientDlg*的成员变量m_pDlg,并修改默认构造函数和析构函数如下:
(注意,也应修改类声明中的构造函数原型)
CMySocket:
:
CMySocket(CChatClientDlg*pDlg)
{
m_pDlg=pDlg;
}
~CMySocket()
m_pDlg=NULL;
在CMySocket类的头文件中,添加前置声明:
classCChatClientDlg;
14.为CChatClientDlg类添加类型为CMySocket*的成员变量m_pMySocket,类型为BOOL的成员变量m_bGroupChat。
在CChatClientDlg类的头文件中,添加前置声明:
classCMySocket;
15./////在CChatClientDlg:
OnInitDialog()中添加如下代码:
//TODO:
Addextrainitializationhere
m_bGroupChat=TRUE;
//默认聊天方式为群聊
((CButton*)GetDlgItem(IDC_RADIO_GROUP))->
SetCheck(TRUE);
//设置群聊按钮被选中
((CButton*)GetDlgItem(IDC_BUTTON_LOGOUT))->
EnableWindow(FALSE);
((CEdit*)GetDlgItem(IDC_EDIT_MESSAGE))->
//设置编辑框不可用
16.在CChatClientDlg类的OnButtonLogin()中添加如下代码:
voidCChatClientDlg:
OnButtonLogin()
//TODO:
Addyourcontrolnotificationhandlercodehere
//新增加的代码
switch(nRet)
{
caseIDOK:
m_pMySocket=newCMySocket(this);
if(!
m_pMySocket->
Create())
{
deletem_pMySocket;
m_pMySocket=NULL;
AfxMessageBox("
创建套接字失败!
"
);
return;
}
Connect(dlg.m_strIP,dlg.m_nPort))
连接服务器失败!
((CButton*)GetDlgItem(IDC_BUTTON_LOGOUT))->
EnableWindow(TRUE);
((CButton*)GetDlgItem(IDC_BUTTON_LOGIN))->
((CEdit*)GetDlgItem(IDC_EDIT_MESSAGE))->
break;
caseIDCANCEL:
default:
}
在CChatClientDlg类的实现文件中,包含CMySocket类的头文件,即:
MySocket.h"
。
注意:
C++头文件不能相互包含,详情可参考以下链接
二、服务器端
创建服务器框架程序,创建监听套接字并接受连接请求。
1.在Chat工作区,用MFCAppWizard创建基于对话框的项目ChatServer,并在Step2中选中WindowsSocket选项。
2.添加一个从CSocket类派生的CListenSocket类,为该类添加类型为CChatServerDlg*的成员变量m_pDlg,并修改构造函数和析构函数如下:
CListenSocket:
CListenSocket(CChatServerDlg*pDlg)
~CListenSocket()
在类CListenSocket的头文件中,添加前置声明:
classCChatServerDlg;
3.为CChatServerDlg类添加类型为CListenSocket*的成员变量m_pListenSocket
在类CChatServerDlg的头文件中,添加前置声明:
classCListenSocket;
4.在CChatServerDlg类的OnInitDialog()中添加如下代码:
m_pListenSocket=newCListenSocket(this);
if(!
m_pListenSocket->
Create(8000))
deletem_pListenSocket;
m_pListenSocket=NULL;
AfxMessageBox("
创建套接字错误!
returnFALSE;
Listen())
启动监听错误!
在CChatServerDlg类的实现文件中,包含CListenSocket类的头文件,即:
ListenSocket.h"
5.给项目添加CClientSocket类,该类从CSocket类派生,用于服务器和客户机进行消息传输。
给该类添加类型为CChatServerDlg*的成员变量m_pDlg,并修改构造函数和析构函数如下:
CClientSocket:
CClientSocket(CChatServerDlg*pDlg)
~CClientSocket()
在类CClientSocket的头文件中,添加前置声明:
6.为CChatServerDlg类添加类型为CClientSocket*的成员变量m_pClientSocket
classCClientSocket;
7.为CChatServerDlg类添加成员函数voidOnAccept(void),并添加如下代码:
CClientSocket*pSocket=newCClientSocket(this);
m_pListenSocket->
Accept(*pSocket);
在CChatServerDlg类的实现文件中,包含CClientSocket类的头文件,即:
ClientSocket.h"
8.为CListenSocket类添加虚函数OnAccept(),并添加如下代码:
CSocket:
OnAccept(nErrorCode);
m_pDlg->
OnAccept();
在CListenSocket类的实现文件中,包含CChatServerDlg类的头文件,即:
ChatServerDlg.h"
编译项目,并运行服务器和客户机,试一试客户机是否能够连接服务器。
任务3:
定义消息格式、消息类型,实现用户的登录。
图1客户机/服务器之间的消息传输顺序图
表1消息格式
序号
字段名
类型
长度
说明
1
type
整型
4字节
消息的类型
2
username
字符串
20字节
用户名
3
data
256字节
数据,如文字消息
表2消息类型
种类
LOGIN_REQUEST
登录请求消息
LOGIN_SUCCESS
登录成功消息
LOGIN_FAILED
登录失败消息
4
LOGOUT_REQUEST
注销消息
5
ADD_USER
增加用户消息
6
REMOVE_USER
删除用户消息
7
PRIVATE_MESSAGE
私聊消息
8
PUBLIC_MESSAGE
群聊消息
连接并登录服务器的顺序图大致如下:
实现过程:
1.在客户端的对话框类CChatClientDlg的头文件中添加如下代码:
enumPROTO_TYPE//消息类型定义
LOGIN_REQUEST,
LOGIN_SUCCESS,
LOGIN_FAILED,
LOGOUT_REQUEST,
ADD_USER,
REMOVE_USER,
PRIVATE_CHAT,
PUBLIC_CHAT
};
typedefstructtagPacket//数据包格式定义
PROTO_TYPEtype;
charusername[20];
chardata[256];
}Packet;
2.在CChatClientDlg:
OnButtonLogin()中的caseIDOK:
下添加如下代码:
(创建套接字,并连接服务器。
连接成功后创建登录数据包并发送至服务器)
caseIDOK:
m_pMySocket=newCMySocket(this);
deletem_pMySocket;
m_pMySocket=NULL;
return;
//连接成功后发送登录数据包至服务器
Packetpacket;
memset(&
packet,0,sizeof(Packet));
packet.type=LOGIN_REQUEST;
strcpy(packet.username,dlg.m_strUserName);
strcpy(packet.data,dlg.m_strPassword);
m_pMySocket->
Send(&
packet,sizeof(Packet));
break;
3.在CChatClientDlg类中添加voidOnReceive(void)函数,接收服务器发送的数据包,并根据数据包类型(在这里是登录成功数据包)进行相应处理。
Receive(&
switch(packet.type)
caseLOGIN_SUCCESS:
m_listMessage.AddString("
loginsucceed."
4.在CMySocket类中重载OnReceive()函数,并添加如下代码:
(当套接字收到数据包时,MFC框架会调用该函数,而该函数在内部调用对话框的OnReceive()函数对接收的数据包进行相应的处理)
OnReceive(nErrorCode);
OnReceive();
在CMySocket类的实现文件中,包含CChatClientDlg类的头文件,即:
ChatClientDlg.h"
5.在服务器端的CChatServerDlg类的头文件的开头添加如下的结构体,用于保存在线用户的名称和对应的套接字指针,以实现一对一的聊天消息的转发:
typedefstructtagUserInfo
char*username;
CClientSocket*pSocket;
}UserInfo;
在CChatServerDlg类的头文件中,添加前置声明:
1.在服务器端的对话框类CChatServerDlg的头文件中添加如下代码:
2.在服务器端的CChatServerDlg类中添加类型为CPtrList类型的成员变量m_listUser,当用户登录服务器之后,在该链表中添加用户的信息(指向UserInfo结构体类型的指针)。
3.在重载CListenSocket类的OnAccept()函数,并添加如下代码:
(前面已经做了)
4.在CChatServerDlg类中添加voidOnAccept(void)函数,在该函数中服务器接收客户端的连接请求,生成该用户的UserInfo结构信息,并把该信息添加到m_listUser链表中,具体代码如下:
UserInfo*pUserInfo=newUserInfo();
pUserInfo->
pSocket=pSocket;
m_listUser.AddHead(pUserInfo);
5.在CClientSocket类中重载OnReceive()函数,并在该函数中调用CChatServerDlg类的OnReceive()函数,代码如下:
在CClientSocket类的实现文件中,包含CChatServerDlg类的头文件,即:
6.在CChatServerDlg类中添加voidOnReceive(void)成员函数,在该函数中服务器接收客户端发送的数据包并根据数据包的类型进行相应处理(在这里是处理登录请求),其代码如下:
pSocket->
caseLOGIN_REQUEST:
POSITIONpos;
for(pos=m_listUser.GetHeadPosition();
pos!
=NULL;
)
UserInfo*pInfo=(UserInfo*)m_listUser.GetNext(pos);
if(pInfo->
pSocket==pSocket)
{
pInfo->
username=packet.username;
packet.type=LOGIN_SUCCESS;
pSocket->
break;
}