您的位置:首页 > 理论基础 > 计算机网络

C/S + HTML 桌面网络软件的实现

2009-06-29 15:07 295 查看

C/S + HTML 桌面网络软件的实现

作者:陈明
Email:qmroom@126.com
Blog: http://blog.csdn.net/qmroom
(转载请注明出处)

环境:VC8+SERVER2003/XP
关键字:C/S、HTML、网络

摘要:
纯B/S的软件系统在系统实时性、桌面功能拓展性上有很大的不足;纯C/S的软件系统虽然实时性强、功能拓展强,但在界面美观和开发效率上远不如B/S软件。有没有一种更完美的解决方案呢,既能解决界面美观性、开发高效性,又有很强的实时性和很好的拓展性?解决方案当然有好多种,如Web+控件、C/S +界面控件+其他基础控件库、C/S +Flash+控件库……,我们今天要讨论的是另一种实现模式:C/S + HTML。
C/S + HTML的实现思路(我们以VC为例):服务器端用VC实现服务程序。客户端在VC中嵌入IE控件,以VC为框架HTML为交换界面。这样做有诸多优点:页面开发高效、界面修改方便、实时性高、功能可拓展性强、可在网页里嵌入其他控件、利用js控制MS OFFICE行为实现vba等。而VC部分只需要关注数据的处理,便于功能划分。

下面我们以竞价系统为例说明:

页面展示:



中间是网页,利用js接口或页面元素和vc通信。



VC从服务器端取得数据,绘制折线图。







利用js技术显示word文档,可以打开并操作word文档。

实现:

新建一个vc对话框工程,在对话框中加入LABEL,修改id为:IDC_BROWSER

定义浏览器类:
class CBrowserView : public CHtmlView {
DECLARE_DYNCREATE(CBrowserView)
DECLARE_MESSAGE_MAP()
DECLARE_DISPATCH_MAP() // 构建dispatch映射表以暴露方法或属性

public:
// 当在一个窗体上创建时(当作窗体的一个控件)可以指定一个控件ID按指定的控件大小位置
BOOL CreateFromStatic(UINT nID, CWnd* pParent);
// 调用JS函数
BOOL CallJScript(LPCTSTR strFunc, const CStringArray& paramArray, _variant_t* pVarResult = NULL);
// 修改网页元素的内容
BOOL PutElementHtml(LPCTSTR lpElemID, LPCTSTR lpHtml);
// 取表单元素的值
BOOL GetElementValue(LPCTSTR lpElemID, CString &Value);
// 设置元素的值
BOOL PutElementValue(LPCTSTR lpElemID, LPCTSTR lpValue);

}

在对话框中定义浏览器对象
CBrowserView m_Browser;
在对话框初始化时,创建浏览器 Browser.CreateFromStatic(IDC_BROWSER, this);
指定其实页Browser.Navigate2(“webs/index.html”);

JS接口部分:
在类CBrowserView中实现js接口:

BEGIN_DISPATCH_MAP(CBrowserView, CBrowserView)
DISP_FUNCTION(CBrowserView, "CloseWin", CloseWin, VT_EMPTY, VTS_NONE)
DISP_FUNCTION(CBrowserView, "ShowWin", ShowWin, VT_EMPTY, VTS_NONE)
DISP_FUNCTION(CBrowserView, "HideWin", HideWin, VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()

BrowserView.h头文件声明:
//常用操作
void CloseWin(); //关闭窗口
void ShowWin(); //显示窗口
void HideWin(); //隐藏窗口

BrowserView.cpp实现:

void CBrowserView::CloseWin()
{
GetMainDlg()->PostMessage(WM_CLOSE);
}
void CBrowserView::ShowWin()
{
GetMainDlg()->ShowWindow(SW_SHOW);
GetMainDlg()->UpdateWindow();
}
void CBrowserView::HideWin()
{
GetMainDlg()->ShowWindow(SW_HIDE);
GetMainDlg()->UpdateWindow();
}

同时可以触发JS事件:

//JS事件函数,由C++框架调用
function GetMessage(type)
{
do
{
if('1' == type) //最高价更新了
{
//你需要更新页面显示
//同时,若在延时阶段,你需要重置延时结束时间为GetProjYs()秒,更新页面倒计时钟
alert("最高价更新了");
break;
}

if('2' == type) //开始竞价
{
alert("开始竞价");
break;
}

if('3' == type) //正常竞价结束,进入延时竞价
{
alert("正常竞价结束,进入延时竞价");
break;
}

if('4' == type) //竞价结束
{
alert("竞价结束");
break;
}


if('7' == type) //报价成功【服务器接受了报价】
{
alert("报价成功");
break;
}

if('8' == type) //报价失败【服务器没有接受报价】,失败原因可以通过GetLastErrMsg()获取
{
alert("报价失败:" + external.GetLastErrMsg());
break;
}

}while(false);
}

网络通信实现部分,可采用异步多线程通信:
监听
bool CNet::Listen(int port)
{
if(IsListening())
StopListen();

sockaddr_in addr;
SOCKET sock;

sock = socket(AF_INET, SOCK_STREAM, 0); // Create socket

addr.sin_family = AF_INET; // Address family Internet
addr.sin_port = htons(port); // Assign port 'port' to this socket
addr.sin_addr.s_addr = htonl(INADDR_ANY); // No destination

if(bind(sock,(LPSOCKADDR) &addr, sizeof(addr)) == SOCKET_ERROR)
{
closesocket(sock);
long err = WSAGetLastError();
CNet::SetLastError(err);
return false;
}

if(listen(sock, BACKLOG) == SOCKET_ERROR)
{
closesocket(sock);
long err = WSAGetLastError();
CNet::SetLastError(err);
return false;
}

m_sync.Enter();
m_socket = sock;
m_addr = addr;
m_sync.Leave();

hAcceptThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) AcceptThread,
this,
0,
(LPDWORD) &dwAcceptThreadID);
if(!hAcceptThread)
{
StopListen();
return false;
}

return true;
}

int CNet::AcceptWait()
{
int addr_size = sizeof(sockaddr_in);

SOCKET lsock;
SOCKET csock = INVALID_SOCKET;
sockaddr_in caddr = { 0 };

m_sync.Enter();
lsock = m_socket;
m_sync.Leave();

while(IsListening())
{
m_sync.Enter();
bool bRightSock =(lsock == m_socket);
m_sync.Leave();
if(!bRightSock) return 1;

csock = accept(lsock,(LPSOCKADDR) &caddr, &addr_size); // accept a connection
if(csock == INVALID_SOCKET)
{
int aErr = WSAGetLastError();
if(aErr == WSAEINTR)
{
StopListen();
return 1;
}
else
{
// error: could not accept
long err = WSAGetLastError();
CNet::SetLastError(err);

StopListen();
return 1;
}
}
else
{
CConnection* cAccepted = new CConnection();
cAccepted->m_socket = csock;
cAccepted->m_addr = caddr;

cAccepted->hRecvThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) cAccepted->RecvThread,
cAccepted,
0,
(LPDWORD) &cAccepted->dwRecvThreadID);

if(cAccepted->hRecvThread == NULL)
{
cAccepted->Disconnect();
CNet::SetLastError(1002);
}
else
{
m_sync.Enter();
m_accepted.Add(cAccepted);

HANDLE hEvent = hAcceptEvent;
CALLBACKFUNC fncCallback = hAcceptFunc;
m_sync.Leave();

if(hEvent) SetEvent(hEvent); // if an event was given, pulse the event
if(fncCallback)(fncCallback)((DWORD) this); // if a function ptr was given, call the function
}
}
}

return 0;
}

int CNet::AcceptThread(void* pThis)
{
return((CNet*)(pThis))->AcceptWait();
}

连接:
bool CCon::Connect(const char* host, unsigned short port)
{
Disconnect();

sockaddr_in addr;
SOCKET sock;

sock = socket(AF_INET, SOCK_STREAM, 0); // create socket
addr.sin_family = AF_INET; // address family Internet
addr.sin_port = htons(port); // set server抯port number
addr.sin_addr.s_addr = inet_addr(host); // set server抯IP

if(connect(sock, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)
{
long err = WSAGetLastError();
CConnection::SetLastError(err);
return false;
}

linger lg;
lg.l_onoff = 1;
lg.l_linger = 10;
setsockopt(sock, SOL_SOCKET, SO_LINGER, reinterpret_cast <const char*>(&lg), sizeof(linger));

hRecvThread = CreateThread(NULL
, 0
, (LPTHREAD_START_ROUTINE) RecvThread
, this
, 0
, (LPDWORD) &dwRecvThreadID);

if(hRecvThread == NULL)
{
CConnection::SetLastError(1002);
closesocket(sock);
return false;
}

m_sync.Enter();
m_socket = sock;
m_addr = addr;
m_sync.Leave();

return true;
}

至此,C/S + HTML的框架已经搭建完成,剩下的就你们自由发挥了。J
对于这个框架,当然也有缺点,缺点就是需要在客户端安装一个程序。
由于本人水平有限,本文难免有错误疏漏之处,欢迎来信交流指正:qmroom@126.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: