使用MFC类库实现回声程序


网络程序设计实验课的一个小作业……
课本上有详细步骤,但有些地方还是需要更改,就把它整理一下发上来吧……

一、 创建客户端程序

下面是使用CAsyncSocket类创建客户端的步骤。

1.创建一个工程

创建一个基于对话框的工程,该工程的名称为CSockClient。

这里要选上Windows套接字

2.设计程序对话框

在这里插入图片描述
添加成员变量
在这里插入图片描述

3.生成CAsyncSocket类的子类MySock

在这里插入图片描述

4.在MySock.ccp文件中添加文件包含信息

在生成的MySock.cpp中添加如下文件包含信息:

#include "CSockClient.h"
#include "CSockClientDlg.h"

5.向MySock.h文件中添加代码

//向MySock.h文件中添加如下代码:
		public:
   		 BOOL m_bConnected;	 	//是否连接
   		 UINT m_nLength;	     	//消息长度
   		 char m_szBuffer[4096];  	//消息缓冲区

6.重载MySock.ccp文件中的各函数

(1)  MySock( )函数重载的代码如下:

MySock::MySock()
{
	m_nLength = 0;
	memset(m_szBuffer, 0, sizeof(m_szBuffer));
	m_bConnected = FALSE;
}

MySock::~MySock()
{
	//关闭套接字
	if (m_hSocket != INVALID_SOCKET)
		Close();
}

(2)  OnReceive ( )函数重载的代码如下:

void MySock::OnReceive(int nErrorCode)
{
	
	m_nLength = Receive(m_szBuffer, sizeof(m_szBuffer), 0);
	//下面两行代码用来获取对话框指针
	CCSockClientApp* pApp = (CCSockClientApp*)AfxGetApp();
	CCSockClientDlg*pDlg = (CCSockClientDlg*)pApp->m_pMainWnd;
	
	pDlg->m_MSGS.InsertString(0, m_szBuffer);
	memset(m_szBuffer, 0, sizeof(m_szBuffer));
	CAsyncSocket::OnReceive(nErrorCode);
}

(3)  OnSend( )函数重载的代码如下:

void MySock::OnSend(int nErrorCode)
{
	Send(m_szBuffer, m_nLength, 0);
	m_nLength = 0;
	memset(m_szBuffer, 0, sizeof(m_szBuffer));
	//继续提请一个''读''的网络事件,接收Server消息
	AsyncSelect(FD_READ);
	CAsyncSocket::OnSend(nErrorCode);
}

(4)  OnConnect( )函数重载的代码如下:

void MySock::OnConnect(int nErrorCode)
{
	if (nErrorCode == 0)
	{
		m_bConnected = TRUE;
		CCSockClientApp* 	pApp = (CCSockClientApp*)AfxGetApp();
		CCSockClientDlg* pDlg = (CCSockClientDlg*)pApp->m_pMainWnd;
		memcpy(m_szBuffer, "Connected to ", 13);
		strncat(m_szBuffer, pDlg->m_szServerAdr,
			sizeof(pDlg->m_szServerAdr));
		pDlg->m_MSGS.InsertString(0, m_szBuffer);
		//提请一个''读''的网络事件,准备接收
		AsyncSelect(FD_READ);
	}
	CAsyncSocket::OnConnect(nErrorCode);
}

7.新建一个输入地址的对话框

  • 新建一个输入地址信息的对话框IDD_Addr用来输入IP地址和端口号;
    • 生成一个新的基于对话框的类CAddrDlg。在该对话框中增加两个编辑(Edit)控件
      • IDC_Addr:用来输入要连接的IP地址
      • IDC_Port:用来输入端口号
  • 使用类向导(ClassWizard)为CAddrDlg类添加m_Addr和m_Port两个类变量。它们的ID标识、类型和名称如下:
Control IDTypeMember
IDC_AddrCStringm_Addr
IDC_Portintm_Port
  • 然后向CSockClientDlg类中添加如下代码:
protected:
	int TryCount;
	MySock m_clientSocket;
	UINT m_szPort;
public:
	char m_szServerAdr[256];

10.添加“连接”按钮的程序代码

双击IDD_CSOCKCLIENT_DIALOG对话框中的“连接”按钮,添加以下代码:

void CCSockClientDlg::OnBnClickedConnect()
{
	m_clientSocket.ShutDown(2);//0关闭接收操作,也就是断开输入流;1关闭发送操作,也就是断开输出流;2同时关闭接收和发送操作
	m_clientSocket.m_hSocket = INVALID_SOCKET;//设置成无效套接字
	m_clientSocket.m_bConnected = FALSE;
	CAddrDlg m_Dlg;
	//默认端口7
	m_Dlg.m_Port = 7;
	if (m_Dlg.DoModal() == IDOK && !m_Dlg.m_Addr.IsEmpty())
	{
		memcpy(m_szServerAdr, m_Dlg.m_Addr, sizeof(m_szServerAdr));
		m_szPort = m_Dlg.m_Port;
		//建立计时器,每1秒尝试连接一次,直到连上或	TryCount>10
		SetTimer(1, 1000, NULL);
		TryCount = 0;
	}
}

11.添加Windows消息WM_TIMER的响应函数OnTimer

  • 在CSockClientDlg中打开类向导,添加消息WM_TIMER的响应函数OnTimer
void CCSockClientDlg::OnTimer(UINT_PTR nIDEvent)
{
	if (m_clientSocket.m_hSocket == INVALID_SOCKET)
	{
		BOOL 	bFlag = m_clientSocket.Create(0, SOCK_STREAM, FD_CONNECT);
		if (!bFlag)
		{
			AfxMessageBox("Socket Error!");
			m_clientSocket.Close();
			PostQuitMessage(0);
			return;
		}
	}
	m_clientSocket.Connect(m_szServerAdr, m_szPort);
	TryCount++;
	if (TryCount >= 10 || m_clientSocket.m_bConnected)
	{
		KillTimer(1);
		if (TryCount >= 10)
			AfxMessageBox("Connect Failed!");
		return;
	}

	CDialogEx::OnTimer(nIDEvent);
}

12.添加“Send”按钮的程序代码

  • 双击IDD_CSOCKCLIENT_DIALOG对话框中的“Send”按钮,添加以下代码:
void CCSockClientDlg::OnBnClickedSend()
{
	if (m_clientSocket.m_bConnected)
	{
		m_clientSocket.m_nLength = m_MSG.GetWindowText
		(m_clientSocket.m_szBuffer, sizeof(m_clientSocket.m_szBuffer));
		m_clientSocket.AsyncSelect(FD_WRITE);
		m_MSG.SetWindowText("");
	}

}

13.添加“Exit”按钮的程序代码

双击IDD_CSOCKCLIENT_DIALOG对话框中的“Exit”按钮,添加以下代码:

void CCSockClientDlg::OnBnClickedExit()
{
	//关闭Socket
	m_clientSocket.ShutDown(2);
	//关闭对话框
	EndDialog(0);
}

测试

  • 上述步骤完成后,调试程序并建立可执行的 .exe文件
  • 当该项目运行时,弹出如图所示的程序对话框
    在这里插入图片描述
  • 点击“Connect”按钮,弹出如图所示的对话框
    在这里插入图片描述
  • 输入要连接的服务器的IP地址和端口号,然后单击“OK”按钮。当连接建立后,就可以在客户程序对话框中输入回送信息,并按“Send”按钮进行程序测试了。这里因为还没有写服务端,所以就先用NetAssist作为服务端测试一下啦
    在这里插入图片描述

二、创建服务器端程序

服务端要使用到ListenAccept函数与客户端建立连接。

1.建立一个CAsyncSocket的子类

  • 回声程序的服务器程序应将收到的信息原封不动地发送给客户程序。因此,服务器程序的代码主要是重载CAsyncSocket类的接收和发送函数。
  • 建立一个CAsyncSocket类的子类CNewSocket,并重载CAsyncSocket类的OnReceive( )和OnSend( )函数。程序代码如下:
void CNewSocket::OnReceive(int nErrorCode)
{
	m_nLength = Receive(m_szBuffer, sizeof(m_szBuffer), 0);
	// 直接转发消息
	AsyncSelect(FD_WRITE);
}

void CNewSocket::OnSend(int nErrorCode)
{
	Send(m_szBuffer, m_nLength,0);
	AsyncSelect(FD_READ);//加上这句让套接字准备接收信息,没有的话就不会继续接收
}

这里注意,如果想要程序可以连续发送和接收消息的话,需要在OnSend中加上这一句:

-AsyncSelect(FD_READ);

2.创建一个用于建立连接的类CMyServerSocket

  • 创建一个用于建立连接的类CMyServerSocket,并重载CAsyncSocket类的OnAccept( )函数
    在MyServerSocket.h中声明:
public:
	CNewSocket*  m_pSocket;

OnAccept( )程序的代码如下:

void CMyServerSocket::OnAccept(int nErrorCode)
{
	//侦听到连接请求,调用Accept函数
	CNewSocket* pSocket = new CNewSocket();
	if (Accept(*pSocket))
	{
		pSocket->AsyncSelect(FD_READ);
		m_pSocket = pSocket;
	}
	else
		delete pSocket;
}

3.为对话框添加一个“Listen”按钮

  • 为对话框添加一个“Listen”按钮,并添加如下代码。
    在CSockServerDlg.h中声明变量:
public:
	CMyServerSocket  m_srvrSocket;
	UINT UserPort=7;//服务器端口号,一般为7

一定一定一定不要忘记设置端口号啊啊啊啊啊啊!!!
不然就会连接失败……

OnListen( )函数的代码如下:

void CCSockServerDlg::OnBnClickedListen()
{
	if (m_srvrSocket.m_hSocket == INVALID_SOCKET)
	{
		BOOL bFlag = m_srvrSocket.Create
			(UserPort,SOCK_STREAM,FD_ACCEPT);
			if (!bFlag)
			{
				AfxMessageBox(_T("Socket Error!"));
				m_srvrSocket.Close();
				PostQuitMessage(0);
				return;
			}
	}
	// ''侦听''成功,等待连接请求
	if (!m_srvrSocket.Listen(1))
	{
		int nError = m_srvrSocket.GetLastError();
			if (nError != WSAEWOULDBLOCK)
			{
				AfxMessageBox(_T("Socket Error!"));
				m_srvrSocket.Close();
				PostQuitMessage(0);
				return;
			}
	}
}

然后就好啦!

测试

在这里插入图片描述

实验过程中遇到的小问题

  • "char*"类型的实参与“LPCTSTR”类型的形参不兼容
    在这里插入图片描述
    • 解决方法
      项目-属性-配置属性-常规-字符集-使用多字节字符集

还有好多根本算不上问题的小问题,都是细节上的,就不列举了……

做完之后其实对代码的理解还是不太透彻,还需要继续琢磨……

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值