实验目标

简单的聊天室程序,采用C/S模型,分为客户端和服务器端程序。是点对点通信的程序。客户端和服务器端通过网络交换聊天的字符串内容,并显示。

实验目的

  1. 学习如何从CAsyncSocket类派生出自己的WinSock类。
  2. 理解WinSock类与应用程序框架的关系
  3. 学习流式套接字对象的使用
  4. 学习处理网络事件的方法
  5. 了解MFC编程

实验环境

Windows10 x64,Visual Studio 2017

实验内容

分为两大块,创建客户端和创建服务器端。

客户端

  1. 使用MFC ApplicationWizard创建客户端应用程序框架
  2. 为对话框界面添加控件对象
  3. 为对话框中的控件对象定义相应的成员变量
  4. 创建从CAsynSocket类继承的派生类
  5. 为对话框类添加控件对象事件的响应函数
  6. 为CtalkcDlg对话框类添加其他的成员函数和成员变量
  7. 添加关于控件变量的初始化代码
  8. 添加事件函数和成员函数的代码

服务器端

与客户端同理

实验原理

服务器端:

  • 创建一个套接字
  • 创建Socket,绑定到指定端口
  • 启动监听,准备客户端的连接请求
  • 接收连接
  • 消息的传送
  • 关闭套接字

客户端:

  • 创建一个套接字
  • 创建Socket
  • 请求连接服务器
  • 消息的传送
  • 关闭套接字

实验过程(代码分析)

👉创造程序框架 (MFC基于对话框)

👉添加控件对象

👉添加控件成员变量

CSocket类从CAsynSocket类继承了许多成员函数,封装了Windows套接字应用程序编程接口API。

应用程序类Ctalkc\s App对应文件

VC++自动生成

派生类CMySocket对应文件

对CMySocket进行定义。它是从CAsynSocket类继承下来的派生类,将Socket事件传给对话框,以便执行用户自己的事件处理函数。

class CMySocket:public CAsynSocket

添加事件响应函数,用于连接、断开、和接收

服务器端:

✏️MySocket.h

#pragma once
#include <afxsock.h>

class CtalksDlg;
////////////////////////////////

class CMySocket :
    public CAsyncSocket
{
public:
    CMySocket();
    virtual ~CMySocket();
public:
    virtual void OnAccept(int nErrorCode);
    virtual void OnClose(int nErrorCode);
    virtual void OnReceive(int nErrorCode);

private:
    CtalksDlg* m_pDlg;
public:
    void SetParent(CtalksDlg* pDlg);
};

✏️MySocket.cpp

#include "talks.h"
#include "pch.h"
#include "MySocket.h"
#include "talksDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
#endif

// CMySocket

CMySocket::CMySocket()
{
    m_pDlg = NULL;
}
CMySocket::~CMySocket()
{
    m_pDlg = NULL;
}
void CMySocket::SetParent(CtalksDlg* pDlg)
{
    
    m_pDlg = pDlg;
}


#if 0
BEGIN_MESSAGE_MAP(CMySocket, CAsyncSocket)

END_MESSAGE_MAP()
#endif

//CMySocket member functions

void CMySocket::OnAccept(int nErrorCode)
{
    if (nErrorCode == 0) m_pDlg->OnAccept();

}


void CMySocket::OnClose(int nErrorCode)
{

    if (nErrorCode == 0) m_pDlg->OnClose();
}


void CMySocket::OnReceive(int nErrorCode)
{
    
    if (nErrorCode == 0) m_pDlg->OnReceive();

}


客户端:

✏️MySocket.h

#pragma once
#include <afxsock.h>

class CtalkcDlg;
////////////////////////////////
//CMySocket ÀàµÄ¶¨Òå
class CMySocket :
    public CAsyncSocket
{
public:
    CMySocket();
    virtual ~CMySocket();
public:
    virtual void OnConnect(int nErrorCode);
    virtual void OnClose(int nErrorCode);
    virtual void OnReceive(int nErrorCode);

private:
    CtalkcDlg* m_pDlg;
public:
    void SetParent(CtalkcDlg* pDlg);
};

✏️MySocket.cpp

#include "talkc.h"
#include "pch.h"
#include "MySocket.h"
#include "talkcDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
#endif

// CMySocket

CMySocket::CMySocket()
{
    m_pDlg = NULL;        
}
CMySocket::~CMySocket()
{
    m_pDlg = NULL;
}
void CMySocket::SetParent(CtalkcDlg* pDlg)
{
    // TODO: ÔÚ´Ë´¦Ìí¼ÓʵÏÖ´úÂë.
    m_pDlg = pDlg;
}


#if 0
BEGIN_MESSAGE_MAP(CMySocket, CAsyncSocket)

END_MESSAGE_MAP()
#endif

//CMySocket member functions

void CMySocket::OnConnect(int nErrorCode)
{
    // TODO: ÔÚ´ËÌí¼ÓרÓôúÂëºÍ/»òµ÷ÓûùÀà
    if (nErrorCode == 0) m_pDlg -> OnConnect();
    
}


void CMySocket::OnClose(int nErrorCode)
{
    // TODO: ÔÚ´ËÌí¼ÓרÓôúÂëºÍ/»òµ÷ÓûùÀà
    if (nErrorCode == 0) m_pDlg->OnClose();
}


void CMySocket::OnReceive(int nErrorCode)
{
    // TODO: ÔÚ´ËÌí¼ÓרÓôúÂëºÍ/»òµ÷ÓûùÀà
    if (nErrorCode == 0) m_pDlg->OnReceive();
    
}


对话框类CTalkDlg对应文件

主要是对控件变量的声明和定义。

服务器端:

✏️talksDlg.h


// talksDlg.h: 头文件
//

#pragma once
#include "MySocket.h"

// CtalksDlg 对话框
class CtalksDlg : public CDialogEx
{
// 构造
public:
    CtalksDlg(CWnd* pParent = nullptr);    // 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_TALKS_DIALOG };
#endif

    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()
public:
    afx_msg void OnLbnSelchangeList2();
    CButton m_btnListen;
    CString m_strServName;
    int m_nServPort;
    CString m_strMsg;
    CListBox m_listSent;
    CListBox m_listReceived;
    afx_msg void OnButtonClose();
    afx_msg void OnButtonListen();
    afx_msg void OnSendMsg();
    CMySocket m_sListenSocket;
    CMySocket m_sConnectSocket;
    void OnClose();
    void OnAccept();
    void OnReceive();
};

✏️talksDlg.cpp


// talksDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "talks.h"
#include "talksDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ABOUTBOX };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CtalksDlg 对话框



CtalksDlg::CtalksDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_TALKS_DIALOG, pParent)
    , m_strServName(_T(""))
    , m_nServPort(0)
    , m_strMsg(_T(""))
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CtalksDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_BUTTON_LISTEN, m_btnListen);
    DDX_Text(pDX, IDC_EDIT_SERVNAME, m_strServName);
    DDX_Text(pDX, IDC_EDIT_SERVPORT, m_nServPort);
    DDX_Text(pDX, IDC_EDIT_MSG, m_strMsg);
    DDX_Control(pDX, IDC_LIST_SENT, m_listSent);
    DDX_Control(pDX, IDC_LIST_RECEIVED, m_listReceived);
}

BEGIN_MESSAGE_MAP(CtalksDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CtalksDlg::OnButtonClose)
    ON_BN_CLICKED(IDC_BUTTON_LISTEN, &CtalksDlg::OnButtonListen)
    ON_BN_CLICKED(IDOK, &CtalksDlg::OnSendMsg)
END_MESSAGE_MAP()


// CtalksDlg 消息处理程序

BOOL CtalksDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != nullptr)
    {
        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: 在此添加额外的初始化代码
    m_strServName = "localhost";
    m_nServPort = 1000;
    UpdateData(FALSE);
    m_sListenSocket.SetParent(this);
    m_sConnectSocket.SetParent(this);
    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CtalksDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CtalksDlg::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
    {
        CDialogEx::OnPaint();
    }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CtalksDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}



void CtalksDlg::OnButtonClose()
{
    // TODO: 在此添加控件通知处理程序代码
    OnClose();
}


void CtalksDlg::OnButtonListen()
{
    UpdateData(TRUE);
    GetDlgItem(IDC_BUTTON_LISTEN)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT_SERVNAME)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT_SERVPORT)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_SERVNAME)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_SERVPORT)->EnableWindow(FALSE);
    
    m_sListenSocket.Create(m_nServPort);
    m_sListenSocket.Listen();

}


void CtalksDlg::OnSendMsg()
{
    // TODO: 在此添加控件通知处理程序代码
    int nLen;
    int nSent;
    UpdateData(TRUE);
    if (!m_strMsg.IsEmpty())
    {
        nLen = m_strMsg.GetLength();
        nSent = m_sConnectSocket.Send(LPCTSTR(m_strMsg), nLen);
        if (nSent != SOCKET_ERROR)
        {
            m_listSent.AddString(m_strMsg);
            UpdateData(FALSE);
        }
        else {
            AfxMessageBox(_T("信息发送错误!", MB_OK | MB_ICONSTOP));
        }
        m_strMsg.Empty();
        UpdateData(FALSE);
    }
}


void CtalksDlg::OnClose()
{
    m_listReceived.AddString("服务器已经收到啦OnClose的消息♥");
    m_sConnectSocket.Close();
    GetDlgItem(IDC_EDIT_MSG)->EnableWindow(FALSE);
    GetDlgItem(IDOK)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_MSG)->EnableWindow(FALSE);
    GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(FALSE);

    while (m_listSent.GetCount() != 0) m_listSent.DeleteString(0);
    while (m_listReceived.GetCount() != 0) m_listReceived.DeleteString(0);

    GetDlgItem(IDC_BUTTON_LISTEN)->EnableWindow(TRUE);
    GetDlgItem(IDC_EDIT_SERVNAME)->EnableWindow(TRUE);
    GetDlgItem(IDC_EDIT_SERVPORT)->EnableWindow(TRUE);
    GetDlgItem(IDC_STATIC_SERVNAME)->EnableWindow(TRUE);
    GetDlgItem(IDC_STATIC_SERVPORT)->EnableWindow(TRUE);
}


void CtalksDlg::OnAccept()
{
    // TODO: 在此处添加实现代码.
    m_listReceived.AddString("服务器已经收到啦OnAccept的消息!!");
    m_sListenSocket.Accept(m_sConnectSocket);    
    GetDlgItem(IDC_EDIT_MSG)->EnableWindow(TRUE);
    GetDlgItem(IDOK)->EnableWindow(TRUE);
    GetDlgItem(IDC_STATIC_MSG)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(TRUE);

    
}


void CtalksDlg::OnReceive()
{
    char *pBuf = new char[1025];
    int nBufSize = 1024;
    int nReceived;
    CString strReceived;
    m_listReceived.AddString("服务器已经收到啦OnReceive的消息!!");
    nReceived = m_sConnectSocket.Receive(pBuf, nBufSize);
    if (nReceived != SOCKET_ERROR)
    {
        pBuf[nReceived] = NULL;
        strReceived = pBuf;
        m_listReceived.AddString(strReceived);
        UpdateData(FALSE);
    }
    else {
        AfxMessageBox(_T("信息接收出错啦!!", MB_OK | MB_ICONSTOP));
    }
}

客户端:

✏️talkcDlg.h

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER >1000
// talkcDlg.h: 头文件
//
#include "MySocket.h"
#pragma once


// CtalkcDlg 对话框
class CtalkcDlg : public CDialogEx
{
// 构造
public:
    void OnClose();
    void OnConnect();
    void OnReceive();
    CMySocket m_sConnectSocket;
    CtalkcDlg(CWnd* pParent = nullptr);    // 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_TALKC_DIALOG };
#endif
    CListBox m_listSent;
    CListBox m_listReceived;
    CButton m_btnConnect;
    CString m_strMsg;
    CString m_strServName;
    int m_nServPort;

    
    
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()
public:
    
    afx_msg void OnButtonClose();
    afx_msg void OnButtonConnect();
    afx_msg void OnSendMsg();
};

✏️talkcDlg.cpp


// talkcDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "talkc.h"
#include "talkcDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ABOUTBOX };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CtalkcDlg 对话框



CtalkcDlg::CtalkcDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_TALKC_DIALOG, pParent)
    , m_strServName(_T(""))
    , m_nServPort(0)
    , m_strMsg(_T(""))
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CtalkcDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_BUTTON_CONNECT, m_btnConnect);
    DDX_Text(pDX, IDC_EDIT_SERNAME, m_strServName);
    DDX_Text(pDX, IDC_EDIT_SERVPORT, m_nServPort);
    DDX_Text(pDX, IDC_EDIT_MSG, m_strMsg);
    DDX_Control(pDX, IDC_LIST_SENT, m_listSent);
    DDX_Control(pDX, IDC_LIST_RECEIVED, m_listReceived);
}

BEGIN_MESSAGE_MAP(CtalkcDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CtalkcDlg::OnButtonClose)
    ON_BN_CLICKED(IDC_BUTTON_CONNECT, &CtalkcDlg::OnButtonConnect)
    ON_BN_CLICKED(IDOK, &CtalkcDlg::OnSendMsg)
END_MESSAGE_MAP()


// CtalkcDlg 消息处理程序

BOOL CtalkcDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != nullptr)
    {
        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: 在此添加额外的初始化代码
    m_strServName = "localhost";
    m_nServPort = 1000;
    UpdateData(FALSE);
    GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT_SERNAME)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT_SERVPORT)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_SERVNAME)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_SERVPORT)->EnableWindow(FALSE);
    
    m_sConnectSocket.SetParent(this);
    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CtalkcDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CtalkcDlg::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
    {
        CDialogEx::OnPaint();
    }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CtalkcDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}



void CtalkcDlg::OnButtonConnect()
{
    // TODO: 在此添加控件通知处理程序代码
    UpdateData(TRUE);
    GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT_SERNAME)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT_SERVPORT)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_SERVNAME)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_SERVPORT)->EnableWindow(FALSE);
    m_sConnectSocket.Create();
    m_sConnectSocket.Connect(m_strServName, m_nServPort);

}

void CtalkcDlg::OnSendMsg()
{
    // TODO: 在此添加控件通知处理程序代码
    int nLen;
    int nSent;
    UpdateData(TRUE);
    if (!m_strMsg.IsEmpty())
    {
        nLen = m_strMsg.GetLength();
        nSent = m_sConnectSocket.Send(LPCTSTR(m_strMsg), nLen);
        if (nSent != SOCKET_ERROR)
        {
            m_listSent.AddString(m_strMsg);
            UpdateData(FALSE);
        }
        else {
            AfxMessageBox(_T("信息发送错误!", MB_OK | MB_ICONSTOP));
        }
        m_strMsg.Empty();
        UpdateData(FALSE);
    }
}


void CtalkcDlg::OnButtonClose()
{
    // TODO: 在此添加控件通知处理程序代码
    OnClose();
}

void CtalkcDlg::OnReceive()
{
    // TODO: 在此处添加实现代码.
    char *pBuf = new char[1025];
    int nBufSize = 1024;
    int nReceived;
    CString strReceived;
    nReceived = m_sConnectSocket.Receive(pBuf, nBufSize);
    if (nReceived != SOCKET_ERROR)
    {
        pBuf[nReceived] = NULL;
        strReceived = pBuf;
        m_listReceived.AddString(strReceived);
        UpdateData(FALSE);
    }
    else {
        AfxMessageBox(_T("信息接收出错啦!!", MB_OK | MB_ICONSTOP));
    }
}


void CtalkcDlg::OnConnect()
{
    // TODO: 在此处添加实现代码.
    //开放连接配置的相关控件,如“连接”按钮、服务器名称、端口的文本框和标签
    GetDlgItem(IDC_EDIT_MSG)->EnableWindow(TRUE);
    GetDlgItem(IDOK)->EnableWindow(TRUE);
    GetDlgItem(IDC_STATIC_MSG)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(TRUE);

}

void CtalkcDlg::OnClose()
{
    // TODO: 在此处添加实现代码.
    m_sConnectSocket.Close();
    GetDlgItem(IDC_EDIT_MSG)->EnableWindow(FALSE);
    GetDlgItem(IDOK)->EnableWindow(FALSE);
    GetDlgItem(IDC_STATIC_MSG)->EnableWindow(FALSE);
    GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(FALSE);

    while (m_listSent.GetCount() != 0) m_listSent.DeleteString(0);
    while (m_listReceived.GetCount() != 0) m_listReceived.DeleteString(0);

    GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(TRUE);
    GetDlgItem(IDC_EDIT_SERVNAME)->EnableWindow(TRUE);
    GetDlgItem(IDC_EDIT_SERVPORT)->EnableWindow(TRUE);
    GetDlgItem(IDC_STATIC_SERVNAME)->EnableWindow(TRUE);
    GetDlgItem(IDC_STATIC_SERVPORT)->EnableWindow(TRUE);
}

实验界面结果展示

实验过程中遇到的问题

⛔️关于字符的问题

💚在配置中调属性,或宏定义_T_。解决该报错

实验参考书

客户端

服务器端

评论