一,需求的提出
我的应用程序需要访问一个webservice,webservcie 的数据时从socket读取的,在实时变化,当需求变化时,如果服务端没有现成的数据的话,我自己得模拟数据,
在IIS建立网站,自己构建webservcie,然后添加数据。我就想啊,不使用IIS,直接用应用程序来提供webservcie 的方法。刚开始想用WCF,但是google了半天,就没有用wcf 生成webservcie 的方法,或者是我rp不够。
二,解决方案
终于,找到了解决的方法,用winform程序承载IIS 服务,就是建立winform程序,监听tcplistener。这样你通过浏览器访问,就可以用应用程序处理了。
下面是参考的方法:
http://www.microsoft.com/china/MSDN/library/WebServices/WebServices/ServiceStation.mspx?mfr=true
http://msdn.microsoft.com/zh-cn/magazine/cc163879(en-us).aspx#fig5
三,具体实施
下载源代码,运行程序。
下面是网上抄的,我把他单独放在一个dll里,为什么了?原因下面会讲解。
我的应用程序需要访问一个webservice,webservcie 的数据时从socket读取的,在实时变化,当需求变化时,如果服务端没有现成的数据的话,我自己得模拟数据,
在IIS建立网站,自己构建webservcie,然后添加数据。我就想啊,不使用IIS,直接用应用程序来提供webservcie 的方法。刚开始想用WCF,但是google了半天,就没有用wcf 生成webservcie 的方法,或者是我rp不够。
二,解决方案
终于,找到了解决的方法,用winform程序承载IIS 服务,就是建立winform程序,监听tcplistener。这样你通过浏览器访问,就可以用应用程序处理了。
下面是参考的方法:
http://www.microsoft.com/china/MSDN/library/WebServices/WebServices/ServiceStation.mspx?mfr=true
http://msdn.microsoft.com/zh-cn/magazine/cc163879(en-us).aspx#fig5
三,具体实施
下载源代码,运行程序。
下面是网上抄的,我把他单独放在一个dll里,为什么了?原因下面会讲解。
using
System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Web.Hosting;
namespace MyasmxHost
{
// ASPDOTNETHost类实例需要跨越两个应用程序域,所以继承自MarshalByRefObject
public class ASPDOTNETHost:MarshalByRefObject
{
public void ProcessRequest( string fileName, ref string output)
{
MemoryStream ms = new MemoryStream(); // 内存流,当然为了速度
StreamWriter sw = new StreamWriter(ms); // 输出
sw.AutoFlush = true ; // 设为自动刷新 /先构造一个HttpWorkRequest请求类,以便ASP.NET能够分析获取请求信息,同时传入一个输出流对象供ASP.NET执行期间返回html流
HttpWorkerRequest worker = new SimpleWorkerRequest(fileName, "" , sw); // 调度某个页,这里面的包含很多细节,后面分析
HttpRuntime.ProcessRequest(worker);
StreamReader sr = new StreamReader(ms); // 准备从内存流中读取
ms.Position = 0 ; // 移动指针到头
output = sr.ReadToEnd();
}
}
}
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Web.Hosting;
namespace MyasmxHost
{
// ASPDOTNETHost类实例需要跨越两个应用程序域,所以继承自MarshalByRefObject
public class ASPDOTNETHost:MarshalByRefObject
{
public void ProcessRequest( string fileName, ref string output)
{
MemoryStream ms = new MemoryStream(); // 内存流,当然为了速度
StreamWriter sw = new StreamWriter(ms); // 输出
sw.AutoFlush = true ; // 设为自动刷新 /先构造一个HttpWorkRequest请求类,以便ASP.NET能够分析获取请求信息,同时传入一个输出流对象供ASP.NET执行期间返回html流
HttpWorkerRequest worker = new SimpleWorkerRequest(fileName, "" , sw); // 调度某个页,这里面的包含很多细节,后面分析
HttpRuntime.ProcessRequest(worker);
StreamReader sr = new StreamReader(ms); // 准备从内存流中读取
ms.Position = 0 ; // 移动指针到头
output = sr.ReadToEnd();
}
}
}
于是乎,我开始运行程序了,但是 到调用 ASPDOTNETHost的时候,老是抛出异常,
“未能加载MyasmxHost的一部分”。这个问题困扰了我很久,继续google,牛人的解答是: MyasmxHost必须部署到GAC中。这就是 我把ASPDOTNETHost单独放在一个dll里边的用意。
1,应用程序的私有部署与全局。
私有部署,即你自己构建的应用程序所需要的dl都在应用程序的目录下边。全局部署,即把dll或者exe 部署到GAC中。
2,强命名程序集:
使用sn.exe -i mycompany.keys
mycompany.keys 放到dll里边 在AssemblyInfo.cs里边添加一行 [assembly: AssemblyKeyFile("myasmxhost.keys")]。
3,部署到gac中 。使用 gacutil.exe -i *.dll 部署到GAC中
@echo
off
:: 设置gacutil.exe 的位置
set gacu = gacutil . exe
:: 执行部署gac
start %gacu% -i MyasmxHost . dll
:: 设置gacutil.exe 的位置
set gacu = gacutil . exe
:: 执行部署gac
start %gacu% -i MyasmxHost . dll
这样刚才的异常就木有了。一切都ok了。看看服务端的处理方式了。
服务端myserver
public MyServer(string virtualDir, string realPath,int port)
{//在构造函数中启动web监听服务
try
{
//固定端口添加TcpListerner
mytcp = new TcpListener(port);
mytcp.Start();
//利用CreateApplicationHost方法建立一个独立的应用程序域执行ASP.NET程序
ASPnetHost = (ASPDOTNETHost)ApplicationHost.CreateApplicationHost
(typeof(ASPDOTNETHost), virtualDir, realPath);
t = new Thread(new ThreadStart(MainSvcThread));
t.Start();
}
catch (NullReferenceException)
{
Debug.Print("NullReferenceException throwed!");
}
}
{//在构造函数中启动web监听服务
try
{
//固定端口添加TcpListerner
mytcp = new TcpListener(port);
mytcp.Start();
//利用CreateApplicationHost方法建立一个独立的应用程序域执行ASP.NET程序
ASPnetHost = (ASPDOTNETHost)ApplicationHost.CreateApplicationHost
(typeof(ASPDOTNETHost), virtualDir, realPath);
t = new Thread(new ThreadStart(MainSvcThread));
t.Start();
}
catch (NullReferenceException)
{
Debug.Print("NullReferenceException throwed!");
}
}
(1),添加头层皮listener ,在固定端口,
(2),利用CreateApplicationHost方法建立一个独立的应用程序域执行ASP.NET程序
主线程里边需要做的就是获取接收到的东西,然后发送出去。
public void MainSvcThread() //ASP.NET Host的web服务器的主要服务线程
{
int s = 0;
string strRequest; //请求信息
string strDir; //请求的目录
string strRequestFile; //请求的文件名
string strErr =""; //错误信息
string strRealDir=@"d:\test"; //实际目录
string strWebRoot = @"d:\test"; //应用根目录
string strRealFile = ""; //正在请求的文件的磁盘路径
string strResponse = ""; //回应响应缓冲区
string strMsg = ""; //格式化响应信息
byte[] bs; //输出字节缓冲区
while (bSvcRunning)
{
Socket sck = mytcp.AcceptSocket(); //每个请求到来
try
{
if (sck.Connected)
{
Debug.Print("Client {0} connected!", sck.RemoteEndPoint);
byte[] bRecv = new byte[1024]; //缓冲区
int l = sck.Receive(bRecv, bRecv.Length, 0);
string strBuf = Encoding.Default.GetString(bRecv); //转换成字符串,便于分析
s = strBuf.IndexOf("HTTP", 1);
string httpver = strBuf.Substring(s, 8); // HTTP/1.1 之类的
strRequest = strBuf.Substring(0, s - 1);
strRequest.Replace("\\", "/");
if ((strRequest.IndexOf(".") < 1) && (!strRequest.EndsWith("/")))
{
strRequest += "/";
}
s = strRequest.LastIndexOf("/") + 1;
strRequestFile = strRequest.Substring(s); strDir = strRequest.Substring(strRequest.IndexOf("/"), strRequest.LastIndexOf("/") - 3); //取得访问的URL
if (strDir == "/")
{
strRealDir = strWebRoot;
}
else
{
strDir = strDir.Replace("/", "\\");
strRealDir = strWebRoot + strDir;
}
Debug.Print("Client request dir: {0}", strRealDir);
if (strRequestFile.Length == 0)
{
strRequestFile = "default.html"; //缺省文档
}
int iTotlaBytes = 0; //总计需要输出的字节
strResponse = ""; //输出内容
strRealFile = strRealDir + "\\" + strRequestFile;
// 处理WebMonitorSvr.asmx?wsdl 的情况
if (strRealFile.IndexOf("WebMonitorSvr.asmx?wsdl")!=-1)
{
strRealFile = @"D:\test\WebMonitorSvr.asmx";
}
if (strRealFile.EndsWith(".ASPx")) //这里有Bug!!
{
string output = "";
//注意我下面的语句们给host对象ProcessRequest方法传递了一个ref类型的参数,
//ASPnetHost会从ASP.NET的执行应用程序域执行一个请求后返回流给当前web server所在的域,这实际上发生了一个域间的调用
ASPnetHost.ProcessRequest(strRequestFile, ref output);//转换成字节流
bs = System.Text.Encoding.Default.GetBytes(output);
iTotlaBytes = bs.Length; //调用套接字将执行结果返回
WriteHeader(httpver, "text/html", iTotlaBytes, "200 OK", ref sck);
FlushBuf(bs, ref sck);
}
else
{
try
{
fs = new FileStream(strRealFile, FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryReader reader = new BinaryReader(fs); //读取
bs = new byte[fs.Length];
int rb;
while ((rb = reader.Read(bs, 0, bs.Length)) != 0)
{
strResponse = strResponse + Encoding.Default.GetString(bs, 0, rb);
iTotlaBytes = iTotlaBytes + rb;
}
reader.Close();
fs.Close();
WriteHeader(httpver, "text/html", iTotlaBytes, "200 OK", ref sck);
FlushBuf(bs, ref sck);
}
catch (System.IO.FileNotFoundException)
{//假设找不到文件,报告404 WriteHeader(httpver,"text/html",iTotlaBytes,"404 OK",ref sck);
Debug.Print("404 错误");
}
}
}
}
catch (System.Exception ex)
{
Debug.Print(ex.Message);
}
finally
{
sck.Close(); //Http请求结束
}
}
}
{
int s = 0;
string strRequest; //请求信息
string strDir; //请求的目录
string strRequestFile; //请求的文件名
string strErr =""; //错误信息
string strRealDir=@"d:\test"; //实际目录
string strWebRoot = @"d:\test"; //应用根目录
string strRealFile = ""; //正在请求的文件的磁盘路径
string strResponse = ""; //回应响应缓冲区
string strMsg = ""; //格式化响应信息
byte[] bs; //输出字节缓冲区
while (bSvcRunning)
{
Socket sck = mytcp.AcceptSocket(); //每个请求到来
try
{
if (sck.Connected)
{
Debug.Print("Client {0} connected!", sck.RemoteEndPoint);
byte[] bRecv = new byte[1024]; //缓冲区
int l = sck.Receive(bRecv, bRecv.Length, 0);
string strBuf = Encoding.Default.GetString(bRecv); //转换成字符串,便于分析
s = strBuf.IndexOf("HTTP", 1);
string httpver = strBuf.Substring(s, 8); // HTTP/1.1 之类的
strRequest = strBuf.Substring(0, s - 1);
strRequest.Replace("\\", "/");
if ((strRequest.IndexOf(".") < 1) && (!strRequest.EndsWith("/")))
{
strRequest += "/";
}
s = strRequest.LastIndexOf("/") + 1;
strRequestFile = strRequest.Substring(s); strDir = strRequest.Substring(strRequest.IndexOf("/"), strRequest.LastIndexOf("/") - 3); //取得访问的URL
if (strDir == "/")
{
strRealDir = strWebRoot;
}
else
{
strDir = strDir.Replace("/", "\\");
strRealDir = strWebRoot + strDir;
}
Debug.Print("Client request dir: {0}", strRealDir);
if (strRequestFile.Length == 0)
{
strRequestFile = "default.html"; //缺省文档
}
int iTotlaBytes = 0; //总计需要输出的字节
strResponse = ""; //输出内容
strRealFile = strRealDir + "\\" + strRequestFile;
// 处理WebMonitorSvr.asmx?wsdl 的情况
if (strRealFile.IndexOf("WebMonitorSvr.asmx?wsdl")!=-1)
{
strRealFile = @"D:\test\WebMonitorSvr.asmx";
}
if (strRealFile.EndsWith(".ASPx")) //这里有Bug!!
{
string output = "";
//注意我下面的语句们给host对象ProcessRequest方法传递了一个ref类型的参数,
//ASPnetHost会从ASP.NET的执行应用程序域执行一个请求后返回流给当前web server所在的域,这实际上发生了一个域间的调用
ASPnetHost.ProcessRequest(strRequestFile, ref output);//转换成字节流
bs = System.Text.Encoding.Default.GetBytes(output);
iTotlaBytes = bs.Length; //调用套接字将执行结果返回
WriteHeader(httpver, "text/html", iTotlaBytes, "200 OK", ref sck);
FlushBuf(bs, ref sck);
}
else
{
try
{
fs = new FileStream(strRealFile, FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryReader reader = new BinaryReader(fs); //读取
bs = new byte[fs.Length];
int rb;
while ((rb = reader.Read(bs, 0, bs.Length)) != 0)
{
strResponse = strResponse + Encoding.Default.GetString(bs, 0, rb);
iTotlaBytes = iTotlaBytes + rb;
}
reader.Close();
fs.Close();
WriteHeader(httpver, "text/html", iTotlaBytes, "200 OK", ref sck);
FlushBuf(bs, ref sck);
}
catch (System.IO.FileNotFoundException)
{//假设找不到文件,报告404 WriteHeader(httpver,"text/html",iTotlaBytes,"404 OK",ref sck);
Debug.Print("404 错误");
}
}
}
}
catch (System.Exception ex)
{
Debug.Print(ex.Message);
}
finally
{
sck.Close(); //Http请求结束
}
}
}
这里我设置d:\test 为虚拟目录,里边存放了一个default.html 文件。
主程序图:
浏览器效果:
源码: