使用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:用来输入端口号
- 生成一个新的基于对话框的类CAddrDlg。在该对话框中增加两个编辑(Edit)控件
- 使用类向导(ClassWizard)为CAddrDlg类添加m_Addr和m_Port两个类变量。它们的ID标识、类型和名称如下:
Control ID | Type | Member |
---|---|---|
IDC_Addr | CString | m_Addr |
IDC_Port | int | m_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作为服务端测试一下啦
二、创建服务器端程序
服务端要使用到Listen 和 Accept函数与客户端建立连接。
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”类型的形参不兼容
- 解决方法
项目-属性-配置属性-常规-字符集-使用多字节字符集
- 解决方法
还有好多根本算不上问题的小问题,都是细节上的,就不列举了……
做完之后其实对代码的理解还是不太透彻,还需要继续琢磨……