Socket编程模型之异步选择模型

本文介绍了一个基于MFC的异步选择模型实现,通过创建一个迷你MFC程序和一个反射式服务端,详细展示了如何利用Windows API进行异步网络通信,包括创建、监听、接收和发送消息等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

核心仍然是用OOP实现异步选择模型。假设老陈是服务器端应用程序。老陈的多个女儿是客户端应用程序。送信的邮递员是网络。操作系统式是个信箱管理员。邮递员不能直接投到信箱,是先把信给信箱管理员,信箱管理员再把信放到指定的信箱。信箱是已经和客户端相连的socket。bind可用检测老陈要挂信箱的位置是否被别人的信箱占用了。listen表示老陈要安排计划定期或者不定期查看信箱了。accept表示老陈新设置一个信箱。connect表示女儿找到老陈的信箱,recv表示老陈查看信箱中是否有邮件。send表示其中一个人发信件。

一个迷你的MFC程序

Microsoft Visual Studio 开发软件以体积庞大著称,用它的VC向导建立起来的MFC软件体积也不小。大多数情况下我们不需要这个复杂的MFC程序,却想要使用MFCk中的类。这里给出一个非常简单的办法做一个迷你的MFC程序。首先新建一个Win32工程,选择建立一个空的工程,在工程属性设置中把MFC的引用设置好,然后添加一个main.cpp代码文件,代码如下:

#include <sdkddkver.h>
#include <afxdialogex.h>

int AFXAPI AfxWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPTSTR lpCmdLine, int nCmdShow)
{
	CWinApp app;
	CDialogEx dlg;
	LPCTSTR lpClassName;
	HBRUSH brBack;

	AfxInitialize();
	AfxWinInit(hInst, hPrev, lpCmdLine, nCmdShow);
	AfxOleInitModule();
	brBack = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
	lpClassName = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, 0, brBack);
	app.InitInstance();
	dlg.CreateEx(0, lpClassName, L"迷你MFC程序", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CRect(0, 0, 320, 240), nullptr, 0);
	app.m_pMainWnd = &dlg;
	app.Run();
	return 0;
}

代码截图以及运行效果如下所示:


怎么样,是不是体积很小?其它那代码还可以再删除几行,自己试验看看。

反射式服务端

测试内容与上一博客相同,都是把接收到的消息原封不动的发送回去,用来检验软件的正确性与稳定性。异步选择模型的异步问题由Windows自己处理。为保证结构一致性,需要服务管理器,需要引用公共库。因为是异步选择模型,所以工程名称命名为asynchronous_server,工程类型是Win32应用程序,空工程。新增一个asynchronous_window类型,继承自CDialogEx类型和iserver_manager接口,头文件代码如下:

#pragma once

#include "afxdialogex.h"
#include <iserver_manager.h>

#define WM_SOCKET WM_USER+1

class asynchronous_window :
	public CDialogEx,
	public iserver_manager
{
	DECLARE_DYNCREATE(asynchronous_window)
	DECLARE_MESSAGE_MAP()
private:
	CBrush br;
	CRect rc;
	int iclient_count;
	int iaddr_size;
	int iport;
	CEdit messageCtrl;
	CFont font;
	SOCKET server;
	SOCKET clients[FD_SETSIZE];

protected:
	bool accept_by_crt();
	bool accept_by_winapi();
	void receive();
	void add_message(LPTSTR msg);
	void disconnect(WPARAM w);

public:
	void shutdown();
	void free();
public:
	asynchronous_window();
	virtual ~asynchronous_window();

protected:
	afx_msg LRESULT on_socket(WPARAM w, LPARAM l);
	afx_msg void OnPaint();
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnDestroy();
};


实现文件代码如下:

#include "asynchronous_window.h"

IMPLEMENT_DYNCREATE(asynchronous_window, CDialogEx)

BEGIN_MESSAGE_MAP(asynchronous_window, CDialogEx)

	ON_MESSAGE(WM_SOCKET, &asynchronous_window::on_socket)
	ON_WM_PAINT()
	ON_WM_CREATE()
	ON_WM_DESTROY()
END_MESSAGE_MAP()

asynchronous_window::asynchronous_window()
{
	br.CreateSolidBrush(GetSysColor(COLOR_3DFACE));
	iport = 5150;
	font.CreateFont(24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"微软雅黑");
	iaddr_size = sizeof(SOCKADDR_IN);
}


asynchronous_window::~asynchronous_window()
{
}


LRESULT asynchronous_window::on_socket(WPARAM w, LPARAM l)
{
	SOCKET client_conn;
	SOCKADDR_IN client_addr;
	CString str;
	char msg[1024] = { 0 };
	int iret = 0;
	LPTSTR lpconvert;

	USES_CONVERSION;
	if (WSAGETSELECTERROR(l))
	{
		closesocket(w);
		return 0;
	}
	switch (WSAGETSELECTEVENT(l))
	{
	case FD_ACCEPT:
		client_conn = accept(w, (struct sockaddr*)&client_addr, &iaddr_size);
		if (client_conn == -1)
		{
			add_message(L"有一个错误的连接被忽略。");
			return 0;
		}
		clients[iclient_count++] = client_conn;
		lpconvert = A2W(inet_ntoa(client_addr.sin_addr));
		str.Format(L"新连接:%s:%d", lpconvert, ntohs(client_addr.sin_port));
		add_message(str.GetBuffer());
		WSAAsyncSelect(client_conn, GetSafeHwnd(), WM_SOCKET, FD_ALL_EVENTS);
		return 0;
	case FD_READ:
		iret = recv(w, msg, 1024, 0);
		if (iret == 0 || (iret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
		{
			disconnect(w);
		}
		else
		{
			msg[iret] = 0;
			send(w, msg, iret, 0);
		}
		return 0;
	case FD_CLOSE:
		disconnect(w);
		return 0;
	default:
		TRACE("消息内容:%d\n", WSAGETSELECTEVENT(l));
		break;
	}
	return 0;
}

void asynchronous_window::disconnect(WPARAM w)
{
	SOCKADDR_IN client_addr;
	CString str;
	LPTSTR lpconvert;
	int i = 0;

	USES_CONVERSION;
	getpeername(w, (struct sockaddr*)&client_addr, &iaddr_size);
	lpconvert = A2W(inet_ntoa(client_addr.sin_addr));
	str.Format(L"%s:%d已断开连接。", lpconvert, ntohs(client_addr.sin_port));
	add_message(str.GetBuffer());
	closesocket(w);
	for (i = 0; i < iclient_count; i++)
	{
		if (clients[i] != w)
			continue;
		if (i < iclient_count - 1)
			clients[i] = clients[iclient_count - 1];
	}
	clients[iclient_count - 1] = 0;
	iclient_count--;
}

void asynchronous_window::OnPaint()
{
	CPaintDC dc(this);

	GetClientRect(&rc);
	dc.FillRect(&rc, &br);

}

bool asynchronous_window::accept_by_crt()
{
	return true;
}

bool asynchronous_window::accept_by_winapi()
{
	WSADATA wsa;
	SOCKET server;
	SOCKADDR_IN conn_addr;
	int iret = 0;

	WSAStartup(MAKEWORD(2, 2), &wsa);
	server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	conn_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	conn_addr.sin_family = AF_INET;
	conn_addr.sin_port = htons(iport);
	while (bind(server, (struct sockaddr*)&conn_addr, iaddr_size) == -1)
	{
		iport++;
		conn_addr.sin_port = htons(iport);
	}
	listen(server, 3);
	WSAAsyncSelect(server, GetSafeHwnd(), WM_SOCKET, FD_ACCEPT);
	add_message(L"服务启动成功");
	return true;
}

void asynchronous_window::add_message(LPTSTR message)
{
	CString logs;

	messageCtrl.GetWindowText(logs);
	logs.Append(message);
	logs.Append(L"\r\n");
	messageCtrl.SetWindowText(logs);
	messageCtrl.SetSel(logs.GetLength(), logs.GetLength());
}

void asynchronous_window::receive()
{

}

void asynchronous_window::shutdown()
{
	closesocket(server);
	WSACleanup();
}

int asynchronous_window::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;
	SetFont(&font);
	messageCtrl.Create(ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | WS_VISIBLE | ES_READONLY, CRect(0, 0, 320, 220), this, 101);
	messageCtrl.SetFont(&font);
	accept_by_winapi();
	return 0;
}


void asynchronous_window::OnDestroy()
{
	__super::OnDestroy();
	shutdown();
}

void asynchronous_window::free()
{
	delete this;
}

新增一个asynchronous_server类型,头文件代码如下:

#pragma once

#include "asynchronous_window.h"

class asynchronous_application :
	public CWinApp
{
private:
	asynchronous_window wnd;

public:
	asynchronous_application();
	virtual ~asynchronous_application();
	virtual BOOL InitInstance();
};


实现文件代码如下:

#include "asynchronous_application.h"


asynchronous_application::asynchronous_application()
{
	m_pMainWnd = new asynchronous_window;
}


asynchronous_application::~asynchronous_application()
{
	delete m_pMainWnd;
}

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	asynchronous_application app;

	AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
	app.InitInstance();
	app.Run();
	return 0;
}

BOOL asynchronous_application::InitInstance()
{
	BOOL r = CWinApp::InitInstance();
	InitApplication();
	m_pMainWnd->CreateEx(0, AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW), L"异步选择模式测试", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CRect(0, 0, 320, 240), NULL, 0);
	return r;
}

这里与迷你MFC做法稍微有所不同,主要区别是继承了CWinApp类型和CDialogEx类型。

运行效果

运行截图如下:


这个编程模型存在一个问题是不知道客户端异常掉线或者正常掉线。解决办法是检测每一个Socket是否通畅。







转载于:https://my.oschina.net/zhtqs/blog/1509803

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值