目的:实现类似QQ群聊的聊天室,可以看到好友列表及互相传送信息。
分析:可基于C/S结构实现即时通讯
1、创建基于对话框的MFC程序(支持windows套接字),并增加相应的类与结构体,完善对话框界面。
服务器端:新增两个基于CSocket的类CClientSocket/CServerSocket分别用于服务与监听;并新增一个结构体信息,用于对客户端传输的信息(消息类型与长度)进行甄别。
客户端:新增基于CSocket的类CClientSocket用于服务;并新增一个结构体信息,用于对客户端传输的信息(消息类型与长度)进行甄别。
结构体信息,用于标识传输的内容类型
1 typedef struct tagHeader 2 { 3 char type; 4 int len; 5 }Header, *pHeader; 6 #define LOGIN_IO 1 //后面可以继续完善,比如视频/音频/文件 7 #define SEND_MESSAGE 3 8 ;
2、完善框架结构
服务器:在Clientsocket.h声明Dlg类,便于进行指针操作,并标识登录的用户昵称,创建CPtrList链队列数据结构。
1 class CCase006Dlg; 2 class CClientSocket : public CSocket 3 { 4 public: 5 CString m_strName; //请求登录的用户 6 CPtrList *clist; //储存队列的结构 7 CCase006Dlg* m_dlgserver; //对话框指针机制 8 CClientSocket(CPtrList*list); //生成一个socket队列 9 10 CCase006Dlg* m_Dlg; 11 void GetDlg(CCase011Dlg *dlg);
12 }
在Clientsocket.cpp中包含结构体头文件,并实现指针函数
1 #include "tagHeader.h" 2 3 // CClientSocket 4 CClientSocket::CClientSocket(CPtrList *list) : m_dlgserver(NULL) 5 { 6 clist=list; 7 }
在CSeverSocket.h中定义列表对象-CPtrList connectList;
在CSeverSocket.cpp中添加结构体头文件;
在CXXXXDlg.h中声明套接字类,并定义指针对象与用户列表更新函数
1 class CServerSocket; //监听套接字类
2 class CClientSocket; //创建与客户端通信的套接字类 3
// CCase006Dlg 对话框 4 class CCase006Dlg : public CDialog 5 { 6 // 构造 7 public: 8 CCase006Dlg(CWnd* pParent = NULL); // 标准构造函数 9 CString m_str_name; //用于标识管理员名 10 CClientSocket* clientsocket; //定义套接字指针 11 CServerSocket* m_serversocket; 12 void UpdateUser(CClientSocket* psocket); //声明用户更新函数
在CXXXXDlg.cpp中包含头文件,并在初始化函数中初始化Socket指针对象
1 #include "ServerSocket.h" 2 #include "ClientSocket.h" 3 #include "tagHeader.h" 4 5 BOOL CCase006Dlg::OnInitDialog() 6 { 7 serversocket=NULL; 8 clientsocket =NULL; 9 ...... 10 }
客户端:在 Clientsocket.h中定义登录用户名,并定义一个窗口指针及函数
1 class CCase005Dlg; //注意声明类
2 class CClientSocket : public CSocket 3 { 4 public: 5 CCase005Dlg *m_Dlg; 6 CString m_str_name; //登录用户 7 void GetDlg(CCase005Dlg *dlg); 8 };
在ClientSocket.cpp中声明结构体,并实现窗口指针获取函数
1 #include "Case005Dlg.h" 2 #include "tagHeader.h" 3 4 // CClientSocket 成员函数 5 void CClientSocket::GetDlg(CCase005Dlg *dlg) 6 { 7 m_dlg=dlg; 8 }
在CXXXXDlg.h中声明套接字类,并定义指针对象与用户列表更新函数以及是否获取信息函数
1 #include "ClientSocket.h" 2 3 class CCase005Dlg : public CDialog 4 { 5 // 构造 6 public: 7 CClientSocket *m_clientsocket; 8 void Updateuser(); 9 BOOL GetmsgFromRoom(); 10 ...... 11 }
在CXXXXDlg.cpp中包含头文件,并在初始化函数中初始化Socket指针对象
1 #include "ClientSocket.h" 2 #include "tagHeader.h"
此时已完成基本框架体系结构,接下来实现通信相关函数。
3、实现界面事件函数
服务器端:单击" 开始" 按钮开始监听
1 void CCase011Dlg::OnBnClickedBnOpen() 2 { 3 // TODO: 在此添加控件通知处理程序代码 4 UpdateData(); 5 serversocket=new CServerSocket; //开启服务,定义一个服务对象 6 7 BYTE nfield[4]; 8 CString strIP; 9 UpdateData(); //需要更新 10 11 //将IP传给地址框 12 m_edit_ip.GetAddress(nfield[0],nfield[1],nfield[2],nfield[3]); 13 strIP.Format("%d.%d.%d.%d",nfield[0],nfield[1],nfield[2],nfield[3]); 14 CTime time=CTime::GetCurrentTime(); 15 16 if(m_str_name.IsEmpty()) //对输入信息判定,,可简写 17 { 18 AfxMessageBox("请先登记管理员名!"); 19 return ; 20 } 21 if(m_edit_ip.IsBlank()) 22 { 23 AfxMessageBox("请配置聊天室IP"); 24 return ; 25 } 26 if(m_str_port.IsEmpty()) 27 { 28 AfxMessageBox("请配置要开放的端口"); 29 return ; 30 } 31 32 //开启聊天室,首先创建用于监听的套接字 33 if(serversocket->Create(atoi(m_int_port),1,strIP)) 34 { 35 m_editbox.SetWindowText(""); 36 m_editbox.ReplaceSel("聊天室开启成功!\r\n"); //此处使用的是编辑框控件,所以需使用replaceSel
CString temp=time.Format("%Y-%m-%d"); //增加时间排头 37 m_editbox.ReplaceSel("日期:"+temp+"\r\n"); //编辑框输出信息 38 temp=time.Format("%H:%M:%S"); 39 theApp.m_str_name=m_str_name; //如果想要实现聊天界面标题为登录者 40 m_editbox.ReplaceSel(temp+" 管理员"+theApp.m_str_name+"开放聊天室\r\n"); 41 } 42 if(serversocket->Listen()) //打开监听 43 { 44 m_editbox.ReplaceSel("等待成员加入。。。\r\n"); 45 } 46 //目前无人,因此仅有管理员一人在线 47 m_listbox.ResetContent(); 48 m_listbox.AddString(theApp.m_str_name+"(管理员)"); 49 this->SetWindowTextA("管理员:"+m_str_name+"服务器端"); 50 }
更新用户列表处理函数
void CCase011Dlg::UpdateUser(CClientSocket* psocket) { m_listbox.ResetContent(); //清空列表 m_listbox.AddString(theApp.m_strName+"(管理员)"); //添加管理员用户 CString userInfo; userInfo = theApp.m_str_ame+"(管理员)"+"&"; //添加管理员信息 if(clientsocket!=NULL) { CClientSocket* psock=NULL; POSITION pos=clientsocket->clist->GetHeadPosition(); //获取列表头位置 while(pos!=NULL) { psock=(CClientSocket*)clientsocket->clist->GetNext(pos); m_listbox.AddString(psock->m_str_name); userInfo+=(psock->m_strName+"&"); //逐个增加已经记录的用户名 } Header head; head.type=LOGIN_IO; head.len=userInfo.GetLength(); POSITION po=clientsocket->clist->GetHeadPosition(); while(po!=NULL) //将最新的用户列表转发给各个用户 { psock=(CClientSocket*)clientsocket->clist->GetNext(po); psock->Send((char*)&head,sizeof(Header)); psock->Send((LPCTSTR)userInfo,userInfo.GetLength()); } } }