当前位置:   article > 正文

基于MFC的简易TCP/IP调试助手开发_tcpip调试助手

tcpip调试助手

在学习TCP/IP通讯过程中,打算参考网上的教程写一个简易的调试助手,服务器与客户端分别参考以下两位代码完成,效果图如下。

1、如何基于TCP/IP协议进行MFC Socket网络通讯编程_Ezreal_zh的博客-CSDN博客_mfc tcp通信

2、使用MFC制作简易TCP客户端_哔哩哔哩_bilibili

 

        首先我们要了解windows套接字的编程流程是什么,分为服务器和客户端:

服务器:

1、加载套接字库 
2、创建套接字(socket)。 
3、将套接字绑定到一个本地地址和端口上(bind)。 
4、将套接字设为监听模式,准备接收客户请求(listen)。 
5、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。 
6、用返回的套接字和客户端进行通信(send/recv)。 
7、返回,等待另一客户请求。 
8、关闭套接字

客户端程序:

1、加载套接字库 
2、创建套接字(socket)。 
3、向服务器发出连接请求(connect)。 
4、和服务器端进行通信(send/recv)。 
5、关闭套接字。

原文链接:https://blog.csdn.net/qq_32171677/article/details/60959137

        首先新建一个项目,选中基于对话框,勾选windows(套接字),选择完成即可。

        要完成标签页的切换效果需要用到MFC 的Tab Control控件,在主窗口添加 Tab Control控件,然后需要插入两个DIALOG资源,

将其ID分别命名为IDC_DIALOG_SERVER,和IDC_DIALOG_CLIENT。

        由于服务端和客户端是作为子级页面存在与标签页中,所以需要将其属性中的

Stlye设置为:Child(子级),同时将其Border属性设置为:none(无)

分别为server、client标签页添加类,基类为CDialogEX

双击Tab Control控件,生成OnTcnSelchangeTab1消息函数,同时选中控件右键 添加变量 :m_tab,

在头文件中,声明server,client 的对象用于绑定切换    CClient client; CServer    server;需要include Server 和Client

  1. // TCP_IPDlg.h : 头文件
  2. //
  3. #pragma once
  4. #include "afxcmn.h"
  5. #include "Client.h"
  6. #include "Server.h"
  7. // CTCP_IPDlg 对话框
  8. class CTCP_IPDlg : public CDialogEx
  9. {
  10. .
  11. .
  12. .
  13. .
  14. .
  15. .
  16. .
  17. .
  18. .
  19. public:
  20. CTabCtrl m_tab;
  21. afx_msg void OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult);
  22. public:
  23. CClient client;
  24. CServer server;
  25. }

在OnInitDialog函数中初始化标签页的大小和绑定关系。

  1. BOOL CTCP_IPDlg::OnInitDialog()
  2. {
  3. .
  4. .
  5. .
  6. .
  7. .
  8. .
  9. .
  10. // TODO: 在此添加额外的初始化代码
  11. m_tab.InsertItem(0, _T("Server")); //插入Server标签
  12. m_tab.InsertItem(1, _T("Client")); //插入Client标签
  13. server.Create(MAKEINTRESOURCE(IDD_DIALOG_SERVER), &m_tab); //创建server标签页
  14. client.Create(MAKEINTRESOURCE(IDD_DIALOG_CLIENT), &m_tab); //创建client标签页
  15. CRect tabRect = { 0x00 }, tabWinRect = { 0x00 }; //标签控件客户区的Rect
  16. //设置客户区Rect
  17. m_tab.GetClientRect(&tabRect);
  18. tabRect.left += 2;
  19. tabRect.right -= 2;
  20. tabRect.top += 23;
  21. tabRect.bottom -= 2;
  22. //根据调整好的tabRect放置server,client对话框,并设为显示
  23. server.MoveWindow(&tabRect);
  24. client.MoveWindow(&tabRect);
  25. server.ShowWindow(TRUE);
  26. return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
  27. }

在OnTcnSelchangeTab1设置切换的显示关系

  1. void CTCP_IPDlg::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult)
  2. {
  3. *pResult = 0;
  4. // TODO: 在此添加控件通知处理程序代码
  5. CRect tabRect, tabWinRect;
  6. m_tab.GetClientRect(&tabRect);
  7. tabRect.left += 5;
  8. tabRect.right -= 5;
  9. tabRect.top += 23;
  10. tabRect.bottom -= 5;
  11. switch (m_tab.GetCurSel()) {//返回组合框中列表框中当前选中的项的下标,如果没有选中项返回CB_ERR
  12. //如果当前选中为标签1 server
  13. case 0:
  14. server.ShowWindow(TRUE);
  15. client.ShowWindow(FALSE);
  16. break;
  17. //如果当前选中为标签2 client
  18. case 1:
  19. server.ShowWindow(FALSE);
  20. client.ShowWindow(TRUE);
  21. break;
  22. default:
  23. server.ShowWindow(TRUE);
  24. break;
  25. }
  26. }

         主界面的显示功能便完成,可进行server,client标签页的切换,接下来先进行client客户端的开发。

        在客户端界面,我们需要添加五个Edit Control控件用于输入IP地址、PORT端口号,显示日志文件,接收显示服务器消息以及输入发送服务端的内容;添加两个Button控件用于连接/断开连接和发送消息,具体布局如下,控件ID可分别设置为:

IDC_EDIT_IP

IDC_EDIT_PORT

IDC_EDIT_LOG

IDC_EDIT_RECEIVE

IDC_EDIT_SEND

IDC_BUTTON_CONNECT

IDC_BUTTON_SEND

为IDC_EDIT_LOG控件添加变量m_log,类别为control用于日志内容显示

为IDC_EDIT_SEND控件添加变量m_send,类别为value,用于保存控件输入发送的内容

双击IDC_BUTTON_CONNECT、IDC_BUTTON_SEND生成消息函数

        参照原文使用的接收服务器消息的实现方法为,添加一个ClientJin类,基类为CSocket,添加消息函数OnReceive具体操作为:在类视图中找到CTCP_IPDlg类(你的主窗口类),右键打开“类向导”,点击“添加类”

ClientJin.h代码如下:

  1. #pragma once
  2. #include "afxsock.h"
  3. class CClient;
  4. class CClientJin :
  5. public CSocket
  6. {
  7. public:
  8. CClientJin(CClient* pDlg);
  9. virtual ~CClientJin();
  10. virtual void OnReceive(int nErrorCode);
  11. private:
  12. CClient* m_dlg;
  13. };

 ClientJin.cpp代码如下:

  1. #include "stdafx.h"
  2. #include "ClientJin.h"
  3. #include "Client.h"
  4. #include "Resource.h"
  5. #include "TCP_IP.h"
  6. #include "TCP_IPDlg.h"
  7. CClientJin::CClientJin(CClient* pDlg) : m_dlg(pDlg)
  8. {
  9. }
  10. CClientJin::~CClientJin()
  11. {
  12. }
  13. void CClientJin::OnReceive(int nErrorCode)
  14. {
  15. // TODO: 在此添加专用代码和/或调用基类
  16. char* pData = NULL;
  17. pData = new char[1024];
  18. memset(pData, 0, sizeof(char) * 1024);
  19. CString str;
  20. Receive(pData, 1024);
  21. str = pData;
  22. CTCP_IPDlg* mainDlg = (CTCP_IPDlg*)theApp.GetMainWnd();
  23. if (mainDlg != NULL)
  24. {
  25. mainDlg->client.SetDlgItemText(IDC_EDIT_RECEIVE, str);
  26. }
  27. //if (m_dlg != NULL)
  28. //{
  29. // m_dlg->SetDlgItemTextW(IDC_EDIT_RECEIVE, str);
  30. //}
  31. delete pData;
  32. pData = NULL;
  33. CSocket::OnReceive(nErrorCode);
  34. }

回到Client类,在头文件添加以下声明,并在cpp文件实现

  1. //系统自动生成
  2. public:
  3. CEdit m_log;
  4. CString m_send;
  5. afx_msg void OnBnClickedButtonConnect();
  6. afx_msg void OnBnClickedButtonSend();
  7. //自己手动添加
  8. public:
  9. CClientJin sock;
  10. bool m_connect = false;//用于判断服务器:连接/断开连接
  11. void Log(CString str);

连接功能实现:

  1. void CClient::OnBnClickedButtonConnect()
  2. {
  3. // TODO: 在此添加控件通知处理程序代码
  4. if (m_connect == true) {//m_connect初始定义为false
  5. m_connect = false;
  6. sock.Close();
  7. SetDlgItemText(IDC_BUTTON_CONNECT, _T("连接服务器"));
  8. Log(_T("断开连接成功"));
  9. return;
  10. }
  11. if (!sock.Create()) {
  12. MessageBox((_T("创建端口失败")));
  13. return;
  14. }
  15. CString sIP;
  16. GetDlgItemText(IDC_EDIT_IP, sIP);
  17. UINT PORT = GetDlgItemInt(IDC_EDIT_PORT);
  18. if (!sock.Connect(sIP, PORT)) {
  19. MessageBox((_T("连接失败")));
  20. sock.Close();
  21. return;
  22. }
  23. m_connect = true;
  24. SetDlgItemText(IDC_BUTTON_CONNECT, _T("断开连接"));
  25. Log(_T("连接成功"));
  26. }

发送功能实现:

  1. void CClient::OnBnClickedButtonSend()
  2. {
  3. // TODO: 在此添加控件通知处理程序代码
  4. UpdateData(true);
  5. if (m_send != "") {
  6. m_send += "\r\n";
  7. int nlen = m_send.GetLength() + 1;
  8. char* pBuff = new char[nlen];
  9. memset(pBuff, 0, nlen);
  10. WideCharToMultiByte(CP_OEMCP, 0, m_send.GetBuffer(1), -1, pBuff, nlen, 0, 0);
  11. if (sock.Send(pBuff, nlen) == SOCKET_ERROR) {
  12. MessageBox((_T("发送失败")));
  13. return;
  14. }
  15. CString str;
  16. str = pBuff;
  17. str = _T("你发送的内容为:") + str;
  18. Log(str);
  19. delete pBuff;
  20. }
  21. }

日志显示功能实现:

  1. void CClient::Log(CString str)
  2. {
  3. str += "\r\n";
  4. m_log.SetSel(-1, -1);//定位光标
  5. m_log.ReplaceSel(str);
  6. }

        客户端功能便已完成,可以通过网络调试助手测试客户端通讯功能,我用的是NetAssist V5.0.2,下载连接:NetAssist网络调试助手下载_NetAssist5.0.2官方最新版下载 - 系统之家

        接下来便是服务器的开发,添加ListBox Control存放日志,添加一个Edit Control 用于输入发送的消息,两个Button控件用启动/断开服务器和发送消息,具体布局如下,控件ID可分别设置为:

IDC_LIST_SSEND

IDC_EDIT_SSEND

IDC_BUTTON_START

IDC_BUTTON_SSEND

为IDC_LIST_SSEND控件添加变量m_listwords,类别为control

 为IDC_EDIT_SSEND控件添加变量send_edit,类别为control

双击IDC_BUTTON_START和IDC_BUTTON_SSEND生成消息函数

在类向导中添加虚函数OnInitDialog()用于初始化

Serve.h代码如下:

服务器的启动我们以线程的方式启动,需要在头文件声明一个全局的IP和sock listen_sock和线程函数;然后在cpp文件中定义,为防止重复定义,需使用extern关键字

  1. // CServer 对话框
  2. extern CString IP;
  3. extern SOCKET listen_sock;
  4. extern SOCKET sock;
  5. UINT server_thd(LPVOID p);
  6. class CServer : public CDialogEx
  7. {
  8. .
  9. .
  10. .
  11. .
  12. .
  13. .
  14. .
  15. .
  16. //系统生成
  17. public:
  18. CListBox m_listwords;
  19. virtual BOOL OnInitDialog();
  20. afx_msg void OnBnClickedButtonSsend();
  21. afx_msg void OnBnClickedButtonStart();
  22. private:
  23. CEdit send_edit;
  24. //手动生成
  25. public:
  26. bool m_connect = false;
  27. void update(CString s);
  28. }

在cpp文件中进行定义

  1. // Server.cpp : 实现文件
  2. //
  3. #include "stdafx.h"
  4. #include "TCP_IP.h"
  5. #include "Server.h"
  6. #include "afxdialogex.h"
  7. #include "Resource.h"
  8. #include <afxwin.h>
  9. CString IP;
  10. SOCKET listen_sock;
  11. SOCKET sock;
  12. // CServer 对话框

线程函数实现:

  1. UINT server_thd(LPVOID p)
  2. {
  3. CServer* dlg = (CServer*) p;
  4. SOCKADDR_IN local_addr,client_addr;
  5. int iaddrSize = sizeof(SOCKADDR_IN);
  6. int res;
  7. char msg[1024];
  8. //CServer* dlg = (CServer *)AfxGetApp()->GetMainWnd();
  9. //char ch_ip[20];
  10. //CString2Char(IP, ch_ip);
  11. CString strFormat;
  12. //获取本地IP地址
  13. //local_addr.sin_addr.s_addr = inet_addr(ch_ip);
  14. local_addr.sin_addr.s_addr = INADDR_ANY;
  15. local_addr.sin_family = AF_INET;
  16. local_addr.sin_port = htons(8888);
  17. //创建套接字
  18. if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
  19. dlg->update(_T("创建监听失败"));
  20. return -1;
  21. }
  22. //绑定套接字
  23. if (bind(listen_sock, (struct sockaddr*)&local_addr, sizeof(SOCKADDR_IN))) {
  24. strFormat.Format(_T("绑定错误:%d"), WSAGetLastError());
  25. dlg->update(strFormat);
  26. closesocket(listen_sock);
  27. return -1;
  28. }
  29. //监听端口
  30. if (listen(listen_sock, 1) != 0) {
  31. strFormat.Format(_T("listen错误:%d"), WSAGetLastError());
  32. dlg->update(strFormat);
  33. closesocket(listen_sock);
  34. return -1;
  35. }
  36. //接收套接字
  37. if ((sock = accept(listen_sock, (struct sockaddr*)&client_addr, &iaddrSize)) == INVALID_SOCKET) {
  38. dlg->update(_T("accept失败"));
  39. return -1;
  40. }else {
  41. CString port;
  42. port.Format(_T("%d"), int(ntohs(client_addr.sin_port)));
  43. dlg->update(_T("已连接客户端:") + CString(inet_ntoa(client_addr.sin_addr)) + _T("端口:") + port);
  44. }
  45. //接收数据
  46. while (1)
  47. {
  48. if ((res = recv(sock, msg, 1024, 0)) == -1) {
  49. dlg->update(_T("失去客户端连接"));
  50. break;
  51. }
  52. else {
  53. msg[res] = '\0';
  54. CString port;
  55. port.Format(_T("%d"), int(ntohs(client_addr.sin_port)));
  56. dlg->update(_T("\n") + CString(inet_ntoa(client_addr.sin_addr)) + _T("端口:") + port+ CString(msg));
  57. }
  58. }
  59. return 0;
  60. }

OnInitDialog()函数如下 :

  1. / TODO: 在此添加额外的初始化
  2. send_edit.GetDlgItem(IDC_EDIT_SSEND);
  3. send_edit.SetFocus();
  4. WSADATA wsaData;
  5. WORD wVersion;
  6. wVersion = MAKEWORD(2, 2);
  7. WSAStartup(wVersion, &wsaData);
  8. return FALSE; // return TRUE unless you set the focus to a control
  9. // 异常: OCX 属性页应返回 FALSE

update()函数如下:

  1. void CServer::update(CString s)
  2. {
  3. m_listwords.AddString(s);
  4. }

启动/断开服务功能如下:

  1. // TODO: 在此添加控件通知处理程序代码
  2. if (m_connect)
  3. {
  4. m_connect = false;
  5. closesocket(listen_sock);
  6. SetDlgItemText(IDC_BUTTON_START, _T("启动服务器"));
  7. update(_T("服务器已关闭"));
  8. return;
  9. }
  10. if (m_connect !=true)
  11. {
  12. char name[128];
  13. hostent* pHost;
  14. gethostname(name, 128);//获得主机名
  15. pHost = gethostbyname(name);//获得主机结构
  16. IP = inet_ntoa(*(in_addr*)pHost->h_addr);
  17. update(_T("本服务器IP地址为:") + IP);
  18. AfxBeginThread(server_thd, this);//创建线程
  19. m_connect = true;
  20. SetDlgItemText(IDC_BUTTON_START, _T("关闭服务器"));
  21. return;
  22. }

发送消息内容如下:

  1. // TODO: 在此添加控件通知处理程序代码
  2. CString s;
  3. char msg[1024];
  4. send_edit.GetWindowTextW(s);
  5. CString2Char(s, msg);
  6. if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR) {
  7. m_listwords.SetWindowTextW(_T("发送失败"));
  8. }else if (s==" ")
  9. {
  10. MessageBox(_T("请输入信息"));
  11. }
  12. else
  13. {
  14. //s = msg;
  15. //m_listwords.AddString(_T("server:") + s);
  16. update(_T("server:") + s);
  17. send_edit.SetWindowTextW(_T(""));
  18. m_listwords.SetFocus();
  19. }

至此所有功能均已完成,可用网络调试助手进行测试通讯,以上的代码为记录我的学习过程而制作的简易TCP/IP通讯助手,代码还存在很大的优化空间,如线程的同步问题,服务器的日志区,发送区分离,accept功能分块等,后续随缘更新,感谢观看。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/583660
推荐阅读
相关标签
  

闽ICP备14008679号