C++网络编程:基于MFC CSocket类的局域网聊天系统

看完这篇文章,你会学会CSocket类的网络编程

先看项目截图

在这里插入图片描述
在这里插入图片描述
我先给大家推荐一个图标网站:iconfont
https://www.iconfont.cn/
里面有很多免费的图标

正文

CSocket类我们通常不直接使用它,而是通过类继承派生出我们自己的类,CSocket是阻塞模式。

我们采用C/S模式啊,就是一个服务器,多个客服端。

服务器的网络通信流程:
Create()->Listen()->Send()/Receive()->Receive()/Send()
客服端的网络通信流程:
Create()->Connect->Send()/Receive()->Receive()/Send()

我们先打开VS2019,新建一个MFC对话框项目,项目名叫ChatClient
作为客服端

新建一个对话框,并添加类:CDlgLogin,对话框布局就是上面的登录窗口。
添加相关的控件变量:

//DlgLogin.h
 CString m_Name;//昵称
 CString m_Ip;//IP地址
 INT m_Port;//端口号

双击“登录”按钮,在响应函数中写入相关代码

//DlgLogin.cpp
EndDialog(IDOK);//关闭对话框
UpdateData();//更新数据
CChatClientDlg dlg;
dlg.DoModal();//弹出对话框

有人会问,你那个对话框怎么有背景图案呢?
打开CDlgLogin类的类向导,添加WM_PAINT消息响应函数OnPaint()
并输入代码

CRect Rect;
GetClientRect(&Rect);
CBitmap bmp;
bmp.LoadBitmap(IDB_BITMAP1);
BITMAP logbmp;
bmp.GetBitmap(&logbmp);
CDC memDC;
memDC.CreateCompatibleDC(&dc);
memDC.SelectObject(&bmp);
dc.SetStretchBltMode(HALFTONE);
dc.StretchBlt(0, 0, Rect.Width(), Rect.Height(), &memDC, 0, 0,   logbmp.bmWidth, logbmp.bmHeight, SRCCOPY);

图片我就不发出来了,你们可以自己找一个位图

我们添加一个类:CClientSocket
基类为CSocket
在它的构造函数中写入代码:

CClientSocket(CChatClientDlg*dlg);
CClientSocket::CClientSocket(CChatClientDlg* dlg)
{
    
    
	m_Maindlg = dlg;
}

传入一个CChatClientDlg的指针,好方便我们对控件的编辑
并在ClientSocket.h中写入代码

#include "ChatClientDlg.h"
class CChatClientDlg;//类的前置声明
CChatClientDlg* m_Maindlg;

在ChatClientDlg.cpp中的OnInitDialog()写入代码

    CString strError;
	m_pSocket = new CClientSocket(this);
	m_LoginDlg = (CDlgLogin*)AfxGetMainWnd();
	if (FALSE == m_pSocket->Create())
	{
    
    
		strError.Format(L"创建套接字失败,错误代码:%d", GetLastError());
		MessageBox(strError, L"错误", MB_ICONERROR | MB_OK);
		delete m_pSocket;
		m_pSocket = NULL;
		EndDialog(IDOK);
		return TRUE;
	}
	CString strIp;//IP地址
	UINT m_Port;//端口号
	strIp = m_LoginDlg->m_Ip;
	m_Port = m_LoginDlg->m_Port;
	 
	if (FALSE == m_pSocket->Connect(strIp,m_Port))
	{
    
    
		strError.Format(L"连接服务器失败,错误代码:%d", GetLastError());
		MessageBox(strError, L"错误", MB_ICONERROR | MB_OK);
		delete m_pSocket;
		m_pSocket = NULL;
		EndDialog(IDOK);
		return TRUE;
	}
	
	//发送进入聊天室消息
	CString strSendName;
	CString strMsg;
	strSendName = m_LoginDlg->m_Name;
	strMsg.Format(L"%s 已进入聊天室\r\n", strSendName);
	m_pSocket->Send(strMsg, strMsg.GetLength() * 2);

在ChatClientDlg.h添加变量

    class CClientSocket;//类的前置声明
    class CDlgLogin;//类的前置声明
    CDlgLogin* m_LoginDlg; 
	CClientSocket* m_pSocket;

为那两个CEdit控件添加变量

    CEdit m_Message;
	CString m_Send;

在发送按钮的响应函数中添加代码

    UpdateData(); //更新数据
	CString strSend; //待发送的消息
	CString strName; //昵称
	strName = m_LoginDlg->m_Name;
	if (m_Send.GetLength() > 50)
	{
    
    
		MessageBox(L"您发送的内容太多", L"错误", MB_ICONERROR | MB_OK);
		return;
	}

	strSend.Format(L"%s : %s\r\n",strName, m_Send);

	m_pSocket->Send(strSend, strSend.GetLength() * 2);

在ClientSocket.cpp重载OnReceive虚函数

void CClientSocket::OnReceive(int nErrorCode)
{
    
    
	TCHAR* pbuffer = new TCHAR[1024];
	ZeroMemory(pbuffer, sizeof(TCHAR) * 1024);
	Receive(pbuffer, sizeof(TCHAR) * 1024);
	
	int size = m_Maindlg->m_Message.GetWindowTextLength();
	m_Maindlg->m_Message.SetSel(size, -1);
	m_Maindlg->m_Message.ReplaceSel(pbuffer);
	
	delete[]pbuffer;
	CSocket::OnReceive(nErrorCode);
}

客服端写完了,来写服务端
“开启服务器”按钮的响应函数:

void CChatServerDlg::OnBnClickedButton1()
{
    
    
	UpdateData();
	
	
	CString str;
	m_Socket = new CListenSocket;
	if (m_Ip.IsEmpty() || m_Port <= 1024)
	{
    
    
		MessageBox(L"请输入正确的IP地址和端口号", L"错误", MB_OK | MB_ICONERROR);
		return;
	}
	if (FALSE==m_Socket->Create(m_Port, SOCK_STREAM, m_Ip))
	{
    
    
		str.Format(L"创建套接字失败,错误代码:%d", GetLastError());
		MessageBox(str, L"错误", MB_OK | MB_ICONERROR);
		delete m_Socket;
		m_Socket = NULL;
		return;
	}
	if (FALSE == m_Socket->Listen())
	{
    
    
		str.Format(L"监听失败,错误代码:%d", GetLastError());
		MessageBox(str, L"错误", MB_OK | MB_ICONERROR);
		delete m_Socket;
		m_Socket = NULL;
		return;
	}
	GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
}

相应的变量

CListenSocket* m_Socket;
CString m_Ip;
INT m_Port;

服务器我们添加了两个CSocket派生类:CListenSocket、CClientSocket

“关闭服务器”按钮:

    if (m_Socket != NULL)
	{
    
    
		
		m_Socket->Close();
		KillTimer(1);
	}
	GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);
并在ChatServerDlg.cpp中写入
extern list<CClientSocket*>m_pClientsocket;

在CListenSocket.cpp中写入

#include "pch.h"
#include "ListenSocket.h"
#include "ClientSocket.h"

list<CClientSocket*>m_pClientsocket;
void CListenSocket::OnAccept(int nErrorCode)
{
    
    
	CClientSocket* m_psocket=new CClientSocket;
	Accept(*m_psocket);
	m_pClientsocket.push_back(m_psocket);

	CSocket::OnAccept(nErrorCode);
}

在CClientSocket.cpp中写入

#include "pch.h"
#include "ClientSocket.h"

extern list<CClientSocket*>m_pClientsocket;
void CClientSocket::OnReceive(int nErrorCode)
{
    
    
	TCHAR* pbuffer = new TCHAR[1024];
	ZeroMemory(pbuffer, sizeof(TCHAR) * 1024);
	Receive(pbuffer, sizeof(TCHAR) * 1024);//接收到的数据
	
	list<CClientSocket*>::iterator it;
	for (it = m_pClientsocket.begin(); it != m_pClientsocket.end(); it++)
	{
    
    
		CString strMsg;          //发送的消息
		CTime m_Time;            //时间
		CString strTime;
		CClientSocket* pSocket = *it;
		CString strIpAddress;   //IP地址
		UINT uPort;             //端口号
		m_Time = CTime::GetTickCount();
		strTime = m_Time.Format(L"%H:%M:%S");
		pSocket->GetPeerName(strIpAddress, uPort);
		strMsg.Format(L"[%s] %s",strTime, pbuffer);
		
		pSocket->Send(strMsg, strMsg.GetLength() * 2);
	}
	delete[] pbuffer;
	CSocket::OnReceive(nErrorCode);
}

好了,完了
我给一个软件链接
https://fow.lanzoui.com/b02cdqbsd
密码:eh6x

猜你喜欢

转载自blog.csdn.net/m0_47563648/article/details/119927247