========================
操作說明:
先打開 sever.exe
{width="3.103778433945757in" height="1.5518897637795275in"}
設定好IP與port,點擊OK
再打開client.exe
輸入地址與端口,再輸入用戶名與密碼(密碼只能是數字)點登陸
如果沒有賬號,則一樣直接輸入,會直接新建一個賬號并登陸。
{width="2.5517639982502187in" height="3.0100404636920386in"}
進入聊天名單
{width="2.2601345144356957in" height="3.957839020122485in"}
點擊一個使用者
輸入消息聊天
{width="5.2493438320209975in" height="3.812023184601925in"}
聊天消息會儲存在chatlog.txt.
程式說明
Sever
Class"
CServerDig:建立伺服器主視窗,管理使用者資訊鏈錶,管理線上使用者鏈錶,管理離線訊息鏈錶,讀取儲存使用者資訊,讀取儲存離線訊息。
class CServerDlg : public CDialog
{
// Construction
public:
CServerDlg(CWnd* pParent = NULL); //构造函数
virtual ~CServerDlg(); //析构函数
// Dialog Data
//{{AFX_DATA(CServerDlg)
enum { IDD = IDD_SERVER_MAIN_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
// Implementation
public:
BOOL Init(void); //初始化
BOOL StartService(void); //启动服务
BOOL ProcessPendingRead(CArchive *pArchiveIn,
CArchive *pArchiveOut,CClientSocket *pClientSocket);//接收数据
void ProcessPendingAccept(void); //接受客户端连接请求
void ReleaseChatter(CClientSocket *pClientSocket); //当用户离线时删除用户
void DeleteChatter(CClientSocket *pClientSocket); //用户登录密码错误或者重复登录时删除用户
void DeleteAllChatter(void); //删除所有在线用户
protected:
void InitListCtrlSetting(void); //定义列表控件
void InitUserList(void); //初始化用户列表
void SaveUserList(CObList &obList); //保存用户信息
void LoadUserList(CObList &obList); //读取用户信息
void SaveOfflineMsg(CObList &obList); //保存离线消息
void LoadOfflineMsg(CObList &obList); //读取离线消息
void SendUserList(void); //发送用户列表
void CopyUserList(CObList &obList); //复制用链表
void DeleteTempUserList(CObList &obList); //删除临时链表
BOOL UpdateUserList(const CUserInfo &userInfo, CClientSocket *pClientSocket);//更新用户链表
void TransmitMsg(const CChatPacket &packet, CClientSocket *pClientSocket); //转发离线消息
void UpdateServerListCtl(const CObList &obList); //更新服务器界面
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CServerDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnDestroy();
afx_msg void OnRclickClientList(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnServerListDeleteMenuitem();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
CListenSocket *m_pListenSocket; //CListenSocket指针
CImageList *m_pImageList; //图标列表指针
CObList m_UserList; //用户链表
CObList m_OfflineMsgList; //离线信息链表
CObList m_ChatterList; //在线用户链表
CCriticalSection m_csUserList; //保护用户链表临界区对象
CCriticalSection m_csOfflineList;//保护离线信息链表临界区对象
CCriticalSection m_csChatterList;//保护在线用户链表临界区对象
};
CListenSocket:繼承CSocket,監聽客戶端連線請求。
CClistenSocket:繼承Csocket接收客戶端資料,向客戶端傳送資料
CSeverAddressDig:登錄的對話方塊,設置伺服器的IP和PORT
class CServerAddressDlg : public CDialog
{
// Construction
public:
CServerAddressDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CServerAddressDlg)
enum { IDD = IDD_SERVER_ADDRESS_DLG };
CIPAddressCtrl m_ctlServIP;
CEdit m_ctlServPort;
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CServerAddressDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CServerAddressDlg)
virtual void OnOK();
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
public:
DWORD m_strServIP; //服务器IP地址
SHORT m_shServPort; //服务器端口
};
CChatPacket:資料封包類別
class CChatPacket : public CObject
{
public:
CChatPacket();
virtual ~CChatPacket();
DECLARE_SERIAL( CChatPacket )
enum PACKETTYPE //包的类型
{
MESSAGE, //聊天消息
USERLIST, //用户列表
SERVERMSG, //服务器发送给用户的消息
UNKNOWN //未知类型
};
public:
void Init(void); //初始化成员变量
virtual void Serialize(CArchive& ar); //序列化
public:
PACKETTYPE m_type; //包类型
CUserInfo m_UserInfo; //用户信息
CString m_strMsg; //消息
CObList *m_pUserList; //用户链表
CUserInfo m_OfflineUserInfo; //离线用户信息
CTime m_time; //日期和时间
};
CUserInfo:使用者資訊類別。
class CUserInfo : public CObject
{
public:
CUserInfo();
CUserInfo(const CUserInfo& userInfo);
virtual ~CUserInfo();
DECLARE_SERIAL(CUserInfo)
enum USERSTATUE{
ONLINE, //在线
OFFLINE, //离线
LOGIN, //登录
UNKNOWN //未知
};
public:
void Init(void); //初始化
virtual void Serialize(CArchive& ar); //序列化
CUserInfo &operator = (const CUserInfo &userInfo); //赋值函数
public:
CString m_strName; //名称
CString m_strPassword; //密码
USERSTATUE m_eStatus; //状态
DWORD m_lIP; //IP地址
SHORT m_nPort; //端口
CTime m_time; //日期和时间
};
CseverApp:應用程序實體類別
class CServerApp : public CWinApp
{
public:
CServerApp();
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CServerApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CServerApp)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
在CSeverApp類別的InitInstance()中,宣告一個CSeverDig的實體,然後呼叫StartSever()
如果成功,則呼叫CDialog中的DoModal()的涵式建立伺服器主界面。如果失敗,則退出。
在該涵式中宣告CSeverAddressDig 實體,建立伺服器位址和port對話方塊,當按下OK,則呼叫該類別中的OnOk()涵式。
在startSever()中,首先建立一個CListenSocket,然後呼叫CSocket()類別中的Creat()完成Socket建立和綁定。呼叫create()的時候,建立一個不顯示的視窗,并呼叫WSAAsyncSelect()涵式註冊網路事件。在程式中使用預設參數呼叫Create()涵式,建立串流式Socket,註冊全部網路事件。
在severDig中的init()中,initUserList涵式實作初始化使用者列表的功能
首先呼叫LoadUserList()涵式從"userlist.ini"檔案中讀取使用者資訊。
在initUserList中使用COblist類別GetHeaderPosition()取得節點頭,然後不斷呼叫CObList類別中的GetNext()取得下一個節點。當走訪到最後則結束。
InserItem()插入資料項目,設置使用者為離線狀態,然後呼叫CListCtrl類別的SetItemText()為每個資料項目插入子項目。
LoadOfflineMsg()涵式從"offlinemesglist.ini"中讀取使用者離線訊息
在CListenSocket中的OnAceept()中,呼叫CSeverDig類別ProcessPendingAccept()涵式接受客戶端的連線請求。其中呼叫了CSocket的PumpMessages()用於阻塞,CClientSocket類別的init(),建立一個CSocketFile物件,為CArchive物件接受和傳送資料做準備。
CsocketWind中的OnSocketNotify()涵式被呼叫時,會首先呼叫CSocket類別的AuxQueueAdd(),將網路事件訊息加入Socket執行狀態變數的鏈錶。然後呼叫CSocket的ProcessAuxQueue(),將網路事件訊息取出來。
在CClientSocket的OnReceive中,建立讀取寫入檔案物件,然後呼叫CSeverDig的ProcessPendingRead(),如果傳送資料的使用者密碼錯誤或者重新登錄,則刪除使用者。
DoCallBack()呼叫衍生類別的OnrReceive()的虛擬涵式。OnReceive呼叫CSeverDig類別ProcessPendingRead(),最終呼叫到CChatPacket類別Serilalize()。Serialize中的檔案物件呼叫CSockeFile類別的Read(),最終呼叫CSocket類別的Receive()接受客戶端資料。
在CSeverDig類別中的ProcessPendingRead()中,對接收的資料做如下處理:儲存離線訊息,儲存使用者資訊,更行使用者列表,傳送使用者列表,轉發離線訊息和更新伺服器界面。
CSeverDig 類別的UpdateUserList()涵式作為對伺服器使用者的列表的更新。
使用者成功登陸伺服器,伺服器向所有的線上使用者傳送使用者;列表,通知他們更新使用者列表。
SendUserList()涵式實作向線上使用者傳送使用者列表的功能。
TransmitMsg()涵式轉送離線訊息。
SendUserMsg()將離線訊息傳送給使用者。
UppdateSeverListCtl()實作伺服器界面的更新。
OnDestory()呼叫SaveUserList()儲存使用者資訊,SaveOfficeMsg涵式儲存離線資訊,DeleteAllChatter刪除線上使用者。并刪除CListenSocket和CImageList物件。
SaveUserList()實作儲存使用者資訊的功能。伺服器啟動時,在InitUserList中呼叫LoadUserList().
SaveOfflineMsg()涵式實作儲存離線訊息的功能。
Client
CClistenDig:建立客戶端主視窗,建立聊天視窗,接受和傳送離線訊息。
CChatDig:編輯和顯示信息。
CListenSocket:監聽其他客戶連線請求,
CChatSocket:接受資料封包。
CCListenLoginDig:登錄伺服器對話方塊。
CChatPacket:資料封包的CLASS。
CUserInfo:使用者資訊。
CClistenApp:應用程式class
ConnectToSeve()連線伺服器,請求使用者列表,初始化客戶端。CreateChatDig()用來建立聊天視窗。
BeginingListen用於監聽其他客戶連線請求。
ConnectToPeer()用於向另外一個使用者發起聊天請求。
首先InitListenCtrlSetting實作初始化列表控制項的功能。
然後BegingListen建立Socket,呼叫Listen()開始監聽。在呼叫GetSocketName()取得port.
連接伺服器之前先建立CChatSocket物件,再建立登錄對話方塊,取得伺服器位置,port,name和password。按下登陸后呼叫Connect()連接伺服器。
連接成功后,呼叫GetSoketName()涵式取得IP位址,然後定義資料封包,再呼叫sendPacket()涵式講資料包傳送給伺服器。
CChatSocket的OnReceive()涵式被呼叫,在該涵式中呼叫CClistenDig類比的ProcessPendingRead()涵式,其中呼叫ReadPacket(),接受伺服器資料。當接受資料是離線訊息時,呼叫ShowOfflineMsg()涵式顯示訊息,當接受使用者列表時,呼叫CreateUserList()建立使用者列表,然後UpdateClistenList()。如果登錄失敗,則呼叫EndDialog退出。
ShowOfflineMsg()呼叫時,會呼叫CChatDig建構離線訊息視窗。
DisplayOfflineMessage()實作離線訊息的功能。
CClistenDig類別的OnDblclkClientList()涵式被回應。在該涵式內呼叫CListCtrl類比的GetItemTest()涵式取得選中資料項目的名稱,呼叫CreateChatDig()涵式建立聊天視窗,并判斷使用者身份。
CChatDig中的ConnecgtToPeer涵式建立CChatSocket物件,并發出連線請求。當傳送訊息后,PerTranslateMessage()涵式中攔截該鍵盤訊息進行處理。
DisplaySentMessage()中利用CTime類別中的Format()格式化日期和時間。
當要接受訊息時,CChatSocket中的OnReceive()被呼叫,該涵式呼叫CChatDig類別的ProcessPendingRead()涵式,并接受訊息,同時呼叫DisplayRecvMessage()顯示訊息。
Sever:
ChatPacket.h
#if !defined(AFX_CHATPACKET_H__7F5E67FB_273B_4679_8A59_23575978DDCC__INCLUDED_)
#define AFX_CHATPACKET_H__7F5E67FB_273B_4679_8A59_23575978DDCC__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "UserInfo.h"
class CUserInfo;
class CChatPacket : public CObject
{
public:
CChatPacket();
virtual ~CChatPacket();
DECLARE_SERIAL( CChatPacket )
enum PACKETTYPE //包的类型
{
MESSAGE, //聊天消息
USERLIST, //用户列表
SERVERMSG, //服务器发送给用户的消息
UNKNOWN //未知类型
};
public:
void Init(void); //初始化成员变量
virtual void Serialize(CArchive& ar); //序列化
public:
PACKETTYPE m_type; //包类型
CUserInfo m_UserInfo; //用户信息
CString m_strMsg; //消息
CObList *m_pUserList; //用户链表
CUserInfo m_OfflineUserInfo; //离线用户信息
CTime m_time; //日期和时间
};
ChatPacket.cpp
#include "stdafx.h"
#include "ChatPacket.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//IMPLEMENT_DYNCREATE(CChatPacket, CObject)
IMPLEMENT_SERIAL(CChatPacket, CObject, 1 )
CChatPacket::CChatPacket()
{
Init();
}
CChatPacket::~CChatPacket()
{
}
/*
* 初始化
*/
void CChatPacket::Init(void)
{
m_type = UNKNOWN; //为知类型
m_strMsg = _T(""); //清空
m_pUserList = NULL; //清空
m_time = CTime::GetCurrentTime();
}
/*
* 序列化
*/
void CChatPacket::Serialize(CArchive& ar)
{
CObject::Serialize(ar);//调用基类的序列化函数
if (ar.IsStoring())//发送数据
{
BYTE byType = m_type;
ar << byType; //包的类型
ar << m_strMsg; //消息
long lTime = m_time.GetTime(); //日期和时间
ar << lTime;
}else//接收数据
{
BYTE byType;
ar >> byType;
m_type = (PACKETTYPE)byType; //包的类型
ar >> m_strMsg; //消息
long lTime;
ar >> lTime; //日期和时间
m_time = CTime((time_t)lTime);
}
m_UserInfo.Serialize(ar); //序列化用户信息
m_OfflineUserInfo.Serialize(ar); //序列化离线用户信息
if (m_type == USERLIST && NULL != m_pUserList)//序列化用户列表
{
m_pUserList->Serialize(ar);
}
}
ClientSocket.cpp
// ClientSocket.cpp : implementation file
//
#include "stdafx.h"
#include "Server.h"
#include "ClientSocket.h"
#include "ServerDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CClientSocket
CClientSocket::CClientSocket(CServerDlg *pServDlg)
{
m_pServDlg = pServDlg;
}
CClientSocket::~CClientSocket()
{
m_pServDlg = NULL;
if (m_pFile != NULL)
delete m_pFile;
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CClientSocket, CSocket)
//{{AFX_MSG_MAP(CClientSocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CClientSocket member functions
/*
* 初始化
*/
void CClientSocket::Init(void)
{
//创建CSocketFile对象
m_pFile = new CSocketFile(this);
}
/*
* 接收数据
*/
void CClientSocket::OnReceive(int nErrorCode)
{
CArchive *pArchiveIn = new CArchive(m_pFile,CArchive::load); //创建读入文档对象
CArchive *pArchiveOut = new CArchive(m_pFile,CArchive::store); //创建写入文档对象
BOOL reVal = m_pServDlg->ProcessPendingRead(pArchiveIn, pArchiveOut, this); //接收数据
delete pArchiveIn; //删除读入文档对象
pArchiveIn = NULL;
delete pArchiveOut; //删除写入文档对象
pArchiveOut = NULL;
if (FALSE == reVal) //用户密码错误,或者重复登录
{
delete this; //删除自身
}
}
/*
* 关闭套接字
*/
void CClientSocket::OnClose(int nErrorCode)
{
//通知用户不再发送数据
ShutDown(sends);
//更新用户状态
m_pServDlg->ReleaseChatter(this);
delete this;
}
/*
* 发送用户列表
*/
void CClientSocket::SendUserList(CChatPacket *pPacket)
{
CArchive *pArchiveOut = new CArchive(m_pFile,CArchive::store); //初始化写入流
pPacket->Serialize(*pArchiveOut);
pArchiveOut->Flush();
delete pArchiveOut;
pArchiveOut = NULL;
}
/*
* 发送消息
*/
void CClientSocket::SendUserMsg(CChatPacket *pPacket)
{
CArchive *pArchiveOut = new CArchive(m_pFile,CArchive::store);//创建写入流
pPacket->Serialize(*pArchiveOut); //发送数据
pArchiveOut->Flush();
delete pArchiveOut; //删除写入流
pArchiveOut = NULL;
}
/*
* 保存用户地址
*/
void CClientSocket::SaveUserInfo(SOCKADDR_IN clientAddr)
{
m_userInfo.m_lIP = clientAddr.sin_addr.S_un.S_addr;
m_userInfo.m_nPort = clientAddr.sin_port;
}
/*
* 保存用户状态信息
*/
void CClientSocket::SaveUserInfo(const CUserInfo &userInfo)
{
m_userInfo.m_eStatus = userInfo.m_eStatus;
m_userInfo.m_strName = userInfo.m_strName;
m_userInfo.m_strPassword = userInfo.m_strPassword;
m_userInfo.m_time = userInfo.m_time;
}
/*
* 保存用户信息
*/
CUserInfo CClientSocket::GetUserInfo(void)
{
return m_userInfo;
}
ListenSocket.cpp
// ListenSocket.cpp : implementation file
//
#include "stdafx.h"
#include "Server.h"
#include "ListenSocket.h"
#include "ServerDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CListenSocket
CListenSocket::CListenSocket(CServerDlg* pServerDlg)
{
m_pServerDlg = pServerDlg;
}
CListenSocket::~CListenSocket()
{
m_pServerDlg = NULL;
// Close();
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CListenSocket, CSocket)
//{{AFX_MSG_MAP(CListenSocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CListenSocket member functions
/*
* 接受客户端的链接请求
*/
void CListenSocket::OnAccept(int nErrorCode)
{
m_pServerDlg->ProcessPendingAccept();
}
Server.CPP
// Server.cpp : Defines the class behaviors for the application.
//
#include "stdafx.h"
#include "Server.h"
#include "ServerDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CServerApp
BEGIN_MESSAGE_MAP(CServerApp, CWinApp)
//{{AFX_MSG_MAP(CServerApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CServerApp construction
CServerApp::CServerApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CServerApp object
CServerApp theApp;
/////////////////////////////////////////////////////////////////////////////
// CServerApp initialization
BOOL CServerApp::InitInstance()
{
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
CServerDlg dlg;
if (!dlg.StartService())
{
AfxMessageBox(_T("启动服务器失败!"),MB_OK, -1);
return FALSE;
}
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
ServerAddressDlg.cpp
// ServerAddressDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Server.h"
#include "ServerAddressDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CServerAddressDlg dialog
CServerAddressDlg::CServerAddressDlg(CWnd* pParent /*=NULL*/)
: CDialog(CServerAddressDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CServerAddressDlg)
//}}AFX_DATA_INIT
}
void CServerAddressDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CServerAddressDlg)
DDX_Control(pDX, IDC_SERVER_IPADDRESS, m_ctlServIP);
DDX_Control(pDX, IDC_SERVER_PORT_EDIT, m_ctlServPort);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CServerAddressDlg, CDialog)
//{{AFX_MSG_MAP(CServerAddressDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CServerAddressDlg message handlers
void CServerAddressDlg::OnOK()
{
//获取服务器IP和端口
m_ctlServIP.GetAddress(m_strServIP);
CString strServPort;
m_ctlServPort.GetWindowText(strServPort);
m_shServPort = atoi(strServPort); //将字符串转换为SHORT
CDialog::OnOK();
}
BOOL CServerAddressDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_ctlServIP.SetAddress(127, 0, 0, 1);
CString strServIP;
strServIP.Format("%d",SERVERPORT);
m_ctlServPort.SetWindowText(strServIP);
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
ServerDlg.cpp
// ServerDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Server.h"
#include "ServerDlg.h"
#include "ClientSocket.h"
#include "ListenSocket.h"
#include "ServerAddressDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CServerDlg dialog
CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CServerDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CServerDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pListenSocket = NULL; //监听套接字
m_pImageList = NULL; //图标列表
}
void CServerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CServerDlg)
DDX_Control(pDX, IDC_CLIENT_LIST, m_ctlUserList);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CServerDlg, CDialog)
//{{AFX_MSG_MAP(CServerDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_DESTROY()
ON_NOTIFY(NM_RCLICK, IDC_CLIENT_LIST, OnRclickClientList)
ON_COMMAND(ID_SERVER_LIST_DELETE_MENUITEM, OnServerListDeleteMenuitem)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CServerDlg message handlers
BOOL CServerDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
//初始化
Init();
**return TRUE; **
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
**void CServerDlg::OnPaint() **
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CServerDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
CServerDlg::~CServerDlg()
{
}
/*
* 接受客户端连接请求
*/
void CServerDlg::ProcessPendingAccept(void)
{
CClientSocket *pClient = new CClientSocket(this);//创建实例
SOCKADDR_IN clientAddr;
int socketLen = sizeof(SOCKADDR_IN);
m_pListenSocket->Accept(*pClient,(SOCKADDR*)&clientAddr, &socketLen);//接受客户端的连接
pClient->Init(); //初始化
pClient->SaveUserInfo(clientAddr); //保存用户的地址
m_csChatterList.Lock();
m_ChatterList.AddTail(pClient); //加入在线用户链表
m_csChatterList.Unlock();
}
/*
* 初始化
*/
BOOL CServerDlg::Init(void)
**{ **
InitListCtrlSetting(); //定义列表控件
InitUserList(); //初始化用户列表
LoadOfflineMsg(m_OfflineMsgList); //读取离线消息
return TRUE;
}
/*
* 启动服务
*/
BOOL CServerDlg::StartService(void)
{
CServerAddressDlg servAddrDlg; //服务器地址和端口对话框
if (IDOK != servAddrDlg.DoModal())
{
return FALSE;
}
//获取服务器地址
in_addr servAdd;
servAdd.S_un.S_addr = htonl(servAddrDlg.m_strServIP); //主机字节转换为网络字节
CString strServIP = inet_ntoa(servAdd); //转换为点格式
//创建CListenSocket对象
**m_pListenSocket = new CListenSocket(this); **
//创建套接字
if (m_pListenSocket->Create(servAddrDlg.m_shServPort, SOCK_STREAM,strServIP))
{
//开始监听
if (!m_pListenSocket->Listen())
{
delete m_pListenSocket;
m_pListenSocket = NULL;
AfxMessageBox(_T("创建套接字失败!"));
**return FALSE; **
}
}else
{
delete m_pListenSocket;
m_pListenSocket = NULL;
AfxMessageBox(_T("创建套接字失败!"));
**return FALSE; **
**} **
return TRUE;
}
/*
* 定义列表控件
*/
void CServerDlg::InitListCtrlSetting(void)
{
m_pImageList = new CImageList();
**ASSERT(m_pImageList != NULL); **
//32*32,8位,初始化为2个图标,每次增长2个图标
m_pImageList->Create(32, 32, ILC_COLOR8|ILC_MASK, 2, 2);
CWinApp* pApp = AfxGetApp(); //获得应用程序指针
HICON hIconOnline = pApp->LoadIcon(IDI_CHATTER_ONLINE); //在线图标
HICON hIconOffline = pApp->LoadIcon(IDI_CHATTER_OFFLINE); //离线图标
m_pImageList->Add(hIconOnline); //加入图标
m_pImageList->Add(hIconOffline); //加入图标
//将图标列表赋值给列表控件
m_ctlUserList.SetImageList(m_pImageList, LVSIL_SMALL);
//设置列表头
m_ctlUserList.InsertColumn(0, _T("名称"), LVCFMT_IMAGE|LVCFMT_LEFT);
m_ctlUserList.InsertColumn(1, _T("密码"), LVCFMT_CENTER);
m_ctlUserList.InsertColumn(2, _T("地址"), LVCFMT_CENTER);
m_ctlUserList.InsertColumn(3, _T("端口"), LVCFMT_CENTER);
m_ctlUserList.InsertColumn(4, _T("时间"), LVCFMT_CENTER);
//设置各列的宽度
CRect rect;
m_ctlUserList.GetClientRect(&rect);//获取列表视图控件的宽度
int nWidth = rect.Width();
m_ctlUserList.SetColumnWidth(0, nWidth * 25 / 100);//名称占25%
m_ctlUserList.SetColumnWidth(1, nWidth * 13 / 100);//密码占13%
m_ctlUserList.SetColumnWidth(2, nWidth * 25 / 100);//地址占25%
m_ctlUserList.SetColumnWidth(3, nWidth * 12 / 100);//端口占12%
m_ctlUserList.SetColumnWidth(4, nWidth * 25 / 100);//时间占25%
}
/*
* 初始化用户列表
*/
void CServerDlg::InitUserList(void)
{
LoadUserList(m_UserList);//读取用户信息
//遍历链表的每个节点,读取数据,初始化用户列表
POSITION pos;
int nLine = 0;
for (pos = m_UserList.GetHeadPosition(); NULL != pos;)
{
CUserInfo *pUserInfo = (CUserInfo*)m_UserList.GetNext(pos);
if (NULL != pUserInfo)
{
m_ctlUserList.InsertItem(nLine,pUserInfo->m_strName,1); //名称,用户初始化为离线状态
m_ctlUserList.SetItemText(nLine,1,pUserInfo->m_strPassword); //密码
in_addr userAddr;
userAddr.S_un.S_addr = (long)pUserInfo->m_lIP;
CString strUserAddr(inet_ntoa(userAddr)); //IP
m_ctlUserList.SetItemText(nLine,2,strUserAddr);
CString strUserPort; //端口
**strUserPort.Format("%d",ntohs(pUserInfo->m_nPort)); **
m_ctlUserList.SetItemText(nLine,3,strUserPort);
CString strUserTime = pUserInfo->m_time.Format("%c"); //端口
m_ctlUserList.SetItemText(nLine,4,strUserTime);
nLine++;
}
}
}
/*
* 保存用户信息
*/
void CServerDlg::SaveUserList(CObList &obList)
{
char appPath[256];
GetCurrentDirectory(256,appPath); //取得应用程序当前路径
CString filePath; //保存Ini文件名
filePath.Format("%s",appPath);
filePath +="\\";
**filePath += USERLISTINI; **
//删除原来的文件
DeleteFile(filePath);
POSITION pos;
int nIndex = 0;
//遍历整个用户链表
for(pos = obList.GetHeadPosition(); pos != NULL; )
{
//获取数据
CUserInfo *pUserInfo = (CUserInfo*)obList.GetNext(pos);//用户信息
if (NULL == pUserInfo)
{
break;
}
CString strSection("section"); //section
CString strIndex;
strIndex.Format("%d",nIndex);
strSection += strIndex;
in_addr userAddr;
userAddr.S_un.S_addr = (long)pUserInfo->m_lIP;
CString strUserAddr(inet_ntoa(userAddr)); //用户IP
CString strUserPort; //用户端口
strUserPort.Format("%d",pUserInfo->m_nPort);
CString strUserTime; //用户登录线时间
strUserTime.Format("%ld", (long)pUserInfo->m_time.GetTime());
//写用户名字
**WritePrivateProfileString(strSection, _T("Name"), pUserInfo->m_strName, filePath); **
//写用户密码
**WritePrivateProfileString(strSection, _T("Password"), pUserInfo->m_strPassword, filePath); **
//写用户地址
**WritePrivateProfileString(strSection, _T("Address"), strUserAddr,filePath); **
//写用户端口
**WritePrivateProfileString(strSection, _T("Port"), strUserPort,filePath); **
//写用时间
**WritePrivateProfileString(strSection, _T("Time"), strUserTime,filePath); **
delete pUserInfo;//删除对象
pUserInfo = NULL;
nIndex++;
}
obList.RemoveAll();//删除链表所有节点
}
/*
* 读取用户信息
*/
void CServerDlg::LoadUserList(CObList &obList)
{
#define TEMP_BUF_SIZE 32//缓冲区长度
char appPath[256];
GetCurrentDirectory(256,appPath); //取得应用程序当前路径
CString filePath; //保存Ini文件名
filePath.Format("%s",appPath);
filePath += "\\";
filePath += USERLISTINI;
int nIndex = 0;
while (TRUE)
{
CString strSection("section");
CString strIndex;
strIndex.Format("%d",nIndex);
strSection += strIndex;
//用户名称
**CString strSectionKey = "Name"; **
CString strValue = _T("");
char cBuf[TEMP_BUF_SIZE];
memset(cBuf, 0, TEMP_BUF_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
CUserInfo *pUserInfo = new CUserInfo();//新建CUserInfo对象
pUserInfo->m_strName = cBuf;
//读取用户密码
strSectionKey = "Password";
memset(cBuf, 0, TEMP_BUF_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
pUserInfo->m_strPassword = cBuf;
//读取用户地址
strSectionKey = "Address";
memset(cBuf, 0, TEMP_BUF_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
u_long clinetAddr = inet_addr(cBuf);
pUserInfo->m_lIP = (DWORD)clinetAddr;
//读取用户端口
strSectionKey = "Port";
memset(cBuf, 0, TEMP_BUF_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
pUserInfo->m_nPort = atoi(cBuf);
//读取用户时间
strSectionKey = "Time";
memset(cBuf, 0, TEMP_BUF_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
time_t time = atol(cBuf);
pUserInfo->m_time = time;
obList.AddTail(pUserInfo); //加入链表
nIndex++;
**} **
}
/*
* 保存离线消息
*/
void CServerDlg::SaveOfflineMsg(CObList &obList)
{
char appPath[256];
GetCurrentDirectory(256,appPath); //取得应用程序当前路径
CString filePath; //保存Ini文件名
filePath.Format("%s",appPath);
filePath +="\\";
**filePath += OFFLINEMSGLIST; **
//删除原来的文件
DeleteFile(filePath);
POSITION pos;
int nIndex = 0;
for(pos = obList.GetHeadPosition(); pos != NULL; )
{
CChatPacket *pPacket = (CChatPacket*)obList.GetNext(pos);//用户信息
if (NULL == pPacket)
{
break;
}
CString strSection("section"); //section
CString strIndex;
strIndex.Format("%d",nIndex);
strSection += strIndex;
CString strTime;
strTime.Format("%ld",(long)pPacket->m_time.GetTime());
pPacket->m_strMsg.Replace("\r\n", "$");//取代回车符为$
**//写发送消息的用户名称 **
WritePrivateProfileString(strSection, _T("SendMsgUser"), pPacket->m_UserInfo.m_strName, filePath); //发送消息用户
//写接收消息的用户名称
WritePrivateProfileString(strSection, _T("RecvMsgUser"), pPacket->m_OfflineUserInfo.m_strName, filePath); //接收消息用户
//写发送消息 的时间
WritePrivateProfileString(strSection, _T("Time"), strTime,filePath); //发送消息时间
//写发送的消息
WritePrivateProfileString(strSection, _T("Message"), pPacket->m_strMsg, filePath); //发送的消息
delete pPacket;
pPacket = NULL;
nIndex++;
}
obList.RemoveAll();
}
/*
* 读取离线消息
*/
void CServerDlg::LoadOfflineMsg(CObList &obList)
{
**m_csOfflineList.Lock(); **
char appPath[256];
GetCurrentDirectory(256,appPath); //取得应用程序当前路径
CString filePath; //保存Ini文件名
filePath.Format("%s",appPath);
filePath += "\\";
filePath += OFFLINEMSGLIST;
int nIndex = 0;
while (TRUE)
{
CString strSection("section"); //section
CString strIndex;
strIndex.Format("%d",nIndex);
strSection += strIndex;
//读取发送消息的用户名称
CString strSectionKey = "SendMsgUser";
CString strValue = _T("");
char cBuf[MAX_MSG_SIZE];
memset(cBuf, 0, MAX_MSG_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
CChatPacket *pPacket = new CChatPacket(); //新建CChatPacket对象
pPacket->m_type = CChatPacket::MESSAGE;
pPacket->m_UserInfo.m_strName = cBuf; //发送消息的用户名
//读取接收消息的用户名称
strSectionKey = "RecvMsgUser";
memset(cBuf, 0, MAX_MSG_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
pPacket->m_OfflineUserInfo.m_strName = cBuf;
//读取发送消息时间
strSectionKey = "Time";
memset(cBuf, 0, MAX_MSG_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
time_t time = atol(cBuf);
pPacket->m_time = time;
//读取发送消息
strSectionKey = "Message";
memset(cBuf, 0, MAX_MSG_SIZE);
if(0 == GetPrivateProfileString (strSection, strSectionKey, NULL, cBuf, TEMP_BUF_SIZE, filePath))
{
break;
}
////取代$为回车符
CString strMsg(cBuf);
strMsg.Replace( "$","\r\n");
pPacket->m_strMsg = strMsg;
obList.AddTail(pPacket);//加入链表
nIndex++;
}
m_csOfflineList.Unlock();
}
/*
* 处理数据
*/
BOOL CServerDlg::ProcessPendingRead(CArchive *pArchiveIn, CArchive *pArchiveOut, CClientSocket *pClientSocket)
**{ **
**do **
**{ **
CChatPacket chatPact;//接收数据包
chatPact.Serialize(*pArchiveIn);
if (CChatPacket::MESSAGE == chatPact.m_type)//发给离线用户的消息
{
m_csOfflineList.Lock();
//保存离线用户消息
CChatPacket *pChatPacket = new CChatPacket();
//包类型
**pChatPacket->m_type = CChatPacket::MESSAGE; **
//发送消息用户
**pChatPacket->m_UserInfo.m_strName = chatPact.m_UserInfo.m_strName; **
//接收消息用户
pChatPacket->m_OfflineUserInfo.m_strName = chatPact.m_OfflineUserInfo.m_strName;
//发送的消息
**pChatPacket->m_strMsg = chatPact.m_strMsg; **
//发包时间
**pChatPacket->m_time = chatPact.m_time; **
//加入离线消息链表
**m_OfflineMsgList.AddTail(pChatPacket); **
**m_csOfflineList.Unlock(); **
}else if (CChatPacket::USERLIST == chatPact.m_type)//请求用户列表--用户刚登录
{
//保存用户信息
pClientSocket->SaveUserInfo(chatPact.m_UserInfo);
//户更新信息
if (!UpdateUserList(chatPact.m_UserInfo, /*pArchiveOut,*/ pClientSocket))
return FALSE;
//向所有的在线用户发送用户列表
SendUserList();
//转发离线消息
TransmitMsg(chatPact, /*pArchiveOut*/pClientSocket);
//更新服务器界面
UpdateServerListCtl(m_UserList);
}
} while(!pArchiveIn->IsBufferEmpty());
return TRUE;
}
/*
* 发送用户列表
*/
void CServerDlg::SendUserList()
{
//复制用户链表
CObList tempChatterlist;
CopyUserList(tempChatterlist);
CChatPacket packet;
packet.m_type = CChatPacket::USERLIST; //包的类型
packet.m_pUserList = &tempChatterlist; //用户链表
m_csChatterList.Lock();//上锁
//向所有在线用户发送列表
POSITION pos = NULL;
for (pos = m_ChatterList.GetHeadPosition(); NULL!=pos;)
{
CClientSocket *pClientSocket = (CClientSocket*)m_ChatterList.GetNext(pos);
pClientSocket->SendUserList(&packet);//发送数据包
}
m_csChatterList.Unlock();//解锁
DeleteTempUserList(tempChatterlist);//删除临时链表
}
/*
* 复制用户链表
*/
void CServerDlg::CopyUserList(CObList &obList)
{
if (m_UserList.IsEmpty())
{
return ;
}
POSITION pos = NULL;
CUserInfo *pUserInfo = NULL;
CUserInfo *pTempUserInfo = NULL;
for (pos = m_UserList.GetHeadPosition(); pos != NULL;)
{
pUserInfo = (CUserInfo*)m_UserList.GetNext(pos);
//不要复制用户的密码
pTempUserInfo = new CUserInfo();
pTempUserInfo->m_strName = pUserInfo->m_strName; //名称
pTempUserInfo->m_eStatus = pUserInfo->m_eStatus; //状态
pTempUserInfo->m_lIP = pUserInfo->m_lIP; //IP
pTempUserInfo->m_nPort = pUserInfo->m_nPort; //端口
obList.AddTail(pTempUserInfo);
}
}
/*
* 删除临时链表
*/
void CServerDlg::DeleteTempUserList(CObList &obList)
{
if (obList.IsEmpty())
{
return;
}
POSITION pos = NULL;
CUserInfo *pUserInfo = NULL;
for (pos = obList.GetHeadPosition(); NULL != pos;)
{
pUserInfo = (CUserInfo*)obList.GetNext(pos);
delete pUserInfo;
pUserInfo = NULL;
}
obList.RemoveAll();
}
/*
* 更新用户链表
*/
BOOL CServerDlg::UpdateUserList(const CUserInfo &userInfo, CClientSocket *pClientSocket)
{
BOOL retVal = TRUE; //返回值
m_csUserList.Lock(); //上锁
**POSITION pos; //位置变量 **
CUserInfo *pUserInfo = NULL; //用户对象指针
BOOL bFind = FALSE; //是否找到该用户
//该用户是否存在于链表中
for (pos = m_UserList.GetHeadPosition(); NULL != pos;)
{
**pUserInfo = (CUserInfo*)m_UserList.GetNext(pos);//节点下移 **
if (pUserInfo->m_strName == userInfo.m_strName )//名字相同
{
if (pUserInfo->m_strPassword == userInfo.m_strPassword) //在册用户
{
if(CUserInfo::ONLINE == pUserInfo->m_eStatus) //用户已经登录
{
//从在线链表中删除该节点
DeleteChatter(pClientSocket);
//发送"用户已经登录"消息
CChatPacket packet;
packet.m_type = CChatPacket::SERVERMSG; //数据包类型
packet.m_strMsg = _T(" 用户已经登录! ");//消息
packet.m_time = CTime::GetCurrentTime(); //时间
pClientSocket->SendUserMsg(&packet); //发送数据
**m_csUserList.Unlock(); //解锁 **
return FALSE;
}else//用户登录
{
pUserInfo->m_eStatus = CUserInfo::ONLINE; //修改用户状态
pUserInfo->m_lIP = userInfo.m_lIP; //IP地址
pUserInfo->m_nPort = userInfo.m_nPort; //端口
bFind = TRUE;
**break; **
}
}else//密码错误
{
//从在线链表中删除该节点
DeleteChatter(pClientSocket);
//发送"密码错误"消息
CChatPacket packet;
packet.m_type = CChatPacket::SERVERMSG; //数据包类型
**packet.m_strMsg = _T(" 密码错误\t\n 请重新登录! "); //消息 **
packet.m_time = CTime::GetCurrentTime(); //时间
pClientSocket->SendUserMsg(&packet);//发送数据
m_csUserList.Unlock();//解锁
**return FALSE; **
**} **
}
}
if (FALSE == bFind)//注册新用户
{
CUserInfo *pUserInfo = new CUserInfo();
pUserInfo->m_strName = userInfo.m_strName; //姓名
pUserInfo->m_strPassword = userInfo.m_strPassword; //密码
pUserInfo->m_eStatus = CUserInfo::ONLINE; //状态
pUserInfo->m_lIP = userInfo.m_lIP; //地址
pUserInfo->m_nPort = userInfo.m_nPort; //端口
pUserInfo->m_time = userInfo.m_time; //时间
m_UserList.AddTail(pUserInfo); //加入链表
}
m_csUserList.Unlock();//解锁
**return retVal; **
}
/*
* 转发离线消息
*/
void CServerDlg::TransmitMsg(const CChatPacket &packet, CClientSocket *pClientSocket)
{
m_csOfflineList.Lock();
POSITION pos1 = NULL; //位置变量
POSITION pos2 = NULL; //位置变量
CChatPacket *pPacket = NULL;//数据包
//遍历整个离线消息链表
for (pos1 = m_OfflineMsgList.GetHeadPosition(); (pos2 = pos1) != NULL;)
{
//取出数据包
pPacket = (CChatPacket*)m_OfflineMsgList.GetNext(pos1);
//登录用户的名字和接收离线消息的用户名字相同
if (pPacket->m_OfflineUserInfo.m_strName == packet.m_UserInfo.m_strName)
{
pClientSocket->SendUserMsg(pPacket);//发送离线消息
m_OfflineMsgList.RemoveAt(pos2); //删除链表节点
delete pPacket; //删除对象
**pPacket = NULL; **
}
}
m_csOfflineList.Unlock();
}
/*
* 更新服务器界面
*/
void CServerDlg::UpdateServerListCtl(const CObList &obList)
{
m_csUserList.Lock();//上锁
//删除原来数据项
m_ctlUserList.DeleteAllItems();
POSITION pos = NULL; //位置变量
int nIndex = 0; //序号
for (pos = m_UserList.GetHeadPosition(); NULL != pos;)
{
//取出用户信息
CUserInfo *pUserInfo = (CUserInfo*)m_UserList.GetNext(pos);
if (CUserInfo::ONLINE == pUserInfo->m_eStatus)//在线状态
{
**//插入数据项 **
m_ctlUserList.InsertItem(nIndex, pUserInfo->m_strName,CUserInfo::ONLINE);
**}else//离线状态 **
{
**//插入数据项 **
m_ctlUserList.InsertItem(nIndex, pUserInfo->m_strName,CUserInfo::OFFLINE);
}
m_ctlUserList.SetItemText(nIndex,1,pUserInfo->m_strPassword); //用户密码
in_addr userAddr;
userAddr.S_un.S_addr = (long)pUserInfo->m_lIP;
CString strUserAddr(inet_ntoa(userAddr)); //用户IP
m_ctlUserList.SetItemText(nIndex,2,strUserAddr);
CString strUserPort; //用户端口
**strUserPort.Format("%d",ntohs(pUserInfo->m_nPort)); **
m_ctlUserList.SetItemText(nIndex,3,strUserPort);
CString strUserTime = pUserInfo->m_time.Format("%c"); //登录时间
m_ctlUserList.SetItemText(nIndex,4,strUserTime);
**nIndex++; **
}
m_csUserList.Unlock();//解锁
}
/*
* 当用户离线时删除用户
*/
void CServerDlg::ReleaseChatter(CClientSocket *pClientSocket)
{
m_csChatterList.Lock();
POSITION pos = NULL;
//找到该用户
if ((pos = m_ChatterList.Find(pClientSocket)) != NULL)
{
ASSERT((CClientSocket*)m_ChatterList.GetAt(pos) == pClientSocket);
//获取该用户信息
CUserInfo userInfo = pClientSocket->GetUserInfo();
//更新该用户信息
POSITION posUser = NULL;
for (posUser = m_UserList.GetHeadPosition(); posUser != NULL;)
{
CUserInfo *pUserInfo = (CUserInfo*)m_UserList.GetNext(posUser);
if (pUserInfo->m_strName == userInfo.m_strName)
{
pUserInfo->m_eStatus = CUserInfo::OFFLINE; //修改状态为离线
**pUserInfo->m_lIP = userInfo.m_lIP; //修改用户地址 **
pUserInfo->m_nPort = userInfo.m_nPort; //修改用户端口
pUserInfo->m_time = CTime::GetCurrentTime();//设置离线时间
break;
}
}
//删除该在线用户
m_ChatterList.RemoveAt(pos);
//通知所有在线用户更新用户列表
SendUserList();
//更新服务器界面
**UpdateServerListCtl(m_UserList); **
}
m_csChatterList.Unlock();
}
/*
* 登录失败处理
*/
void CServerDlg::DeleteChatter(CClientSocket *pClientSocket)
{
m_csChatterList.Lock();
POSITION pos = NULL;
if ((pos = m_ChatterList.Find(pClientSocket)) != NULL)
{
**ASSERT((CClientSocket*)m_ChatterList.GetAt(pos) == pClientSocket); **
//删除该在线用户
**m_ChatterList.RemoveAt(pos); **
}
m_csChatterList.Unlock();
}
/*
* WM_DESTROY消息相应函数
*/
void CServerDlg::OnDestroy()
{
SaveUserList(m_UserList); //保存用户信息
SaveOfflineMsg(m_OfflineMsgList); //保存离线信息
DeleteAllChatter(); //删除在线用户
if (NULL != m_pListenSocket) //删除CListenSocket类对象
{
delete m_pListenSocket;
m_pListenSocket = NULL;
}
if (NULL != m_pImageList) //删除CImageList类对象
{
delete m_pImageList;
m_pImageList = NULL;
}
}
/*
* 右击列表控件响应函数
*/
**void CServerDlg::OnRclickClientList(NMHDR* pNMHDR, LRESULT* pResult) **
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView->iItem != -1)
{
DWORD dwPos = GetMessagePos(); //鼠标获取位置
CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
CMenu menu;
VERIFY( menu.LoadMenu(IDR_SERVER_LIST_MENU)); //装载菜单资源
CMenu* popup = menu.GetSubMenu(0); //获取菜单项
ASSERT( popup != NULL );
//弹出快捷菜单
popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this );
}
*pResult = 0;
}
/*
* 处理菜单消息
*/
**void CServerDlg::OnServerListDeleteMenuitem() **
{
//获得被选中数据项的位置
POSITION pos = m_ctlUserList.GetFirstSelectedItemPosition();
int nSelectedItem = m_ctlUserList.GetNextSelectedItem(pos);
//获取用户名称
CString strPeerName = m_ctlUserList.GetItemText(nSelectedItem, 0);
**POSITION pos1 = NULL; //位置变量 **
POSITION pos2 = NULL; //位置变量
BOOL bDeleteUser = FALSE; //是否找到该用户
//在用户链表中查找
for (pos1 = m_UserList.GetHeadPosition();(pos2=pos1)!= NULL;)
{
//取出数据
CUserInfo* pUserInfo = (CUserInfo*)m_UserList.GetNext(pos1);
if (pUserInfo->m_strName == strPeerName) //名称相同
{
if (pUserInfo->m_eStatus == CUserInfo::ONLINE) //不能删除在线用户
{
AfxMessageBox("不能删除在线用户!",MB_OK, -1);
return ;
}else//删除不在线用户
{
CUserInfo *pUserInfo = (CUserInfo*)m_UserList.GetAt(pos2);
m_UserList.RemoveAt(pos2); //删除指针
delete pUserInfo; //删除对象
**pUserInfo = NULL; **
bDeleteUser = TRUE;
//更新服务器界面
UpdateServerListCtl(m_UserList);
}
}
}
if (TRUE == bDeleteUser)
{
//通知客户端更新成员列表
SendUserList();
**} **
}
/*
* 删除所有在线用户
*/
void CServerDlg::DeleteAllChatter( void )
{
POSITION pos;
CClientSocket *pClient = NULL;
for (pos = m_ChatterList.GetHeadPosition(); NULL != pos; )
{
pClient = (CClientSocket*)m_ChatterList.GetNext(pos);
delete pClient;
pClient = NULL;
}
m_ChatterList.RemoveAll();
}
UserInfo.cpp
// UserInfo.cpp: implementation of the CUserInfo class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "UserInfo.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//IMPLEMENT_DYNCREATE(CUserInfo, CObject)
IMPLEMENT_SERIAL(CUserInfo, CObject, 1)//new一个对象然后调用默认构造函数->init
CUserInfo::CUserInfo()
{
Init();
}
CUserInfo::~CUserInfo()
{
}
/*
* 初始化
*/
void CUserInfo::Init(void)
{
m_strName = _T("");
m_strPassword = _T("");
m_eStatus = UNKNOWN;
m_lIP = 0;
m_nPort = 0;
m_time = CTime::GetCurrentTime();
}
/*
* 序列化
*/
void CUserInfo::Serialize(CArchive& ar)
{
CObject::Serialize(ar);//调用基类的序列化函数
if (ar.IsStoring())//发送数据
{
ar << m_strName; //名字
ar << m_strPassword; //密码
WORD byStatus = 0;
byStatus = (WORD)m_eStatus;
ar << byStatus; //状态
ar << m_lIP; //地址
ar << m_nPort; //端口
long lTime = m_time.GetTime(); //日期和时间
ar << lTime;
}else
{
ar >> m_strName; //名字
ar >> m_strPassword; //密码
WORD byStatus = 0;
ar >> byStatus;
m_eStatus = (USERSTATUE)byStatus;//状态
ar >> m_lIP; //地址
ar >> m_nPort; //端口
long lTime;
ar >> lTime; //日期和时间
m_time = CTime((time_t)lTime);
}
}
/*
* 赋值构造函数
*/
CUserInfo &CUserInfo::operator = (const CUserInfo &userInfo)
{
m_strName = userInfo.m_strName;
m_strPassword = userInfo.m_strPassword;
m_eStatus = userInfo.m_eStatus;
m_lIP = userInfo.m_lIP;
m_nPort = userInfo.m_nPort;
m_time = userInfo.m_time;
return *this;
}
/*
* 赋值构造函数
*/
CUserInfo::CUserInfo(const CUserInfo& userInfo)
{
m_strName = userInfo.m_strName;
m_strPassword = userInfo.m_strPassword;
m_eStatus = userInfo.m_eStatus;
m_lIP = userInfo.m_lIP;
m_nPort = userInfo.m_nPort;
m_time = userInfo.m_time;
}
ChatDlg.cpp
// ChatDlg.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h"
#include "Client.h"
#include "ChatDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CChatDlg dialog
CChatDlg::CChatDlg(CWnd* pParent /*=NULL*/)
: CDialog(CChatDlg::IDD, pParent),m_strRec("")
{
//{{AFX_DATA_INIT(CChatDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_pSocket = NULL;
m_pFile = NULL;
m_pArchiveIn = NULL;
m_pArchiveOut = NULL;
m_bOfflineWnd = FALSE;
m_pOfflinePacket = NULL;
}
/*
* 在线/离线窗口
*/
CChatDlg::CChatDlg(CWnd* pParent, const CUserInfo &peerInfo, const CUserInfo &userInfor)
:CDialog(CChatDlg::IDD, pParent),m_strRec("")
{
m_PeerInfo = peerInfo; //对方用户信息
m_UserInfo = userInfor; //用户信息
m_pSocket = NULL; //CChatSocket指针
m_pFile = NULL; //文件对象
m_pArchiveIn = NULL; //读入文档对象
m_pArchiveOut = NULL; //写入文档对象
m_pOfflinePacket = NULL;//离线数据包
m_bOfflineWnd = FALSE; //接收离线窗口
}
/*
* 显示离线消息窗口
*/
CChatDlg::CChatDlg(CWnd* pParent, CChatPacket *pPacket)
:CDialog(CChatDlg::IDD, pParent),m_strRec("")
{
m_pOfflinePacket = pPacket; //离线数据包
m_bOfflineWnd = TRUE; //接收离线窗口
m_pSocket = NULL; //CChatSocket指针
m_pFile = NULL; //文件对象
m_pArchiveIn = NULL; //读入文档对象
m_pArchiveOut = NULL; //写入文档对象
}
void CChatDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CChatDlg)
DDX_Control(pDX, IDC_CLIENT_CHAT_OUTPUT_EDIT, m_ctlOutput);
DDX_Control(pDX, IDC_CLIENT_CHAT_IPUT_EDIT, m_ctlInput);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CChatDlg, CDialog)
//{{AFX_MSG_MAP(CChatDlg)
ON_WM_DESTROY()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CChatDlg message handlers
/*
* 接收消息
*/
void CChatDlg::ProcessPendingRead(void)
{
do
{
CChatPacket chatPact;
chatPact.Serialize(*m_pArchiveIn);
if (CChatPacket::MESSAGE == chatPact.m_type)//消息
{
DisplayRecvMessage(&chatPact);
}
} while(!m_pArchiveIn->IsBufferEmpty());
}
/*
* WM_INITDIALOG消息响应函数
*/
BOOL CChatDlg::OnInitDialog()
{
//调用基类函数
CDialog::OnInitDialog();
if (NULL != m_pOfflinePacket && TRUE == m_bOfflineWnd) //显示离线消息
{
DisplayOfflineMessage(m_pOfflinePacket);
}else if (m_PeerInfo.m_eStatus == CUserInfo::ONLINE) //在线聊天
{
SetWindowText(_T("与") + m_PeerInfo.m_strName + _T("聊天中"));
}else //发送离线消息
{
SetWindowText(m_PeerInfo.m_strName + _T("不在线哦"));
}
return TRUE;
}
/*
* 连接对方
*/
BOOL CChatDlg::ConnectToPeer(void)
{
//创建CChatSocket对象
if(!InitSocket())
{
return FALSE;
}
//创建套接字,绑定
if (!m_pSocket->Create())
{
delete m_pSocket;
m_pSocket = NULL;
AfxMessageBox(_T("创建套接字失败!"));
return FALSE;
}
m_pFile = new CSocketFile(m_pSocket); //创建文件对象
m_pArchiveIn = new CArchive(m_pFile,CArchive::load);//创建读入文档对象
//连接对方
SOCKADDR_IN peerAddr;
peerAddr.sin_family = AF_INET;
peerAddr.sin_addr.S_un.S_addr = m_PeerInfo.m_lIP;
peerAddr.sin_port = m_PeerInfo.m_nPort;
while (!m_pSocket->Connect((SOCKADDR*)&peerAddr, sizeof(SOCKADDR_IN)))
{
if (AfxMessageBox(_T("连接对方失败,是否再次尝试连接。"),MB_YESNO) == IDNO)
{
return FALSE;
}
}
return TRUE;
}
BOOL CChatDlg::InitSocket(void)
{
//创建套接字
m_pSocket = new CChatSocket(this);
return TRUE;
}
/*
* 显示主动聊天接收到的消息
*/
void CChatDlg::DisplayRecvMessage(CChatPacket *pPacket)
{
CString chatHeader = pPacket->m_UserInfo.m_strName;//发送消息的用户名称
//格式化日期和时间
CTime timeChatting(pPacket->m_time);
int year = timeChatting.GetYear();
int month = timeChatting.GetMonth();
int day = timeChatting.GetDay();
int hour = timeChatting.GetHour();
int minute = timeChatting.GetMinute();
int second = timeChatting.GetSecond();
CString chatTime;
chatTime.Format(" (%d-%d-%d %d:%d:%d)", year, month, day, hour, minute, second);
//显示消息头
chatHeader += chatTime;
CString strItem = chatHeader + "\r\n"+ pPacket->m_strMsg + "\r\n";
//显示消息
int len = m_ctlOutput.GetWindowTextLength();
m_ctlOutput.SetSel(len,len);
m_ctlOutput.ReplaceSel(strItem);
}
/*
* 处理键盘消息
*/
BOOL CChatDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->hwnd == m_ctlInput.GetSafeHwnd()) //输入消息窗口
{
if (pMsg->wParam == VK_RETURN) //return键
{
if (TRUE == m_bOfflineWnd ) //显示离线消息窗口
{
return TRUE;
}else //在线/离线聊天窗口
{
SendPeerMessage(); //发送消息
m_ctlInput.SetWindowText(""); //清空输入消息窗口内容
}
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg); //其他消息
}
/*
* 发送消息
*/
void CChatDlg::SendPeerMessage( void )
{
//输入消息窗口内容为空
if (0 == m_ctlInput.GetWindowTextLength())
{
return;
}
//对方在线
if(m_PeerInfo.m_eStatus == CUserInfo::ONLINE) //发送在线消息
{
CChatPacket packet;
packet.m_UserInfo = this->m_UserInfo; //发送消息用户名称
packet.m_type = CChatPacket::MESSAGE; //包的类型
m_ctlInput.GetWindowText(packet.m_strMsg); //消息
packet.m_time = CTime::GetCurrentTime(); //发包时间
//发送消息
m_pArchiveOut = new CArchive(m_pFile,CArchive::store);//创建写入文档对象
packet.Serialize(*m_pArchiveOut);
m_pArchiveOut->Flush();
delete m_pArchiveOut;
m_pArchiveOut = NULL;
//显示发送的消息
DisplaySentMessage(&packet);
}else//对方离线
{
//显示发送的离线消息
DisplaySentMessage();
}
}
/*
* 显示发送的消息
*/
void CChatDlg::DisplaySentMessage(CChatPacket *pPacket)
{
CString chatHeader = pPacket->m_UserInfo.m_strName; //发送消息用户名称
//格式化发送消息的时间
CTime timeChatting = pPacket->m_time;
int year = timeChatting.GetYear(); //年
int month = timeChatting.GetMonth(); //月
int day = timeChatting.GetDay(); //日
int hour = timeChatting.GetHour(); //时
int minute = timeChatting.GetMinute(); //分
int second = timeChatting.GetSecond(); //秒
CString chatTime;
chatTime.Format(" (%d-%d-%d %d:%d:%d)", year, month, day, hour, minute, second);
chatHeader += chatTime;//消息头
//获取输入消息
CString strMsg;
m_ctlInput.GetWindowText(strMsg);
//消息头与消息分行显示
CString strItem = chatHeader + "\r\n"+ pPacket->m_strMsg + "\r\n";
//在当前消息行下面显示消息
int len = m_ctlOutput.GetWindowTextLength();//当前文本长度
m_ctlOutput.SetSel(len,len); //移动插入符到当前文本最后
m_ctlOutput.ReplaceSel(strItem); //在当前文本后插入消息
}
/*
* 显示发送的消息
*/
void CChatDlg::DisplaySentMessage(void)
{
CString chatHeader = m_UserInfo.m_strName;
CTime timeChatting = CTime::GetCurrentTime();
int year = timeChatting.GetYear(); //年
int month = timeChatting.GetMonth(); //月
int day = timeChatting.GetDay(); //日
int hour = timeChatting.GetHour(); //时
int minute = timeChatting.GetMinute(); //分
int second = timeChatting.GetSecond(); //秒
CString chatTime;
chatTime.Format(" (%d-%d-%d %d:%d:%d)", year, month, day, hour, minute, second);
chatHeader += chatTime;//消息头
CString strMsg;
m_ctlInput.GetWindowText(strMsg);
CString strItem = chatHeader + "\r\n"+ strMsg + "\r\n";
//显示消息
int len = m_ctlOutput.GetWindowTextLength();
m_ctlOutput.SetSel(len,len);
m_ctlOutput.ReplaceSel(strItem);
}
void CChatDlg::InitArchive(void)
{
//初始化输入和输出流
m_pFile = new CSocketFile(m_pSocket); //初始化CSocketFile
m_pArchiveIn = new CArchive(m_pFile,CArchive::load); //初始化读入流
}
void CChatDlg::SetPeerInfor( CUserInfo &peerInfo )
{
m_PeerInfo = peerInfo;
}
void CChatDlg::SetUserInfor( CUserInfo &userInfo )
{
m_UserInfo = userInfo;
}
void CChatDlg::OnDestroy()
{
m_ctlOutput.GetWindowText(m_strRec);//聊天记录
CDialog::OnDestroy();
}
CChatDlg::~CChatDlg()
{
if(FALSE == m_bOfflineWnd)
{
if (NULL != m_pArchiveIn)
{
delete m_pArchiveIn;
m_pArchiveIn = NULL;
}
if (NULL != m_pFile)
{
delete m_pFile;
m_pFile = NULL;
}
if (NULL != m_pSocket)
{
delete m_pSocket;
m_pSocket = NULL;
}
}
}
CChatSocket *CChatDlg::GetChatSocket( void )
{
return m_pSocket;
}
/*
* 显示离线消息
*/
void CChatDlg::DisplayOfflineMessage( CChatPacket *pPacket )
{
CRect inputWndRect;
m_ctlInput.GetWindowRect(&inputWndRect);//获取输入消息窗口区域
m_ctlInput.MoveWindow(0, 0, 0, 0); //设置该窗口大小为0
CRect chatDlgRect;
GetClientRect(&chatDlgRect); //获取对话框客户区大小
MoveWindow(0, 0, chatDlgRect.Width(), //修改对话框的高度
chatDlgRect.Height() - inputWndRect.Height());
GetClientRect(&chatDlgRect); //获取对话框客户区大小
m_ctlOutput.MoveWindow(5, 5, //修改显示消息窗口高度
chatDlgRect.Width() -10, chatDlgRect.Height() -10);
m_PeerInfo = pPacket->m_UserInfo; //发送消息用户名称
SetWindowText(m_PeerInfo.m_strName + _T("发送给您的离线消息")); //设置标题
m_ctlOutput.SetWindowText(pPacket->m_strMsg ); //设置消息
}
ChatPacket.cpp
// ChatPacket.cpp: implementation of the CChatPacket class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//IMPLEMENT_DYNCREATE(CChatPacket, CObject)
IMPLEMENT_SERIAL(CChatPacket, CObject, 1 )
CChatPacket::CChatPacket()
{
Init();
}
CChatPacket::~CChatPacket()
{
}
/*
* 初始化
*/
void CChatPacket::Init(void)
{
m_type = UNKNOWN; //为知类型
m_strMsg = _T(""); //清空
m_pUserList = NULL; //清空
m_time = CTime::GetCurrentTime();
}
/*
* 序列化
*/
void CChatPacket::Serialize(CArchive& ar)
{
CObject::Serialize(ar);//调用基类的序列化函数
if (ar.IsStoring())//发送数据
{
BYTE byType = m_type;
ar << byType; //包的类型
ar << m_strMsg; //消息
long lTime = m_time.GetTime(); //日期和时间
ar << lTime;
}else//接收数据
{
BYTE byType;
ar >> byType;
m_type = (PACKETTYPE)byType; //包的类型
ar >> m_strMsg; //消息
long lTime;
ar >> lTime; //日期和时间
m_time = CTime((time_t)lTime);
}
m_UserInfo.Serialize(ar); //序列化用户信息
m_OfflineUserInfo.Serialize(ar); //序列化离线用户信息
if (m_type == USERLIST && NULL != m_pUserList)//序列化用户列表
{
m_pUserList->Serialize(ar);
}
}
ChatSocket.cpp
// ChatSocket.cpp : implementation file
//
#include "stdafx.h"
#include "Client.h"
#include "ChatSocket.h"
#include "ClientDlg.h"
#include "ChatDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CChatSocket
CChatSocket::CChatSocket(CClientDlg *pClientDlg)
{
m_pClientDlg = pClientDlg;
m_pChatDlg = NULL;
}
CChatSocket::CChatSocket(CChatDlg *pChatDlg )
{
m_pChatDlg = pChatDlg;
m_pClientDlg = NULL;
}
CChatSocket::~CChatSocket()
{
m_pClientDlg = NULL;
m_pChatDlg = NULL;
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CChatSocket, CSocket)
//{{AFX_MSG_MAP(CChatSocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CChatSocket member functions
void CChatSocket::OnReceive(int nErrorCode)
{
if (NULL != m_pClientDlg)
{
m_pClientDlg->ProcessPendingRead();
}else if (NULL != m_pChatDlg)
{
m_pChatDlg->ProcessPendingRead();
}
}
Client.cpp
// Client.cpp : Defines the class behaviors for the application.
//
#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CClientApp
BEGIN_MESSAGE_MAP(CClientApp, CWinApp)
//{{AFX_MSG_MAP(CClientApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClientApp construction
CClientApp::CClientApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CClientApp object
CClientApp theApp;
/////////////////////////////////////////////////////////////////////////////
// CClientApp initialization
BOOL CClientApp::InitInstance()
{
if (!AfxSocketInit())
{
AfxMessageBox(_T("初始化套接字失败!"));
return FALSE;
}
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
CClientDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
ClientDlg.cpp
// ClientDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"
#include "ClientLogin.h"
#include "ChatDlg.h"
#include "ChatSocket.h"
#include "ListenSocket.h"
#include "PeerSocket.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CClientDlg dialog
CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/)
: CDialog(CClientDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CClientDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pImageList = NULL;
m_pArchiveIn = NULL;
m_pArchiveOut = NULL;
m_pFile = NULL;
m_pSocket = NULL;
memset(&m_hostAddr, 0, sizeof(SOCKADDR_IN));
}
CClientDlg::~CClientDlg()
{
if (NULL != m_pImageList)
{
delete m_pImageList;
m_pImageList = NULL;
}
if (NULL != m_pArchiveIn)
{
delete m_pArchiveIn;
m_pArchiveIn = NULL;
}
if (NULL != m_pArchiveOut)
{
delete m_pArchiveOut;
m_pArchiveOut = NULL;
}
if (NULL != m_pFile)
{
delete m_pFile;
m_pFile = NULL;
}
if (m_pSocket != NULL)
{
BYTE Buffer[50];
m_pSocket->ShutDown();
while(m_pSocket->Receive(Buffer,50) > 0);//????
delete m_pSocket;
m_pSocket = NULL;
}
}
void CClientDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CClientDlg)
DDX_Control(pDX, IDC_CLIENT_LIST, m_ctlUserList);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CClientDlg, CDialog)
//{{AFX_MSG_MAP(CClientDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_DESTROY()
ON_NOTIFY(NM_DBLCLK, IDC_CLIENT_LIST, OnDblclkClientList)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClientDlg message handlers
BOOL CClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
if (!Init())
EndDialog(IDCANCEL);
return FALSE;
//#define WM_USER 0x0400
// CUserInfo userInfo;
// userInfo.m_time = CTime::GetCurrentTime();
// userInfo.m_strName = "sunhm";
// CString str("我吃饭去了。");
// ShowOfflineMsg(userInfo, str);
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CClientDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CClientDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
/*
* 初始化
*/
BOOL CClientDlg::Init(void)
{
InitListCtrlSetting(); //初始化列表视图控件
BegingListen(); //开始监听
if(!ConnectToServer()) //连接服务器
return FALSE;
return TRUE;
}
/*
* 连接服务器
*/
BOOL CClientDlg::ConnectToServer(void)
{
//创建CChatSocket对象
m_pSocket = new CChatSocket(this);
if (!m_pSocket->Create())//创建套接字,绑定,注册网络事件
{
delete m_pSocket;
m_pSocket = NULL;
AfxMessageBox(_T("创建套接字失败!"));
return FALSE;
}
m_pFile = new CSocketFile(m_pSocket); //创建CSocketFile对象
m_pArchiveIn = new CArchive(m_pFile,CArchive::load); //创建读入文档对象
CClientLoginDlg loginDlg(this);//登录对话框
if (IDOK == loginDlg.DoModal())
{
//服务器端口
SHORT shServPort = (SHORT)atoi((LPCTSTR)loginDlg.m_strServerPort);
m_servAddr.sin_family = AF_INET; //地址家族
m_servAddr.sin_addr.S_un.S_addr = htonl((u_long)loginDlg.m_dwIP); //地址
m_servAddr.sin_port = htons(shServPort); //端口
//连接服务器
while (!m_pSocket->Connect((SOCKADDR*)&m_servAddr, sizeof(m_servAddr)))
{
if (AfxMessageBox(_T("连接服务器失败,是否再次尝试连接。"),MB_YESNO) == IDNO)
{
return FALSE;
}
}
//获取本机套接字地址
SOCKADDR_IN hostAddr;
int nSockaddLen = sizeof(SOCKADDR_IN);
m_pSocket->GetSockName((SOCKADDR*)&hostAddr, &nSockaddLen);
m_hostAddr.sin_addr.S_un.S_addr = hostAddr.sin_addr.S_un.S_addr;//主机地址
CChatPacket packet; //数据包
packet.m_type = CChatPacket::USERLIST; //类型
packet.m_UserInfo.m_strName = loginDlg.m_strName; //名称
packet.m_UserInfo.m_strPassword = loginDlg.m_strPassword; //密码
packet.m_UserInfo.m_eStatus = CUserInfo::LOGIN; //用户状态
packet.m_UserInfo.m_lIP = hostAddr.sin_addr.S_un.S_addr; //IP
packet.m_UserInfo.m_nPort = m_hostAddr.sin_port; //端口
packet.m_UserInfo.m_time = CTime::GetCurrentTime(); //登录时间
m_UserInfo = packet.m_UserInfo; //保存用户信息
//请求用户列表
SendPacket(packet);
return TRUE;
}else
{
return FALSE;
}
}
/*
* 等待接收消息
*/
void CClientDlg::ProcessPendingRead(void)
{
do
{
ReadPacket();
if (m_pSocket == NULL)
return;
}
while(!m_pArchiveIn->IsBufferEmpty());//将缓冲区数据全部读入
}
/*
* 接收服务器数据
*/
void CClientDlg::ReadPacket(void)
{
CObList obList;//临时链表
CChatPacket packet;//数据包
packet.m_pUserList = &obList;
TRY
{
packet.Serialize(*m_pArchiveIn); //接收数据
if (packet.m_type == CChatPacket::MESSAGE) //离线消息
{
ShowOfflineMsg(&packet);
}else if (packet.m_type == CChatPacket::USERLIST) //用户列表
{
CreateUserList(&obList); //创建用户链表
UpdateClientListCtl(); //更新用户界面
m_UserInfo.m_eStatus = CUserInfo::ONLINE; //修改用户状态
}else if (packet.m_type == CChatPacket::SERVERMSG) //登录失败消息
{
MessageBox(packet.m_strMsg, "Server Messages", MB_OK);
EndDialog(IDCANCEL); //退出
}
}
CATCH (CFileException, e)
{
MessageBox(_T("读入chatter链表错误"));
}
END_CATCH
}
/*
* 更新用户列表
*/
void CClientDlg::UpdateClientListCtl(void)
{
//删除原来用户链表
m_ctlUserList.DeleteAllItems();
POSITION pos;
int nIndex = 0;
for(pos = m_UserList.GetHeadPosition(); pos != NULL;)//遍历整个链表
{
//获取每个用户信息
CUserInfo userInfo;
userInfo =*(CUserInfo*)m_UserList.GetNext(pos);
LVITEM lvItem;
lvItem.mask = LVIF_TEXT |LVIF_IMAGE |LVIF_PARAM;
lvItem.iItem = nIndex;
lvItem.iSubItem = 0;
lvItem.pszText = userInfo.m_strName.GetBuffer(10);
if (userInfo.m_eStatus == CUserInfo::ONLINE) //在线状态
{
lvItem.iImage = CUserInfo::ONLINE;
}else
{
lvItem.iImage = CUserInfo::OFFLINE; //离线状态
}
lvItem.lParam = (LPARAM)userInfo.m_strName.GetBuffer(10);
m_ctlUserList.InsertItem(&lvItem); //添加数据项
userInfo.m_strName.ReleaseBuffer();
nIndex++;
}
}
/*
* 显示离线消息
*/
void CClientDlg::ShowOfflineMsg(CChatPacket *pPacket)
{
CChatDlg chatDlg(this, pPacket);//创建显示离线消息对话框
chatDlg.DoModal();
SaveChatLog(&chatDlg); //保存聊天记录
}
/*
* 发送数据
*/
void CClientDlg::SendPacket(CChatPacket &packet)
{
m_pArchiveOut = new CArchive(m_pFile,CArchive::store); //初始化写入流
if (NULL != m_pArchiveOut)
{
TRY
{
packet.Serialize(*m_pArchiveOut);
m_pArchiveOut->Flush();
}
CATCH(CFileException, e)
{
m_pArchiveOut->Abort();
delete m_pArchiveOut;
m_pArchiveOut = NULL;
MessageBox(_T("发送数据包错误"));
}
END_CATCH
}
if (NULL != m_pArchiveOut)
{
delete m_pArchiveOut;
m_pArchiveOut = NULL;
}
}
void CClientDlg::CreateUserList(CObList *pObList)
{
POSITION pos = NULL;
CUserInfo *pUserInfo = NULL;
//删除原来链表
for (pos = m_UserList.GetHeadPosition(); NULL != pos;)
{
pUserInfo = (CUserInfo*)m_UserList.GetNext(pos);
delete pUserInfo;
pUserInfo = NULL;
}
m_UserList.RemoveAll();
//复制链表
CUserInfo *pNewUserInfo = NULL;
for (pos = pObList->GetHeadPosition(); NULL != pos;)
{
pUserInfo = (CUserInfo*)pObList->GetNext(pos);
pNewUserInfo= new CUserInfo();
pNewUserInfo->m_strName = pUserInfo->m_strName; //名称
pNewUserInfo->m_eStatus = pUserInfo->m_eStatus; //状态
pNewUserInfo->m_lIP = pUserInfo->m_lIP; //地址
pNewUserInfo->m_nPort = pUserInfo->m_nPort; //端口
m_UserList.AddTail(pNewUserInfo); //加入链表
delete pUserInfo;
pUserInfo = NULL;
}
pObList->RemoveAll();
}
/*
* 双击列表视图控件消息消息响应函数
*/
void CClientDlg::OnDblclkClientList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView->iItem != -1)
{
int nSelectItem = pNMListView->iItem;
CString strPeerName = m_ctlUserList.GetItemText(nSelectItem, 0);
CreateChatDlg(strPeerName);
}
*pResult = 0;
}
/*
* 创建聊天窗口
*/
void CClientDlg::CreateChatDlg( CString strPeerName )
{
//不给自己发送消息
if (strPeerName == m_UserInfo.m_strName)
return;
//在用户链表中找到该用户
CUserInfo *pPeerInfo = NULL;
POSITION pos = NULL;
BOOL bFinder = FALSE;
for (pos = m_UserList.GetHeadPosition(); NULL != pos; )
{
pPeerInfo = (CUserInfo*)m_UserList.GetNext(pos);
if (pPeerInfo->m_strName == strPeerName)
{
bFinder = TRUE;
break;
}
}
ASSERT(TRUE == bFinder);//找到该用户
if (pPeerInfo->m_eStatus == CUserInfo::ONLINE) //用户在线
{
CChatDlg chatDlg(this, *pPeerInfo, m_UserInfo); //定义对话框
if (chatDlg.ConnectToPeer()) //连接对方
{
chatDlg.DoModal();//显示聊天窗口
//保存聊天记录
SaveChatLog(&chatDlg);
}
}else//发送离线消息
{
CChatDlg chatDlg(this, *pPeerInfo, m_UserInfo);//定义对话框
chatDlg.DoModal();//显示聊天窗口
//离线数据包
CChatPacket packet;
packet.m_type = CChatPacket::MESSAGE; //类型
packet.m_UserInfo = m_UserInfo; //发送离线消息的用户信息
packet.m_OfflineUserInfo = *pPeerInfo; //接收离线消息的用户信息
packet.m_strMsg = chatDlg.m_strRec; //离线消息
packet.m_time = CTime::GetCurrentTime();//发送消息的时间
//发送离线数据包
SendPacket(packet);
//保存聊天记录
SaveChatLog(&chatDlg);
}
}
/*
* 开始监听
*/
BOOL CClientDlg::BegingListen( void )
{
m_pListenSocket = new CListenSocket(this); //创建监听对象
if (m_pListenSocket->Create()) //创建绑定套接字
{
if (!m_pListenSocket->Listen()) //开始监听
{
delete m_pListenSocket;
m_pListenSocket = NULL;
AfxMessageBox(_T("创建套接字失败!"));
return FALSE;
}
}
//获取地址信息
SOCKADDR_IN hostAddr;
int hostAddrLen = sizeof(SOCKADDR_IN);
m_pListenSocket->GetSockName((SOCKADDR*)&hostAddr,&hostAddrLen);
m_hostAddr.sin_port = hostAddr.sin_port;//保存监听端口
return TRUE;
}
/*
*接受对方的连接
*/
void CClientDlg::ProcessPendingAccept(void)
{
CChatDlg chatDlg(this); //聊天对话框
chatDlg.InitSocket(); //创建CChatSocket对象
//接受对方连接
CChatSocket *pChatSocket = chatDlg.GetChatSocket();
SOCKADDR_IN peerAddr;
int socketLen = sizeof(SOCKADDR_IN);
m_pListenSocket->Accept(*pChatSocket,(SOCKADDR*)&peerAddr, &socketLen);
//在用户链表中查找该用户
POSITION pos;
CUserInfo *pPeerInfo = NULL;
BOOL bFinder = FALSE;
for (pos = m_UserList.GetHeadPosition(); NULL != pos;)
{
pPeerInfo = (CUserInfo*)m_UserList.GetNext(pos);
if (pPeerInfo->m_lIP == peerAddr.sin_addr.S_un.S_addr &&//找到该用户
pPeerInfo->m_eStatus == CUserInfo::ONLINE)
{
bFinder = TRUE;
break;
}
}
ASSERT(TRUE == bFinder);
//获取对方信息
CUserInfo peerInfo;
peerInfo.m_strName = pPeerInfo->m_strName; //名字
peerInfo.m_eStatus = pPeerInfo->m_eStatus; //状态
peerInfo.m_lIP = peerAddr.sin_addr.S_un.S_addr; //地址
peerInfo.m_nPort = peerAddr.sin_port; //端口
chatDlg.SetPeerInfor(peerInfo); //设置对方信息
chatDlg.SetUserInfor(m_UserInfo); //设置用户信息
chatDlg.InitArchive(); //设置套接字读入文档
//打开聊天对话框
chatDlg.DoModal();
//保存聊天记录
SaveChatLog(&chatDlg);
}
/*
* 初始化列表视图控件
*/
BOOL CClientDlg::InitListCtrlSetting( void )
{
//创建图标列表
m_pImageList = new CImageList();
//32*32,8位,初始化为2个图标,每次增长2个图标
m_pImageList->Create(32, 32, ILC_COLOR8|ILC_MASK, 2, 2);
CWinApp* pApp = AfxGetApp(); //获得应用程序指针
HICON hIconOnline = pApp->LoadIcon(IDI_CHATTER_ONLINE); //在线图标
HICON hIconOffline = pApp->LoadIcon(IDI_CHATTER_OFFLINE); //离线图标
m_pImageList->Add(hIconOnline); //加入图标
m_pImageList->Add(hIconOffline); //加入图标
//将图标列表赋值给列表控件
m_ctlUserList.SetImageList(m_pImageList, LVSIL_SMALL);
//设置列表头
m_ctlUserList.InsertColumn(0,_T("Chatter"),LVCFMT_IMAGE|LVCFMT_LEFT);
//设置第一列的宽度为这个列表控件的宽度
CRect rect;
m_ctlUserList.GetClientRect(&rect);
m_ctlUserList.SetColumnWidth(0,rect.Width());
return TRUE;
}
/*
* 保存聊天记录
*/
void CClientDlg::SaveChatLog(CChatDlg *pChatDlg)
{
//没有聊天消息
if (pChatDlg->m_strRec.IsEmpty())
return;
//保存聊天记录
char appPath[256];
GetCurrentDirectory(256,appPath); //取得应用程序当前路径
CString filePath; //文件名
filePath.Format("%s",appPath);
filePath +="\\";
filePath += CHATLOG;
TRY
{
CFile file;
if (!file.Open(filePath, CFile::modeReadWrite)) //文件已经存在
{
file.Open(filePath, CFile::modeCreate|CFile::modeReadWrite);//新建文件
}else
{
file.SeekToEnd();//移动文件指针到文件尾部
}
int nRecLen = pChatDlg->m_strRec.GetLength(); //聊天记录长度
file.Write(pChatDlg->m_strRec, nRecLen); //写入数据
file.Flush();
file.Close(); //关闭文件
}
CATCH(CFileException, e)
{
MessageBox(_T("保存聊天记录异常"),MB_OK, -1);
}
END_CATCH
}
ClientLogin.cpp
// ClientLogin.cpp : implementation file
//
#include "stdafx.h"
#include "Client.h"
#include "ClientLogin.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CClientLogin dialog
CClientLoginDlg::CClientLoginDlg(CWnd* pParent /*=NULL*/)
: CDialog(CClientLoginDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CClientLogin)
m_strServerPort.Format("%d",SERVERPORT);
m_strName = _T("myChat");
m_strPassword = _T("");
//}}AFX_DATA_INIT
}
void CClientLoginDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CClientLogin)
DDX_Control(pDX, IDC_CLIENT_IPADDRESS, m_serverIP);
DDX_Text(pDX, IDC_CLIENT_PASSWORD_EDIT, m_strPassword);
DDV_MaxChars(pDX, m_strPassword, 10);
DDX_Text(pDX, IDC_CLIENT_NAME_EDIT, m_strName);
DDV_MaxChars(pDX, m_strName, 10);
DDX_Text(pDX, IDC_CLIENT_PORT_EDIT, m_strServerPort);
DDV_MaxChars(pDX, m_strServerPort, 5);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CClientLoginDlg, CDialog)
//{{AFX_MSG_MAP(CClientLogin)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClientLogin message handlers
BOOL CClientLoginDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_serverIP.SetAddress(127, 0, 0, 1);
return TRUE;
}
void CClientLoginDlg::OnOK(void)
{
UpdateData(TRUE);
m_serverIP.GetAddress(m_dwIP);
CDialog::OnOK();
}
ListenSocket.cpp
// ListenSocket.cpp : implementation file
//
#include "stdafx.h"
#include "client.h"
#include "ListenSocket.h"
#include "ClientDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CListenSocket
CListenSocket:: CListenSocket(CClientDlg *pClientDlg)
{
m_pClientDlg = pClientDlg;
}
CListenSocket::~CListenSocket()
{
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CListenSocket, CSocket)
//{{AFX_MSG_MAP(CListenSocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CListenSocket member functions
void CListenSocket::OnAccept(int nErrorCode)
{
m_pClientDlg->ProcessPendingAccept();
}
UserInfo.cpp
// UserInfo.cpp: implementation of the CUserInfo class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "UserInfo.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//IMPLEMENT_DYNCREATE(CUserInfo, CObject)
IMPLEMENT_SERIAL(CUserInfo, CObject, 1)//new一个对象然后调用默认构造函数->init
CUserInfo::CUserInfo()
{
Init();
}
CUserInfo::~CUserInfo()
{
}
/*
* 初始化
*/
void CUserInfo::Init(void)
{
m_strName = _T("");
m_strPassword = _T("");
m_eStatus = UNKNOWN;
m_lIP = 0;
m_nPort = 0;
m_time = CTime::GetCurrentTime();
}
/*
* 序列化
*/
void CUserInfo::Serialize(CArchive& ar)
{
CObject::Serialize(ar);//调用基类的序列化函数
if (ar.IsStoring())//发送数据
{
ar << m_strName; //名字
ar << m_strPassword; //密码
WORD byStatus = 0;
byStatus = (WORD)m_eStatus;
ar << byStatus; //状态
ar << m_lIP; //地址
ar << m_nPort; //端口
long lTime = m_time.GetTime(); //日期和时间
ar << lTime;
}else
{
ar >> m_strName; //名字
ar >> m_strPassword; //密码
WORD byStatus = 0;
ar >> byStatus;
m_eStatus = (USERSTATUE)byStatus;//状态
ar >> m_lIP; //地址
ar >> m_nPort; //端口
long lTime;
ar >> lTime; //日期和时间
m_time = CTime((time_t)lTime);
}
}
/*
* 赋值构造函数
*/
CUserInfo &CUserInfo::operator = (const CUserInfo &userInfo)
{
m_strName = userInfo.m_strName;
m_strPassword = userInfo.m_strPassword;
m_eStatus = userInfo.m_eStatus;
m_lIP = userInfo.m_lIP;
m_nPort = userInfo.m_nPort;
m_time = userInfo.m_time;
return *this;
}
/*
* 赋值构造函数
*/
CUserInfo::CUserInfo(const CUserInfo& userInfo)
{
m_strName = userInfo.m_strName;
m_strPassword = userInfo.m_strPassword;
m_eStatus = userInfo.m_eStatus;
m_lIP = userInfo.m_lIP;
m_nPort = userInfo.m_nPort;
m_time = userInfo.m_time;
}
《WindowsSocket網路程式設計教學》