主要包含类:
CUserInfo 和 CChatPacket 和服务器定义的一致
CChatSocket用于和服务器或其他用户交互数据(类里面内置两个指针,分别指向CClientDlg和CChatDlg 以有效指针来辨别和谁通信)
重写OnReceive和OnClose 均调用对应对话框函数处理
CListenSocket用于监听其他用户的连接请求 在OnAccept和OnClose中调用CClientDlg的函数处理
CLogInDlg用与用户输入登录信息

COffMsgDlg 用于显示离线消息

用户点击回复时 调用CClientDlg::CreateChatDlg 该函数根据用户状态创建不同的聊天对话框
CClientDlg和CChatDlg即为核心实现类
- CClientDlg

在登录成功后
与服务器交互
/*
* 读取并且处理服务器发来的数据包
*/
void CClientDlg::ReadAndProcessPacket()
{
CChatPacket packet;
//临时链表用于保存读取的用户链表信息
CObList obList;
packet.m_pUserlist = &obList;
packet.Serialize(*m_pArchiveIn);
switch(packet.m_type)
{
//收到服务器信息 显示然后退出
case CChatPacket::SERVERMSG:
MessageBox(packet.m_strMsg);
EndDialog(IDCANCEL);
break;
//收到用户链表 更新保存的链表并显示
case CChatPacket::USERLIST:
//更新链表
UpdateUserList(obList);
//刷新显示
UpdateUserListCtrl();
//将自身状态从登录改为在线
m_userinfo.m_eStatue = CUserInfo::ONLINE;
break;
//收到转发的离线消息 显示
case CChatPacket::MESSAGE:
ShowOfflineMsg(packet);
break;
default:
break;
}
}
与用户交互
1.用户双击请求聊天:
/*
* 双击用户列表的某一用户时 触发
*/
void CClientDlg::OnDblclkListUser(NMHDR* pNMHDR, LRESULT* pResult)
{
// TODO: Add your control notification handler code here
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1)
{
int nSelected = pNMListView->iItem;
CString strPeerName = m_ctlUserList.GetItemText(nSelected, 0);
//如果不是和自己聊天
if(strPeerName != m_userinfo.m_strName)
CreateChatDlg(strPeerName);
}
*pResult = 0;
}
/*
* 负责根据用户状态的不同建立不同的聊天对话框
*/
void CClientDlg::CreateChatDlg(const CString &strPeerName)
{
//从用户链表中找到该用户
POSITION pos = NULL;
BOOL bFind = FALSE;
CUserInfo peeruserinfo;
for (pos=m_userlist.GetHeadPosition(); pos!=NULL;)
{
//找到该用户
peeruserinfo = *(CUserInfo*)m_userlist.GetNext(pos);
if (peeruserinfo.m_strName == strPeerName)
{
bFind = TRUE;
break;
}
}
if (bFind)
{
CChatDlg chatdlg(this, &m_userinfo, &peeruserinfo);
if (peeruserinfo.m_eStatue == CUserInfo::ONLINE)
{ //用户在线 则连接用户弹出对话框 并保存聊天记录
//设置在线聊天模式
chatdlg.SetChatMode(FALSE);
if(chatdlg.ConnectToPeer())
{
chatdlg.DoModal();
//保存聊天记录
SaveChatRecord(&chatdlg);
}
}
else if (peeruserinfo.m_eStatue == CUserInfo::OFFLINE)
{ //如果发送的是离线消息 则由主窗口代为发送到服务器
//避免聊天窗口对服务器的再次连接
//设置离线聊天模式
chatdlg.SetChatMode(TRUE);
chatdlg.DoModal();
//打包离线消息
CChatPacket packoffmsg;
packoffmsg.m_type = CChatPacket::MESSAGE;
packoffmsg.m_strMsg = chatdlg.GetChatRecord();
packoffmsg.m_userinfo = m_userinfo;
packoffmsg.m_peerUserinfo = peeruserinfo;
packoffmsg.m_sendtime = CTime::GetCurrentTime();
//发送数据包
SendPacket(packoffmsg);
//保存聊天记录
SaveChatRecord(&chatdlg);
}
}
}
2.用户被动聊天,即响应CListenSocket::OnAccept()
/*
* 当其他用户请求聊天时 调用此函数接受请求并创建聊天对话框
*/
BOOL CClientDlg::ProcessPendingAccept()
{
//得到与一个聊天对话框相关联的一个CChatSocket
CChatDlg chatdlg(this);
chatdlg.InitChatSocket();
CChatSocket* pNewSocket = chatdlg.GetChatSocket();
//用该CChatSocket接收连接
SOCKADDR_IN addrPeer;
int len = sizeof(SOCKADDR_IN);
int ret = m_pListenSocket->Accept(*pNewSocket, (SOCKADDR*)&addrPeer, &len);
//找到请求连接的用户的信息
POSITION pos = NULL;
CUserInfo* pUserInfo = NULL;
BOOL bFind = FALSE;
for (pos = m_userlist.GetHeadPosition(); pos!=NULL;)
{
pUserInfo = (CUserInfo*)m_userlist.GetNext(pos);
if (pUserInfo->m_dwIP == addrPeer.sin_addr.S_un.S_addr
&& pUserInfo->m_eStatue == CUserInfo::ONLINE)
{
bFind = TRUE;
break;
}
}
//找到了则设置对话框对应的数据 显示聊天对话框
if (bFind)
{
chatdlg.SetUserInfo(&m_userinfo);
chatdlg.SetAimUserInfo(pUserInfo);
chatdlg.InitArchive();
chatdlg.SetChatMode(FALSE);
chatdlg.DoModal();
SaveChatRecord(&chatdlg);
}
return TRUE;
}
//其他主要函数
void ProcessServerClose(); //处理服务器退出或断开连接
BOOL ProcessPendingAccept(); //处理新的用户聊天请求
void SaveChatRecord(CChatDlg* pChatDlg); //保存聊天记录
void CreateChatDlg(const CString& strPeerName); //根据用户状态创建聊天对话框
void ShowOfflineMsg(const CChatPacket& packet); //显示离线消息
void UpdateUserListCtrl(); //更新用户列表显示
void UpdateUserList(CObList& oblist); //收到服务器的用户列表时 更新
void ReadAndProcessPacket(); //读取并处理服务器发来的数据包
void ProcessPendingRead(); //调用ReadAndProcessPacket处理数据
BOOL BeginListen(); //创建并开启监听套接字
void SendPacket(CChatPacket& packet); //向服务器发送数据包
BOOL ConnectToServer(); //连接到服务器
void InitListCtrlSetting(); //初始化用户列表控件设置
BOOL Init(); //客户端初始化
//主要数据成员
CObList m_userlist; //用户列表
SOCKADDR_IN m_addrHost; //主机地址(端口为监听套接字端口)
CArchive* m_pArchiveOut; //用于向服务器发送数据的文档对象
CUserInfo m_userinfo; //本用户的信息
CArchive* m_pArchiveIn; //用于接收服务器发送的数据的文档对象
CSocketFile* m_pSocketFile; //与服务器通信用的CSocketFile
CChatSocket* m_pChatSocket; //与服务器聊天的CChatSocket
CImageList* m_pImageList; //用户头像
CListenSocket* m_pListenSocket; //监听套接字
- CChatDlg:
CChatDlg有两种初始化方式,默认构造和直接构造CChatDlg(CWnd* pParent, CUserInfo* puser, CUserInfo* paimuser)
前者用于被动聊天 因为需要先构造CChatDlg用其成员变量m_pChatSocket来作为参数接收连接请求 之后才能得到对方用户信息,可通过SetPeerUserInfo来设置
后者用于主动聊天,此时对方用户信息可以在CClientDlg中得到,因此直接构造。
当聊天为在线聊天时,还需要调用InitArchive获得CSocket序列化支持


CChatDlg主要函数和数据:
// CChatDlg主要函数:
public:
void ProcessPeerClose(); //当对方关闭聊天对话框时调用
CChatSocket* GetChatSocket(); //得到聊天套接字
void InitChatSocket(); //创建聊天套接字
void SetAimUserInfo(const CUserInfo* puserinfo);//保存对方信息
void SetUserInfo(const CUserInfo* puserinfo); //保存本用户信息
void InitArchive(); //初始化必需的CSocketFile和CArchive对象
void SendPacket(CChatPacket* pPacket); //向对方发送数据包
void DisplaySentMsg(CString strInput); //在聊天记录框中显示自己发送的信息
void DisplayRecvMsg(const CChatPacket& recvpacket);//在聊天记录框中显示接收的信息
~CChatDlg();
void ProcessPendingRead(); //当聊天套接字可读时,通过该函数处理
CString GetChatRecord(); //得到聊天记录
void SetChatMode(BOOL bOfflineChat); //设置聊天模式(True:离线留言 False 在线聊天)
bool ConnectToPeer(); //连接到对方
CChatDlg(CWnd* pParent, CUserInfo* puser, CUserInfo* paimuser);//主动聊天的构造函数
// 主要数据成员
CString m_strChatRecord; //聊天记录
CArchive* m_pArchiveOut; //发送数据的文档对象
CArchive* m_pArchiveIn; //接收数据的文档对象
CSocketFile* m_pSocketFile; //聊天套接字关联的CSocketFIle文件
CChatSocket* m_pChatSocket; //聊天套接字
CUserInfo m_aimuserinfo; //聊天对方的信息
CUserInfo m_userinfo; //本用户的信息
BOOL m_bOfflineChat; //标识聊天类型
主要函数实现
//发送数据:
/*
* 用户点击发送按钮 发送信息 并且更新聊天对话框显示
*/
void CChatDlg::OnBtnSend()
{
// TODO: Add your control notification handler code here
//得到输入内容
CString strUserInput;
m_ctrlInput.GetWindowText(strUserInput);
//为空则不发送
if (strUserInput == "")
return;
if(m_bOfflineChat)
//离线留言 只需要负责界面显示 由主对话框负责发送给服务器
{
//清空输入框
m_ctrlInput.SetWindowText("");
//更新聊天框显示
DisplaySentMsg(strUserInput);
}
else //在线聊天
{
//打包数据
CChatPacket msgpacket;
msgpacket.m_type = CChatPacket::MESSAGE;
msgpacket.m_userinfo = m_userinfo;
msgpacket.m_peerUserinfo = m_aimuserinfo;
msgpacket.m_strMsg = strUserInput;
msgpacket.m_sendtime = CTime::GetCurrentTime();
//发送数据
SendPacket(&msgpacket);
//清空输入框
m_ctrlInput.SetWindowText("");
//更新聊天框显示
DisplaySentMsg(strUserInput);
}
}
//接收数据 由CChatSocket::OnReceive调用
void CChatDlg::ProcessPendingRead()
{
do
{
CChatPacket packet;
packet.Serialize(*m_pArchiveIn);
if (packet.m_type == CChatPacket::MESSAGE)
{
DisplayRecvMsg(packet);
}
}while(!m_pArchiveIn->IsBufferEmpty());
}
完整源代码下载地址 点击这里 : http://download.youkuaiyun.com/detail/wudaijun/4911762