您的位置:首页 > 编程语言

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是否通畅。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息