【MFC实战项目】No.2 网络调试助手
程序员文章站
2022-05-23 12:47:04
...
前 言
基于MFC对话框开发了一款网络调试助手,只实现了TCP,还未实现UDP。某些功能与前面开发的串口调试助手原理相同,这里就不打算再做了。
VS版本:
Microsoft Visual Studio 2008 9.0.30729.1 SP
目 录
1. 软件成果展示
这里同样使用铭心软体工作室出品的网络调试助手(V 3.7)进行测试。接着开软件分TCP服务器和TCP客户端进行测试。项目下载地址附文末。
2. 源代码
(1)头文件 NetAssistDlg.h
// NetAssistDlg.h : 头文件
//
#pragma once
#include "afxwin.h"
#include "afxcmn.h"
#include <ZySocket.h>
#include <vector>
using namespace std;
// CNetAssistDlg 对话框
class CNetAssistDlg : public CDialog
{
// 构造
public:
CNetAssistDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_NETASSIST_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
/* ==================== function ======================= */
public:
void InitMenu(); // 初始化菜单栏
void InitNetSetting(); // 初始化网络设置
void SetNetSetCtrlEnable(BOOL Enable = TRUE); // 设置网络设置控件是否可编辑
void ResizeRecvCtrl(BOOL bSmall = FALSE); // 设置网络数据接收区控件大小
void SetSerCtrlAreaVisable(BOOL bVisable = FALSE); // 隐藏与显示控件
void SetCliCtrlAreaVisable(BOOL bVisable = FALSE); // 隐藏与显示控件
void SetConectState(BOOL bConnect = FALSE); // 设置连接状态
void SetRecvNum(); // 接收字节数
void SetSendNum(); // 发送字节数
/* 连接/断开连接 */
void UDPConOrDis(); // UDP
void TCPSerConOrDis(); // TCP Server
BOOL CheckPortIsOccupy(DWORD dwPort); // 检测TCP端口是否被占用
void TCPCliConOrDis(); // TCP Client
CString GetIPAddr(CIPAddressCtrl *ipCtrl); // 获取界面上的IP地址
DWORD GetPort(CEdit *edtCtrl); // 获取界面上的端口号
void SetRecvData(CString strData); // 设置接收区内容
/* 发送消息 */
void SendDataUdp(char *pszData); // UDP发送数据
void SendDataTcpServer(char *pszData); // TCP Server发送数据
void SendDataTcpClient(char *pszData); // TCP CLient发送数据
UINT findSocketInVector(SOCKET newSocket); // 容器中查找端口号
LRESULT OnShowTask(WPARAM wParam, LPARAM lParam); // 托盘图标显示
public:
CMenu m_Menu;
int m_nRecvCount;
int m_nSendCount;
BOOL m_bConnect;
// 套接字
CServerSocket m_server;
CClientSocket m_client;
vector<SOCKET> m_VecSocket; // 保存客户端套接字
public:
CComboBox m_cmb_proToCol; // 协议类型
CIPAddressCtrl m_ipa_lochostIP; // 本地IP地址/服务器IP地址
CStatic m_pic_flag; // 连接/未连接状态标识图片控件
CEdit m_edt_locHostPort; // 本地端口/服务器端口
CButton m_btn_conDis; // 连接/断开连接按钮
CStatic m_edt_connectState; // 状态栏连接/未连接标识
CStatic m_edt_sendNum; // 状态栏发送字节数
CStatic m_edt_recvNum; // 状态栏接收字节数
CEdit m_edt_recvData; // 网络数据接收
CEdit m_edt_sendData; // 发送区
CButton m_ckb_send3; // 发送完自动清空
CButton m_ckb_recv2; // 自动换行显示
CStatic m_stc_localIP; // 本地IP
CStatic m_stc_localPort; // 本地端口
CStatic m_stc_tarHost; // 目标地址
CStatic m_stc_tarPort; // 目标端口
CIPAddressCtrl m_ipa_tarHostIP; // 目标IP地址
CEdit m_edt_tarPort; // 目标端口
CStatic m_gpb_recvData; // 网络数据接收
CStatic m_stc_conTar; // 连接对象
CComboBox m_cmb_conTar; // 连接对象
CStatic m_stc_newCon; // 状态栏 客户端连接/断开
public:
afx_msg void OnBnClickedBtnClearnum(); // 状态栏复位按钮
afx_msg void OnBnClickedBtnClrecv(); // 接受区清除显示
afx_msg void OnBnClickedBtnClsend(); // 清除输入
afx_msg void OnBnClickedBtnCondis(); // 连接/断开连接
afx_msg void OnCbnSelchangeCmbProtocol(); // 协议类型改变
afx_msg void OnClose(); // 窗口关闭响应函数
afx_msg void OnBnClickedBtnSenddata(); // 发送数据
afx_msg void OnMenuAboutBtnClicked(); // 菜单栏关于按钮
afx_msg void OnMenuexitbtn(); // 菜单栏退出按钮
};
(2)源文件 NetAssistDlg.cpp
// NetAssistDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "NetAssist.h"
#include "NetAssistDlg.h"
#include "Iphlpapi.h"
#include <malloc.h>
#include "Winsock2.h"
#include <stdlib.h>
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
NOTIFYICONDATA g_Tray; // 托盘图标全局变量
CString g_strAddrAndPort;
// 套接字回调函数
// 服务器端
void SNewSocketCallBack(const SOCKADDR_IN *szSockAddr, SOCKET newSocket, void *pUserParam); // 客户端连接
void SNewMsgCallBack(const SOCKADDR_IN *pSockAddr, const SOCKET_HAND *pSocketHand, const SOCKET_DATA *pData, void *pUserParam); // 接收消息
void SCloseSocketCallBack(const char *szSocket, SOCKET newSocket, void *pUserParam); // 客户端断开连接
// 客户端
void CNewMsgCallBack(long nPort, SOCKET svrSocket, const SOCKET_DATA *pData, void *pUserParam); // 接收消息
void CDisconnetCallBack(void *pUsr); // 服务器断开连接
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CNetAssistDlg 对话框
CNetAssistDlg::CNetAssistDlg(CWnd* pParent /*=NULL*/)
: CDialog(CNetAssistDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CNetAssistDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_CMB_PROTOCOL, m_cmb_proToCol);
DDX_Control(pDX, IDC_IPA_LOCALHOSTIP, m_ipa_lochostIP);
DDX_Control(pDX, IDC_STC_ICO, m_pic_flag);
DDX_Control(pDX, IDC_EDT_LOCALHOSTPOR, m_edt_locHostPort);
DDX_Control(pDX, IDC_BTN_CONDIS, m_btn_conDis);
DDX_Control(pDX, IDC_STC_STATE, m_edt_connectState);
DDX_Control(pDX, IDC_STC_SENDNUM, m_edt_sendNum);
DDX_Control(pDX, IDC_STC_RECVNUM, m_edt_recvNum);
DDX_Control(pDX, IDC_EDT_RECVDATA, m_edt_recvData);
DDX_Control(pDX, IDC_EDT_SENDDATA, m_edt_sendData);
DDX_Control(pDX, IDC_CK_SEND3, m_ckb_send3);
DDX_Control(pDX, IDC_CK_RECV2, m_ckb_recv2);
DDX_Control(pDX, IDC_STC_LOACLHOSTIP, m_stc_localIP);
DDX_Control(pDX, IDC_STC_LOCALHOSTPOR, m_stc_localPort);
DDX_Control(pDX, IDC_STC_TARHOST, m_stc_tarHost);
DDX_Control(pDX, IDC_STC_TARPORT, m_stc_tarPort);
DDX_Control(pDX, IDC_IPA_TARHOST, m_ipa_tarHostIP);
DDX_Control(pDX, IDC_EDT_TARPORT, m_edt_tarPort);
DDX_Control(pDX, IDC_STATIC_SERAILDATARECV, m_gpb_recvData);
DDX_Control(pDX, IDC_EDT_TARPORT, m_edt_tarPort);
DDX_Control(pDX, IDC_STC_CONTAR, m_stc_conTar);
DDX_Control(pDX, IDC_CMB_CONTAR, m_cmb_conTar);
DDX_Control(pDX, IDC_STC_NEWCONECT, m_stc_newCon);
}
BEGIN_MESSAGE_MAP(CNetAssistDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BTN_CLEARNUM, &CNetAssistDlg::OnBnClickedBtnClearnum)
ON_BN_CLICKED(IDC_BTN_CLRECV, &CNetAssistDlg::OnBnClickedBtnClrecv)
ON_BN_CLICKED(IDC_BTN_CLSEND, &CNetAssistDlg::OnBnClickedBtnClsend)
ON_BN_CLICKED(IDC_BTN_CONDIS, &CNetAssistDlg::OnBnClickedBtnCondis)
ON_CBN_SELCHANGE(IDC_CMB_PROTOCOL, &CNetAssistDlg::OnCbnSelchangeCmbProtocol)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_BTN_SENDDATA, &CNetAssistDlg::OnBnClickedBtnSenddata)
ON_COMMAND(ID_32773, &CNetAssistDlg::OnMenuAboutBtnClicked)
ON_MESSAGE(WM_SHOWTASK, &CNetAssistDlg::OnShowTask) // 托盘图标
ON_COMMAND(ID_MenuExitBtn, &CNetAssistDlg::OnMenuexitbtn)
END_MESSAGE_MAP()
// CNetAssistDlg 消息处理程序
LRESULT CNetAssistDlg::OnShowTask(WPARAM wParam, LPARAM lParam)
{
if (wParam != IDR_MAINFRAME)
return 1;
if (lParam == WM_LBUTTONDBLCLK) // 左键点击托盘
{
this->ShowWindow(SW_SHOWNORMAL);
this->SetForegroundWindow();
Shell_NotifyIcon(NIM_DELETE, &g_Tray);
}
if (lParam == WM_RBUTTONDBLCLK) // 右键点击
{
// 右键弹出托盘菜单
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopUp = menu.GetSubMenu(0);
CPoint pt;
GetCursorPos(&pt);
SetForegroundWindow();
pPopUp->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
PostMessage(WM_NULL, 0, 0);
}
return 0;
}
BOOL CNetAssistDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
/************************************************************************/
/* */
/************************************************************************/
this->InitMenu();
this->InitNetSetting();
this->SetConectState(FALSE);
m_ckb_recv2.SetCheck(TRUE);
m_nSendCount = 0;
m_nRecvCount = 0;
m_bConnect = FALSE;
// 设置套接字回调函数
m_server.m_pUserParam = this;
m_server.NewSocketCallBack = SNewSocketCallBack;
m_server.NewMsgCallBack = SNewMsgCallBack;
m_server.CloseSocketCallBack = SCloseSocketCallBack;
m_client.m_pUserParam = this;
m_client.NewMsgCallBack = CNewMsgCallBack;
m_client.DisconnetCall = CDisconnetCallBack;
ResizeRecvCtrl(FALSE);
SetSerCtrlAreaVisable(FALSE);
SetCliCtrlAreaVisable(FALSE);
m_cmb_conTar.AddString(_T("All Connections"));
m_cmb_conTar.SetCurSel(0);
/************************************************************************/
/* */
/************************************************************************/
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CNetAssistDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if (nID == SC_MINIMIZE) // 最小化窗口
{
g_Tray.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
g_Tray.hWnd = this->m_hWnd;
g_Tray.uID = IDR_MAINFRAME;
g_Tray.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
g_Tray.uCallbackMessage = WM_SHOWTASK; // 自定义的消息名称
g_Tray.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
lstrcpy(g_Tray.szTip, _T("网络调试助手 V1.0")); // 信息提示条
Shell_NotifyIcon(NIM_ADD, &g_Tray); // 在托盘区添加图标
ShowWindow(SW_HIDE); // 隐藏主窗口
return;
}
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CNetAssistDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
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;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CNetAssistDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
/************************************************************************/
/* */
/************************************************************************/
// 加载菜单项
void CNetAssistDlg::InitMenu()
{
m_Menu.LoadMenu( IDR_MENU1 );
this->SetMenu(&m_Menu);
}
void CNetAssistDlg::InitNetSetting()
{
// 协议类型
m_cmb_proToCol.AddString(_T("UDP"));
m_cmb_proToCol.AddString(_T("TCP Server"));
m_cmb_proToCol.AddString(_T("TCP Client"));
m_cmb_proToCol.SetCurSel(0);
// 本地IP地址
char *pszLocIp = m_server.GetLocalIP();
if (pszLocIp = "0.0.0.0")
pszLocIp = "127.0.0.1";
m_ipa_lochostIP.SetWindowText(pszLocIp);
// 本地端口号
m_edt_locHostPort.SetWindowText(_T("8080"));
}
// 设置网络设置控件是否可编辑
void CNetAssistDlg::SetNetSetCtrlEnable(BOOL Enable)
{
m_cmb_proToCol.EnableWindow(Enable);
m_ipa_lochostIP.EnableWindow(Enable);
m_edt_locHostPort.EnableWindow(Enable);
}
void CNetAssistDlg::ResizeRecvCtrl(BOOL bSmall)
{
// 获取消息接收区控件在窗口客户区的坐标及大小
CRect rectGpb, rectEdt;
m_gpb_recvData.GetWindowRect(&rectGpb);
m_edt_recvData.GetWindowRect(&rectEdt);
ScreenToClient(&rectGpb);
ScreenToClient(&rectEdt);
rectGpb.bottom = bSmall ? 425 : 495;
rectEdt.bottom = bSmall ? 420 : 490;
m_gpb_recvData.MoveWindow(&rectGpb);
m_edt_recvData.MoveWindow(&rectEdt);
}
void CNetAssistDlg::SetSerCtrlAreaVisable(BOOL bVisable)
{
m_stc_conTar.ShowWindow(bVisable);
m_cmb_conTar.ShowWindow(bVisable);
}
void CNetAssistDlg::SetCliCtrlAreaVisable(BOOL bVisable)
{
m_stc_tarHost.ShowWindow(bVisable);
m_stc_tarPort.ShowWindow(bVisable);
m_ipa_tarHostIP.ShowWindow(bVisable);
m_edt_tarPort.ShowWindow(bVisable);
if ( bVisable )
{
char *pszLocIp = m_server.GetLocalIP();
if (pszLocIp = "0.0.0.0")
pszLocIp = "127.0.0.1";
m_ipa_tarHostIP.SetWindowText(pszLocIp);
}
}
// 设置连接/断开图片标识,连接/断开按钮,状态栏连接状态
void CNetAssistDlg::SetConectState(BOOL bConnect)
{
HICON hIcon;
CString strBtn, strEdt;
if ( bConnect )
{
hIcon = AfxGetApp()->LoadIcon( IDI_ICN_CONNECT );
strBtn = _T("断开");
strEdt = _T("已连接");
}
else
{
hIcon = AfxGetApp()->LoadIcon( IDI_ICN_DISCON );
strBtn = _T("连接");
strEdt = _T("未连接");
}
m_pic_flag.SetIcon(hIcon);
m_btn_conDis.SetWindowText(strBtn);
m_edt_connectState.SetWindowText(_T("连接状态:") + strEdt);
}
// 接收字节数
void CNetAssistDlg::SetRecvNum()
{
CString strRecvNum;
strRecvNum.Format("%d", m_nRecvCount);
m_edt_recvNum.SetWindowText(_T("接收:") + strRecvNum);
}
// 发送字节数
void CNetAssistDlg::SetSendNum()
{
CString strSendNum;
strSendNum.Format("%d", m_nSendCount);
m_edt_sendNum.SetWindowText(_T("发送:") + strSendNum);
}
// 复位计数
void CNetAssistDlg::OnBnClickedBtnClearnum()
{
m_nRecvCount = 0;
m_nSendCount = 0;
SetRecvNum();
SetSendNum();
}
// 清除显示
void CNetAssistDlg::OnBnClickedBtnClrecv()
{
m_edt_recvData.SetWindowText(_T(""));
}
// 清除输入
void CNetAssistDlg::OnBnClickedBtnClsend()
{
m_edt_sendData.SetWindowText(_T(""));
}
// 协议类型改变
void CNetAssistDlg::OnCbnSelchangeCmbProtocol()
{
CString strLocalIP;
CString strLocalPort;
// TCP Client
if (m_cmb_proToCol.GetCurSel() == 2)
{
strLocalIP = _T("(2)服务器IP地址");
strLocalPort = _T("(3)服务器端口");
}
else
{
strLocalIP = _T("(2)本地IP地址");
strLocalPort = _T("(3)本地端口号");
}
m_stc_localIP.SetWindowText(strLocalIP);
m_stc_localPort.SetWindowText(strLocalPort);
}
/************************************************************************/
/* 连接/断开连接 */
/************************************************************************/
// 连接/断开
void CNetAssistDlg::OnBnClickedBtnCondis()
{
switch (m_cmb_proToCol.GetCurSel())
{
case 0: //UDP
UDPConOrDis();
break;
case 1: // TCP Server
TCPSerConOrDis();
break;
case 2: // TCP Client
TCPCliConOrDis();
break;
default:
break;
}
}
// UDP
void CNetAssistDlg::UDPConOrDis()
{
m_stc_tarHost.SetWindowText(_T("目标主机:"));
m_stc_tarPort.SetWindowText(_T("目标端口:"));
return;
}
// TCP Server
void CNetAssistDlg::TCPSerConOrDis()
{
// 已连接的情况下,断开连接
if ( m_bConnect )
{
if ( m_server.StopListening() ) // 停止监听
{
SetRecvData(_T("服务器已断开连接!\r\n"));
m_bConnect = FALSE;
SetConectState(FALSE);
m_cmb_conTar.ResetContent();
m_cmb_conTar.AddString(_T("All Connections"));
m_cmb_conTar.SetCurSel(0);
SetNetSetCtrlEnable(TRUE);
ResizeRecvCtrl(FALSE);
SetSerCtrlAreaVisable(FALSE);
}
else
{
SetRecvData(_T("服务器断开连接错误!\r\n"));
}
return;
}// if (m_bConnect)
// 未连接的情况下,连接
if ( !m_bConnect )
{
DWORD dwPort = GetPort(&m_edt_locHostPort);
// 检测端口是否被占用
if ( CheckPortIsOccupy(dwPort) )
{
SetRecvData(_T("创建服务器失败,端口已被其它应用程序占用!\r\n"));
return;
}
if ( m_server.StartListening(dwPort, TCP_TYPE) )
{
SetRecvData(_T("服务器已创建!\r\n"));
m_bConnect = TRUE;
SetConectState(TRUE);
SetNetSetCtrlEnable(FALSE);
ResizeRecvCtrl(TRUE);
SetSerCtrlAreaVisable(TRUE);
//SetCliCtrlAreaVisable(FALSE);
}
else
{
SetRecvData(_T("创建服务器失败!\r\n"));
}
return;
}// if (!m_bConnect)
}
// 遍历本地tcp端口 被占用返回TRUE,否则返回FALSE
BOOL CNetAssistDlg::CheckPortIsOccupy(DWORD dwPort)
{
PMIB_TCPTABLE pTcptable;
pTcptable = (MIB_TCPTABLE*)malloc(sizeof(MIB_TCPTABLE));
DWORD dwsize = sizeof(MIB_TCPTABLE);
if (GetTcpTable(pTcptable, &dwsize, 2) == ERROR_INSUFFICIENT_BUFFER)
{
free(pTcptable);
pTcptable = (PMIB_TCPTABLE)malloc(dwsize);
}
if (GetTcpTable(pTcptable, &dwsize, 2) == NO_ERROR)
{
for (int i = 0; i < (int)pTcptable->dwNumEntries; i++)
{
DWORD port = ntohs(pTcptable->table[i].dwLocalPort);
if (port == dwPort)
{
return TRUE;
}
}
}
free(pTcptable);
return FALSE;
}
// TCP Client
void CNetAssistDlg::TCPCliConOrDis()
{
// 已连接的情况下,断开连接
if ( m_bConnect )
{
m_client.CloseSocket();
if ( !m_client.IsConnect() )
{
SetRecvData(_T("已与服务器断开连接!\r\n"));
m_bConnect = FALSE;
SetConectState(FALSE);
SetNetSetCtrlEnable(TRUE);
ResizeRecvCtrl(FALSE);
SetCliCtrlAreaVisable(FALSE);
}
else
{
SetRecvData(_T("断开连接失败!\r\n"));
}
return;
}
// 未连接的情况下,连接
if ( !m_bConnect )
{
m_client.SetSockType(TCP_TYPE);
// 获取IPAddrCtrl中的地址
CString strIP = GetIPAddr(&m_ipa_lochostIP);
char *szIp = strIP.GetBuffer(0);
strIP.ReleaseBuffer();
// 获取端口号
DWORD dwPort = GetPort(&m_edt_locHostPort);
if ( m_client.ConnectServer(szIp, dwPort) )
{
SetRecvData(_T("连接服务器成功!\r\n"));
m_bConnect = TRUE;
SetConectState(TRUE);
SetNetSetCtrlEnable(FALSE);
ResizeRecvCtrl(TRUE);
SetCliCtrlAreaVisable(TRUE);
m_stc_tarHost.SetWindowText(_T("本地主机:"));
m_stc_tarPort.SetWindowText(_T("本地端口:"));
}
else
{
SetRecvData(_T("连接服务器失败!\r\n"));
}
return;
}
}
// 获取IP
CString CNetAssistDlg::GetIPAddr(CIPAddressCtrl *ipCtrl)
{
BYTE byField0, byField1, byField2, byField3;
ipCtrl->GetAddress(byField0, byField1, byField2, byField3);
CString strIP;
strIP.Format("%d.%d.%d.%d", byField0, byField1, byField2, byField3);
return strIP;
}
// 获取端口号
DWORD CNetAssistDlg::GetPort(CEdit *edtCtrl)
{
CString strPort;
edtCtrl->GetWindowText(strPort);
int nPort = _ttoi(strPort);
return (DWORD)nPort;
}
// 设置接收区内容
void CNetAssistDlg::SetRecvData(CString strData)
{
CString strOld;
m_edt_recvData.GetWindowText(strOld);
char buff[128];
int nCount = m_edt_recvData.GetLine( m_edt_recvData.GetLineCount() - 1, buff, 128);
if ( m_ckb_recv2.GetCheck() && nCount != 0)
strData = _T("\r\n") + strData;
strOld += strData;
m_edt_recvData.SetWindowText(strOld);
int nLineCount = m_edt_recvData.GetLineCount();
m_edt_recvData.LineScroll(nLineCount - 1); // 设置滚动条一直在底部
}
// 发送信息
void CNetAssistDlg::OnBnClickedBtnSenddata()
{
if ( !m_bConnect )
{
AfxMessageBox(_T("网络未连接,发送失败!"));
return;
}
CString strData;
m_edt_sendData.GetWindowText(strData);
char *pszData = strData.GetBuffer(0);
strData.ReleaseBuffer();
switch (m_cmb_proToCol.GetCurSel())
{
case 0: // UDP
SendDataUdp(pszData);
break;
case 1: // TCP Server
SendDataTcpServer(pszData);
break;
case 2: // TCP Client
SendDataTcpClient(pszData);
break;
default:
break;
}
// 发送完自动清空
if ( m_ckb_send3.GetCheck() )
m_edt_sendData.SetWindowText(_T(""));
}
// 发送消息
// UDP
void CNetAssistDlg::SendDataUdp(char *pszData)
{
AfxMessageBox(_T("UDP通信暂不可用!"));
}
// TCP Server
void CNetAssistDlg::SendDataTcpServer(char *pszData)
{
if (pszData == NULL)
return;
UINT nLen = strlen(pszData);
int nItem = m_cmb_conTar.GetCurSel();
if (nItem == 0) // All Connections
{
for (UINT i = 0; i < m_VecSocket.size(); i++)
{
long nSendRet = m_server.SendData(m_VecSocket.at(i), pszData, nLen);
if (nSendRet < 0)
{
SetRecvData(_T("\r\n发送消息失败!"));
continue;
}
m_nSendCount += nLen;
SetSendNum();
}
}
else
{
long nSendRet = m_server.SendData(m_VecSocket.at(nItem-1), pszData, nLen);
if (nSendRet < 0)
{
SetRecvData(_T("\r\n发送消息失败!"));
return;
}
m_nSendCount += nLen;
SetSendNum();
}
}
// TCP Client
void CNetAssistDlg::SendDataTcpClient(char *pszData)
{
if (pszData == NULL)
return;
UINT nLen = strlen(pszData);
int nSendRet = m_client.SendData(pszData, nLen);
if (nSendRet <= 1)
{
SetRecvData(_T("\r\n发送消息失败!"));
return;
}
m_nSendCount += nLen;
SetSendNum();
}
/************************************************************************/
/* 套接字回调函数 */
/************************************************************************/
// 服务器端 Server
// 客户端连接
void SNewSocketCallBack(const SOCKADDR_IN *szSockAddr, SOCKET newSocket, void *pUserParam)
{
CNetAssistDlg *p = (CNetAssistDlg *)pUserParam;
p->m_VecSocket.push_back(newSocket);
CString strAddr = inet_ntoa(szSockAddr->sin_addr); // 得到发送者IP地址
char szPort[6];
itoa(szSockAddr->sin_port, szPort, 10); // 得到发送者本地端口
CString strAddrPort = strAddr + _T(":") + szPort;
p->m_cmb_conTar.AddString(strAddrPort);
p->m_stc_newCon.SetWindowText(_T("New Connection:") + strAddrPort);
}
// 接收消息
void SNewMsgCallBack(const SOCKADDR_IN *pSockAddr, const SOCKET_HAND *pSocketHand, const SOCKET_DATA *pData, void *pUserParam)
{
CNetAssistDlg *p = (CNetAssistDlg *)pUserParam;
CString strAddr = inet_ntoa(pSockAddr->sin_addr); // 得到发送者IP地址
char szPort[6];
itoa(pSockAddr->sin_port, szPort, 10); // 得到发送者本地端口
CString strAddrPort = strAddr + _T(":") + szPort;
CString strAddrPortOld = g_strAddrAndPort;
g_strAddrAndPort = strAddrPort;
if (strAddrPortOld != strAddrPort)
{
p->SetRecvData(_T("【Receive from ") + strAddrPort + _T("】:\r\n"));
}
p->SetRecvData(pData->pData);
p->m_nRecvCount += pData->len;
p->SetRecvNum();
}
// 客户端断开连接
void SCloseSocketCallBack(const char *szSocket, SOCKET newSocket, void *pUserParam)
{
CNetAssistDlg *p = (CNetAssistDlg *)pUserParam;
p->m_stc_newCon.SetWindowText(szSocket);
if (p->m_VecSocket.empty())
return;
UINT nItem = p->findSocketInVector(newSocket);
if (nItem != -1)
{
p->m_cmb_conTar.DeleteString(nItem+1);
p->m_cmb_conTar.SetCurSel(nItem);
}
}
// 返回-1表示失败,否则返回索引
UINT CNetAssistDlg::findSocketInVector(SOCKET newSocket)
{
UINT nItem = -1;
UINT i = 0;
for (vector<SOCKET>::iterator it = m_VecSocket.begin(); it != m_VecSocket.end(); )
{
if (*it == newSocket)
{
m_VecSocket.erase(it);
nItem = i;
break;
}
it++;
i++;
}
return nItem;
}
// 客户端 Client
// 接收消息
void CNewMsgCallBack(long nPort, SOCKET svrSocket, const SOCKET_DATA* pData, void * pUserParam)
{
CNetAssistDlg *p = (CNetAssistDlg *)pUserParam;
p->SetRecvData(pData->pData);
p->m_nRecvCount += pData->len;
p->SetRecvNum();
CString strPort;
strPort.Format("%d", nPort);
p->m_edt_tarPort.SetWindowText(strPort);
}
// 服务器断开连接
void CDisconnetCallBack(void *pUsr)
{
AfxMessageBox("此函数为什么不会响应??");
CNetAssistDlg *p = (CNetAssistDlg *)pUsr;
p->SetRecvData(_T("\r\n服务器已下线!"));
p->OnBnClickedBtnCondis();
}
// 串口关闭前,判断套接字是否关闭
void CNetAssistDlg::OnClose()
{
m_server.StopListening();
if (m_client.IsConnect())
m_client.CloseSocket();
CDialog::OnClose();
}
void CNetAssistDlg::OnMenuAboutBtnClicked()
{
CAboutDlg dlg;
dlg.DoModal();
}
void CNetAssistDlg::OnMenuexitbtn()
{
EndDialog(0);
}
3. 说明
CServerSocket类和CClientSocket类是对socket的封装,该类由动态链接库导出,这里不提供源码,项目里有该动态链接库的头文件和相应的DLL和LIB文件,下载地址请转至文末查看。