📄 client.cpp
字号:
#include <afxwin.h>
#include <afxcmn.h>
#include <afxdlgs.h>
#include <afxsock.h>
#include "setup.h"
#include "resource.h"
//
/****************************************************************************
* 显示错误信息
* 输入参数
* hDlg : 窗口的Handle
* nErrorCode : 调用AsyncSocket成员函数时, 发生错误的错误值
*****************************************************************************/
void ShowErrorMessage(HWND hDlg, int nErrorCode)
{
LPVOID lpMsgBuf;
//
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
nErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
::MessageBox(hDlg, (LPCTSTR)lpMsgBuf, "错误", MB_OK | MB_ICONSTOP);
LocalFree(lpMsgBuf);
};
//
/*****************************************************************************
* 主程序对话框的类
*****************************************************************************/
class CClientDlg : public CDialog
{
public:
CClientDlg(CWnd *pParent = NULL);
//
virtual BOOL OnInitDialog();
afx_msg void OnSendbtn();
afx_msg void OnSetupConnect();
afx_msg void OnDisconnect();
afx_msg void OnDestroy();
afx_msg void OnChange();
//
void OnReceive(int nErrorCode); // 接收传来信息时的处理函数
//
struct _tagAsyncSocket : public CAsyncSocket
// 建立一个结构使这个结构能继承CAsyncSocket类,
// 此结构主要是用来 Overlap Receive() 这个CAsyncSocket 的函数成员
{
_tagAsyncSocket(CClientDlg *pSocket) { m_pSocket = pSocket; };
// 结构的建构者, 接受一个CClientDlg 类指针的参数
virtual void OnReceive(int nErrorCode)
{
if (nErrorCode == 0)
// 如果没有发生错误, 则调用CClientDlg的函数成员OnReceive().
m_pSocket->OnReceive(nErrorCode);
else
ShowErrorMessage(::GetDesktopWindow(), GetLastError());
};
CClientDlg *m_pSocket;
};
//
protected:
CString m_lpszName; // 用户名称
CRichEditCtrl *m_pListmsg; // 显示传来的信息
CRichEditCtrl *m_pSendmsg; // 传送的信息
HICON m_hIcon; // Icon 的Handle
struct _tagAsyncSocket *m_pSocket;
// CAsyncSocket的对象, 用来接收连线的状态及信息
//
void SetupIcon(BOOL bIcon = FALSE); // 更改Icon的处理程序
//
DECLARE_MESSAGE_MAP()
};
//
CClientDlg::CClientDlg(CWnd *pParent)
: CDialog(IDD_CLIENTDLG, pParent)
{
m_lpszName = CString(_T(""));
m_pListmsg = NULL;
m_pSendmsg = NULL;
m_pSocket = NULL;
m_hIcon = NULL;
};
//
BEGIN_MESSAGE_MAP(CClientDlg, CDialog)
ON_BN_CLICKED(IDC_SENDBTN, OnSendbtn)
ON_COMMAND(IDC_DISCONNECT, OnDisconnect)
ON_COMMAND(IDM_SETUPCONNECT, OnSetupConnect)
ON_WM_DESTROY()
ON_EN_CHANGE(IDC_SENDMSG, OnChange)
END_MESSAGE_MAP()
//
BOOL CClientDlg::OnInitDialog()
{
DWORD dwStyle = ::GetClassLong(GetSafeHwnd(), GCL_STYLE);
dwStyle |= CS_NOCLOSE;
::SetClassLong(GetSafeHwnd(), GCL_STYLE, dwStyle);
//
SetupIcon();
m_pListmsg = (CRichEditCtrl*)GetDlgItem(IDC_LISTMSG);
// 取得CRichEditCtrl 的控件
m_pSendmsg = (CRichEditCtrl*)GetDlgItem(IDC_SENDMSG);
//
m_pSendmsg->SetEventMask(m_pSendmsg->GetEventMask() | ENM_CHANGE);
// 设置处理CHANGE的事件
//
return TRUE;
};
//
void CClientDlg::OnDestroy()
{
if (m_pSocket) // 如果已经连线
{
m_pSocket->Close(); // 关闭连线并释放资源
delete m_pSocket;
m_pSocket = NULL;
};
};
//
/********************************************************************************
* 终止目前的连线
* 输入参数 : 无
*********************************************************************************/
void CClientDlg::OnDisconnect()
{
if (m_pSocket)
{
m_pSocket->ShutDown();
// 停止连线并释放资源, Socket 并未关闭
delete m_pSocket;
m_pSocket = NULL;
};
SetupIcon();
CMenu *menu = GetMenu(); // 更改选项(MENU) 的状态
if (menu)
{
menu->EnableMenuItem(IDM_SETUPCONNECT, MF_BYCOMMAND | MF_ENABLED);
};
CString str = _T("Client - [未连线...]");
SetWindowText(str);
m_pSendmsg->SetOptions(ECOOP_SET, ECO_READONLY);
// 因为未连线,所以将的CRichEditCtrl控件设成惟读
};
//
/*******************************************************************************
* 设置连线(主机及连接端口)
* 输入参数 : 无
********************************************************************************/
void CClientDlg::OnSetupConnect()
{
CSetupDlg dlg;
//
if (dlg.DoModal() == IDOK) // 如果已设置完成
{
m_pSocket = new _tagAsyncSocket(this);
// 配置 ASyncSocket 的资源
if (!m_pSocket->Create())
// 在客户端使用时可直接使用内定值
// 如果无法开启WinSock
{
delete m_pSocket; // 释放之前所配置的资源
m_pSocket = NULL;
ShowErrorMessage(GetSafeHwnd(), GetLastError());
return;
};
SetupIcon(TRUE); //更换图标, 代表已经连线
m_pSocket->Connect(dlg.m_lpszIPaddr, dlg.m_nPort);
// 连线至主机, 如果成功,此时对AsyncSocket类来说会触发 OnConnect()事件,
// 如果我们要处理的话, 那必须调用Overlap OnConnect()事件,
// 此事件如同其他AsyncSocket的成员函数都属于AsyncSocket的 Callback 函数
m_lpszName = dlg.m_lpszName;
CString str = _T("Client - ");
str += dlg.m_lpszName + _T(" [交谈中...]");
SetWindowText(str);
CMenu *menu = GetMenu();
if (menu)
{
menu->EnableMenuItem(IDM_SETUPCONNECT, MF_BYCOMMAND | MF_GRAYED);
};
m_pSendmsg->SetOptions(ECOOP_XOR, ECO_READONLY);
// 已经连线,所以取消CRichEditCtrl控件的只读设置
};
};
//
/********************************************************************************
* 按下传送信息的按钮
* 输入参数 : 无
*********************************************************************************/
void CClientDlg::OnSendbtn()
{
int len; // 所要传送信息的长度
int sent; // 真正传送的长度
CString s, s1 = m_lpszName;
//
m_pSendmsg->GetWindowText(s);
s1 += _T(" Say :\n") + s;
len = s1.GetLength();
sent = m_pSocket->Send((LPCTSTR)s1, len);
// 调用 CAsyncSocket 的成员函数 Sned()来传送信息
// 如果成功传送, 则传回传送的字符数,
// 否则传回 SOCKET_ERROR ,此时可用 GetLastError() 取得信息的错误值
if (sent == SOCKET_ERROR)
{
ShowErrorMessage(GetSafeHwnd(), GetLastError());
return;
}
else
{
m_pListmsg->GetWindowText(s1);
if (s1.IsEmpty())
s1 = CString(_T("----->")) + s;
else
s1 += CString(_T("\n----->")) + s;
m_pListmsg->SetWindowText(s1); // 将传送的信息显示在窗口中
m_pSendmsg->SetWindowText(_T(""));
};
};
//
/*************************************************************************************
* 接收信息的处理程序 : 此函数被 Overlap 函数 OnReceive() 所调用
* 输入参数 :
* nErrorCode : 0 为正确
* 非 "零" 值错误
* 注意 : 此参数在此函数中不需处理
**************************************************************************************/
void CClientDlg::OnReceive(int nErrorCode)
{
_TCHAR *buf;
int size = 1024;
int nRtn;
CString str;
//
buf = new _TCHAR[1024];
::memset(buf, 0, sizeof(_TCHAR)*1024);
nRtn = m_pSocket->Receive(buf, size);
// 设置接受信息的最大长度
// 如果成功则传回接收的数据长度, 否则传回 SOCKET_ERROR
if (nRtn == SOCKET_ERROR)
{
ShowErrorMessage(GetSafeHwnd(), GetLastError());
}
else
{
m_pListmsg->GetWindowText(str);
str += CString(_T("\n")) + CString(buf);
m_pListmsg->SetWindowText(str);
// 将接收的信息加至窗口中
};
};
//
/**********************************************************************************
* 设置连线时的图标
* 输入参数 :
* bIcon : TRUE 为连线中的图标
* : FALSE 未连线, 此值为内定值
***********************************************************************************/
void CClientDlg::SetupIcon(BOOL bIcon)
{
LPCTSTR id = (bIcon) ? MAKEINTRESOURCE(IDI_NET08)
: MAKEINTRESOURCE(IDI_TRFFC10C);
m_hIcon = ::LoadIcon(AfxGetInstanceHandle(), id);
SetIcon(m_hIcon, FALSE);
SetIcon(m_hIcon, TRUE);
};
//
/*********************************************************************************
* 输入的信息窗口内容被改变时
* 输入参数 : 无
**********************************************************************************/
void CClientDlg::OnChange()
{
CString str;
//
m_pSendmsg->GetWindowText(str);
str.IsEmpty()? GetDlgItem(IDC_SENDBTN)->EnableWindow(FALSE)
: GetDlgItem(IDC_SENDBTN)->EnableWindow();
};
//
/**********************************************************************************
* 主程序的类
***********************************************************************************/
class CClientApp : public CWinApp
{
public:
CClientApp() {};
//
BOOL InitInstance()
{
AfxInitRichEdit();
// 因为在程序中使用了 CRichEditCtrl 这个控件所以要先调用这个API
// 注意:这个API要放在 InitInstance这虚拟函数中
AfxSocketInit();
// 使用 Winsock API
//
CDialog *dlg = new CClientDlg;
m_pMainWnd = dlg;
dlg->DoModal();
//
return TRUE;
};
} theApp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -