最近的Silverlight开发中,由于部分需求对实时性和数据量下载速度有要求,部分WCF服务配置成了netTcpBinding,这种方式跟普通的service.svc寄宿IIS不同的是,Silverlight需要的策略文件需要放置在本机IIS的根下,也就是wwwroot文件夹下,以满足Silverlight在以TCP协议调用本机WCF服务时请求策略文件。
(注:Silverlight通过TCP协议调用WCF服务时,会以http方式请求主机的一个策略文件,地址是http://localhost/clientaccesspolicy.xml)
这其实是个不太好的选择,程序运行的所需的环境被分成了两部分,同事的机器上并未安装IIS,为了大家开发简便,不用在额外安装IIS,也为了让程序更加独立,我就想能不能写代码监控80端口模拟IIS向Silverlight输出这个策略文件。
解决方法有了这个想法之后,首先想到的是通过Socket进行监听,因为此前在MSDN上看到过这种方式,但很无奈,将代码转移过来之后,并未成功。相信做过Silverlight在Socket方面应用的朋友对下面这个PolicyServer类很熟悉吧。
代码@H_419_28@ using@H_419_28@ System;
@H_419_28@ using@H_419_28@ System.IO;
@H_419_28@ using@H_419_28@ System.Net;
@H_419_28@ using@H_419_28@ System.Net.sockets;
@H_419_28@ namespace@H_419_28@ PolicyServer
{
@H_419_28@ //@H_419_28@ Encapsulate and manage state for a single connection from a clIEnt@H_419_28@
@H_419_28@ class@H_419_28@ PolicyConnection
{
@H_419_28@ private@H_419_28@ Socket m_connection;
@H_419_28@ //@H_419_28@ buffer to receive the request from the clIEnt@H_419_28@
@H_419_28@ @H_419_28@ private@H_419_28@ @H_419_28@ byte@H_419_28@ [] m_buffer;
@H_419_28@ private@H_419_28@ @H_419_28@ int@H_419_28@ m_received;
@H_419_28@ //@H_419_28@ the policy to return to the clIEnt@H_419_28@
@H_419_28@ @H_419_28@ private@H_419_28@ @H_419_28@ byte@H_419_28@ [] m_policy;
@H_419_28@ //@H_419_28@ the request that we're expecting from the clIEnt@H_419_28@
@H_419_28@ @H_419_28@ private@H_419_28@ @H_419_28@ static@H_419_28@ @H_419_28@ string@H_419_28@ s_policyRequestString @H_419_28@ =@H_419_28@ @H_419_28@ "@H_419_28@ <policy-file-request/>@H_419_28@ "@H_419_28@ ;
@H_419_28@ public@H_419_28@ PolicyConnection(Socket clIEnt, @H_419_28@ byte@H_419_28@ [] policy)
{
m_connection @H_419_28@ =@H_419_28@ clIEnt;
m_policy @H_419_28@ =@H_419_28@ policy;
m_buffer @H_419_28@ =@H_419_28@ @H_419_28@ new@H_419_28@ @H_419_28@ byte@H_419_28@ [s_policyRequestString.Length];
m_received @H_419_28@ =@H_419_28@ @H_419_28@ 0@H_419_28@ ;
@H_419_28@ try@H_419_28@
{
@H_419_28@ //@H_419_28@ receive the request from the clIEnt@H_419_28@
@H_419_28@ m_connection.BeginReceive(m_buffer, @H_419_28@ 0@H_419_28@ , s_policyRequestString.Length, SocketFlags.None, @H_419_28@ new@H_419_28@ AsyncCallback(OnReceive), @H_419_28@ null@H_419_28@ );
}
@H_419_28@ catch@H_419_28@ (SocketException)
{
m_connection.Close();
}
}
@H_419_28@ //@H_419_28@ Called when we receive data from the clIEnt@H_419_28@
@H_419_28@ @H_419_28@ private@H_419_28@ @H_419_28@ voID@H_419_28@ OnReceive(IAsyncResult res)
{
@H_419_28@ try@H_419_28@
{
m_received @H_419_28@ +=@H_419_28@ m_connection.EndReceive(res);
@H_419_28@ //@H_419_28@ if we haven't gotten enough for a full request yet, receive again@H_419_28@
@H_419_28@ @H_419_28@ if@H_419_28@ (m_received @H_419_28@ <@H_419_28@ s_policyRequestString.Length)
{
m_connection.BeginReceive(m_buffer, m_received, s_policyRequestString.Length @H_419_28@ -@H_419_28@ m_received, @H_419_28@ null@H_419_28@ );
@H_419_28@ return@H_419_28@ ;
}
@H_419_28@ //@H_419_28@ make sure the request is valID@H_419_28@
@H_419_28@ @H_419_28@ string@H_419_28@ request @H_419_28@ =@H_419_28@ System.Text.EnCoding.UTF8.GetString(m_buffer, m_received);
@H_419_28@ if@H_419_28@ (StringComparer.InvariantCultureIgnoreCase.Compare(request, s_policyRequestString) @H_419_28@ !=@H_419_28@ @H_419_28@ 0@H_419_28@ )
{
m_connection.Close();
@H_419_28@ return@H_419_28@ ;
}
@H_419_28@ //@H_419_28@ send the policy@H_419_28@
@H_419_28@ m_connection.BeginSend(m_policy, m_policy.Length, @H_419_28@ new@H_419_28@ AsyncCallback(OnSend), @H_419_28@ null@H_419_28@ );
}
@H_419_28@ catch@H_419_28@ (SocketException)
{
m_connection.Close();
}
}
@H_419_28@ //@H_419_28@ called after sending the policy to the clIEnt; close the connection.@H_419_28@
@H_419_28@ @H_419_28@ public@H_419_28@ @H_419_28@ voID@H_419_28@ OnSend(IAsyncResult res)
{
@H_419_28@ try@H_419_28@
{
m_connection.EndSend(res);
}
@H_419_28@ finally@H_419_28@
{
m_connection.Close();
}
}
}
@H_419_28@ //@H_419_28@ Listens for connections on port 943 and dispatches requests to a PolicyConnection@H_419_28@
@H_419_28@ class@H_419_28@ PolicyServer
{
@H_419_28@ private@H_419_28@ Socket m_Listener;
@H_419_28@ private@H_419_28@ @H_419_28@ byte@H_419_28@ [] m_policy;
@H_419_28@ //@H_419_28@ pass in the path of an XML file containing the socket policy@H_419_28@
@H_419_28@ @H_419_28@ public@H_419_28@ PolicyServer(@H_419_28@ string@H_419_28@ policyfile)
{
@H_419_28@ //@H_419_28@ Load the policy file@H_419_28@
@H_419_28@ fileStream policyStream @H_419_28@ =@H_419_28@ @H_419_28@ new@H_419_28@ fileStream(policyfile, fileMode.Open);
m_policy @H_419_28@ =@H_419_28@ @H_419_28@ new@H_419_28@ @H_419_28@ byte@H_419_28@ [policyStream.Length];
policyStream.Read(m_policy, m_policy.Length);
policyStream.Close();
m_Listener @H_419_28@ =@H_419_28@ @H_419_28@ new@H_419_28@ Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
m_Listener.SetSocketoption(SocketoptionLevel.IPv6, (Socketoptionname)@H_419_28@ 27@H_419_28@ , @H_419_28@ 0@H_419_28@ );
m_Listener.Bind(@H_419_28@ new@H_419_28@ IPEndPoint(IPAddress.IPv6Any, @H_419_28@ 943@H_419_28@ ));
m_Listener.Listen(@H_419_28@ 10@H_419_28@ );
m_Listener.BeginAccept(@H_419_28@ new@H_419_28@ AsyncCallback(OnConnection), @H_419_28@ null@H_419_28@ );
}
@H_419_28@ public@H_419_28@ @H_419_28@ voID@H_419_28@ OnConnection(IAsyncResult res)
{
Socket clIEnt @H_419_28@ =@H_419_28@ @H_419_28@ null@H_419_28@ ;
@H_419_28@ try@H_419_28@
{
clIEnt @H_419_28@ =@H_419_28@ m_Listener.EndAccept(res);
}
@H_419_28@ catch@H_419_28@ (SocketException)
{
@H_419_28@ return@H_419_28@ ;
}
@H_419_28@ //@H_419_28@ handle this policy request with a PolicyConnection@H_419_28@
@H_419_28@ PolicyConnection pc @H_419_28@ =@H_419_28@ @H_419_28@ new@H_419_28@ PolicyConnection(clIEnt, m_policy);
@H_419_28@ //@H_419_28@ look for more connections@H_419_28@
@H_419_28@ m_Listener.BeginAccept(@H_419_28@ new@H_419_28@ AsyncCallback(OnConnection), @H_419_28@ null@H_419_28@ );
}
@H_419_28@ public@H_419_28@ @H_419_28@ voID@H_419_28@ Close()
{
m_Listener.Close();
}
}
@H_419_28@ public@H_419_28@ @H_419_28@ class@H_419_28@ Program
{
@H_419_28@ static@H_419_28@ @H_419_28@ voID@H_419_28@ Main(@H_419_28@ string@H_419_28@ [] args)
{
@H_419_28@ if@H_419_28@