CUserInfo类 主要包含一些必要数据以及Serialize支持
class CUserInfo : public CObject
{
public:
DECLARE_SERIAL(CUserInfo);
CUserInfo();
virtual ~CUserInfo();
CUserInfo(const CUserInfo& userInfo);
enum USERSTATUE
{
ONLINE, //在线
OFFLINE, //离线
LOGIN, //登录
UNKNOWN //未知
};
public:
void Init();
virtual void Serialize(CArchive& ar);
CUserInfo &operator = (const CUserInfo &userInfo);
public:
CString m_strName; //昵称
CString m_strPassword; //密码
USERSTATUE m_eStatus; //状态
DWORD m_dwIP; //地址
USHORT m_nPort; //端口
CTime m_time; //时间
};
通信数据包类CChatPacket
class CChatPacket : public CObject
{
public:
DECLARE_SERIAL(CChatPacket);
CChatPacket();
virtual ~CChatPacket();
enum PACKETTYPE
{
MESSAGE, //用户发送给服务器保存的离线消息
USERLIST, //服务器发送给用户的用户列表
SERVERMSG, //服务器发送给用户的普通消息
UNKNOWN //未知
};
public:
virtual void Serialize(CArchive& ar);
void Init(); //实现一些必要的数据的初始化
public:
//包成员变量 括号里说明某些变量的单边可见性
CUserInfo m_userInfo; //发送该包的用户信息(User->Server)
PACKETTYPE m_type; //包类型
CString m_strMsg; //消息
CObList* m_pUserList; //用户列表 (Server->User)
CUserInfo m_offlineUserInfo; //离线用户信息 (User->Server)
CTime m_sendTime; //发送时间
};
监听套接字CListenSocket类
该类派生自CSocket,用于监听客户端连接请求,并且交由服务器主界面类处理(因为主界面类维护并处理整个客户端链表)。并且服务器界面类创建并监听
m_pServerDlg = pServer;
}
CListenSocket在整个服务器中是唯一的,在服务器创建后它仅重载OnAccept函数,该函数中唯一做的事情就是调用服务器界面线程(CServerDlg)对应函数来完成处理,因此自然CListenSocket在构造时需要传入一个CServerDlg的指针,并且保存,以便调用CServerDlg对应函数
CListenSocket::CListenSocket(CServerDlg* pServer)
{
m_pServerDlg = pServer;
}
void CListenSocket::OnAccept(int nErrorCode)
{
m_pServerDlg->ProcessPendingAccept();}
客户端套接字类CClientSocket
该类也派生自CSocket,负责和客户端进行简单的数据传输,并且接受客户端数据可读通知。它重载了CSocket的虚函数OnClose OnReceive 当有可读数据时,OnReceive触发,OnReceive中将接受数据的操作交由CServerDlg类进行处理,当有客户端连接断开时,OnClose触发,此时调用CServerDlg的相应函数将该用户移出在线列表
另外,该类还包含一些对该客户端的用户信息的保存和读取的操作,并且可以向客户端发送数据,用以服务器主类CServerDlg调用。
CClientSocket::CClientSocket(CServerDlg* pServer)
{
m_pServer = pServer;
m_pSocketFile = NULL;
}
void CClientSocket::Init()
{
m_pSocketFile = new CSocketFile(this);
}
void CClientSocket::OnReceive(int nErrorCode)
{
//创建文档对象 以便服务器从此读取数据包
CArchive* pArchiveIn = new CArchive(m_pSocketFile, CArchive::load);
//如果服务器处理未通过 则删除自身
int ret = m_pServer->ProcessPendingReceive(this, pArchiveIn);
delete pArchiveIn;
if (!ret)
{
delete this;
}
}
void CClientSocket::OnClose(int nErrorCode)
{
//停止发送
ShutDown(1);
//移除在线列表
m_pServer->RemoveChatter(this);
//删除自身
delete this;
}
void CClientSocket::SendUserMsg(CChatPacket &chatpacket)
{
CArchive* pArchiveOut = new CArchive(m_pSocketFile, CArchive::store);
//发送消息
chatpacket.Serialize(*pArchiveOut);
pArchiveOut->Flush();
delete pArchiveOut;
pArchiveOut = NULL;
}
void CClientSocket::SendUserList(CChatPacket* pUserListPacket)
{
CArchive* pArchiveOut = new CArchive(m_pSocketFile, CArchive::store);
pUserListPacket->Serialize(*pArchiveOut);
pArchiveOut->Flush();
delete pArchiveOut;
pArchiveOut = NULL;
}
CClientSocket::~CClientSocket()
{
if (m_pSocketFile)
delete m_pSocketFile;
}
接下来就是服务器的主类CServerDlg
服务器几乎所有的操作和处理都在该类完成。
主要实现:
开启服务器:
创建并开启监听套接字
从文件加载数据:
加载用户信息列表以及离线消息列表
初始化服务器界面:
根据用户信息初始化服务器用户信息列表
当有新的连接请求时(由CListenSocket调用):
创建客户端套接字类,接受请求
当有可读数据(由CClienetSocket调用):
读入数据包,根据数据包类型进行相应处理
如果是离线消息 替用户保存
如果是请求用户列表,则:
1.验证该用户的合法性
2.如果验证通过 将该用户加入到在线用户列表(如果该用户是新用户,还需要更新用户信息列表)
3.向所有在线用户发送新的用户列表(遍历m_ChatterList,依次调用CClientSocket的SendUserList)
4.检查离线消息列表,查看有无该用户的离线消息,并且转发
5.更新服务器界面
当有消息向用户发送:
调用CClientSocket响应函数发送数据包
当用户在用户列表项上单击右键:
弹出移除用户菜单 将用户移除列表 更新服务器显示
当有用户下线时(CClientSocket调用)
将该用户移除在线用户列表 更新服务器显示
当服务器退出时
保存m_UserList m_OfflineMsgList数据到文件
释放m_ChatterList m_UserList m_OfflineMsgList
释放监听套接字以及其他资源
#include "ClientSocket.h"
#include "UserInfo.h"
class CListenSocket;
class CClientSocket;
class CUserInfo;
class CServerDlg : public CDialog
{
// Construction
public:
void DeleteAllChatters(); //释放在线用户列表
void SaveUserList(); //保存用户信息到文件并且释放用户信息链表
void SaveOfflineMsg(); //保存离线消息到文件并且释放离线消息链表
void RemoveChatter(CClientSocket* pClient);//将pClient用户从在线用户列表中移除
void SendUserListToAllChatter();//向所有在线用户发送用户列表
BOOL ProcessPendingReceive(CClientSocket* pClient, CArchive* pArchiveIn);//接收并处理数据包
void ProcessPendingAccept();//处理用户连接请求
void LoadOfflineMsg(CObList& obList);//从文件加载离线消息链表
void LoadUserList(CObList& obList); //从文件加载用户信息链表
void InitUserList(); //将用户信息链表填充到服务器界面
void InitListCtrl(); //初始化设置用户信息列表控件
BOOL StartService(); //创建监听套接字开启监听
BOOL InitServer(); //初始化服务器界面
CServerDlg(CWnd* pParent = NULL); // standard constructor
~CServerDlg();
// Dialog Data
//{{AFX_DATA(CServerDlg)
enum { IDD = IDD_SERVER_DIALOG };
CListCtrl m_ctlUserList;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CServerDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
protected:
void UpdateServerListCtrl(); //更新服务器显示
void TransmitMsg(CClientSocket* pClient); //转发离线消息
void DeleteTempUserList(CObList& obList); //删除临时列表
void CopyUserList(CObList& obList); //复制用户列表(避开复制密码)
//验证用户合法性 并在必要时(用户是新用户),更新m_UserList
BOOL VerifyUserAndUpdateUserList(CUserInfo& userInfo, CClientSocket* pClient);
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CServerDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnDestroy();
afx_msg void OnRclickUserList(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnMenuDeleteUser();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
CObList m_OfflineMsgList; //离线消息链表 保存CChatPacket类型
CObList m_ChatterList; //在线用户列表 保存CClientSocket类型
CObList m_UserList; //用户信息列表 保存CUserInfo类型
CImageList* m_pImageList; //用户头像图片
CListenSocket* m_pListenSocket; //监听套接字
};
具体实现参见源代码
完整源代码下载地址
点击这里
:
http://download.csdn.net/detail/wudaijun/4911762