Socket编程模型之异步选择模型
2016-09-17 15:10
453 查看
简介
核心仍然是用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是否通畅。
相关文章推荐
- .NET平台下几种SOCKET编程通信模型选择以及简要性能供参考解决方案
- 续实例解析SOCKET编程模型之异步通信篇-客户端
- Socket编程模型之事件选择模型
- 续实例解析SOCKET编程模型之异步通信篇(上)
- socket网络编程 服务器模型选择
- Linux编程的socket阻塞与非阻塞,同步与异步、I/O模型
- 利用线程池实现多客户端和单服务器端Socket通讯(二):异步编程模型实现
- (二)Socket I/O模型之异步选择(WSAAsyncSelect)
- Socket I/O模型之异步选择(WSAAsyncSelect)
- 异步/多线程/任务/并行编程之一:如何选择合适的多线程模型?
- 续实例解析SOCKET编程模型之异步通信篇(下)
- 续实例解析SOCKET编程模型之异步通信篇-服务器
- Socket I/O模型之异步选择(WSAAsyncSelect)
- 异步/多线程/任务/并行编程之一:如何选择合适的多线程模型?
- 续实例解析SOCKET编程模型之异步通信篇(上) 代码
- 异步/多线程/任务/并行编程之一:如何选择合适的多线程模型?(转)
- socket模型-异步事件选择模型的正常退出
- 异步、多线程、任务、并行编程之一:选择合适的多线程模型
- Socket I/O模型之异步选择(WSAAsyncSelect)(转)
- 续实例解析SOCKET编程模型之异步通信篇(下)