在Silverlight中進行通訊,只能使用4502-4534之間的端口進行數據傳輸,另外Silverlight客戶端會自動向943端口的服務器端發送一個“<policy-file-request/>”的語句請求,然后服務器端943端口回發以下文件以許可Socket通訊。
<?xml version="1.0" encoding="utf-8" ?> <access-policy> <cross-domain-access> <policy> <allow-from> <domain uri="*"/> </allow-from> <grant-to> <socket-resource port="4502-4534" protocol="tcp"/> </grant-to> </policy> </cross-domain-access> </access-policy>
A.現在我們首先來看服務器端的代碼,主要分為策略響應步驟和服務響應步驟。
策略步驟一:啟動監聽943端口是否有需要安全策略文件請求
策略步驟二:如果客戶端請求是<policy-file-request/>,則將安全策略文件作為bytes發送給客戶端
服務步驟一:啟動服務器端,監聽4525端口,是否有Socket對話請求
服務步驟二:如果有客戶端請求的連接,則發送消息告知客戶端
代碼如下:
class Program { static void Main(string[] args) { //策略步驟一:啟動監聽943端口是否有需要安全策略文件請求 Thread access = new Thread(new ThreadStart(accessThread)); access.Start(); //服務步驟一:啟動服務器端,監聽4525端口,是否有Socket對話請求 Thread server = new Thread(new ThreadStart(ServerThread)); server.Start(); } //策略請求監聽 static void accessThread() { //獲取943端口監聽的Socket服務端 Socket socket = GetSocketServer(943); while (true) { Socket new_access = socket.Accept(); string clientPolicyString = "<policy-file-request/>"; byte[] requestbytes = new byte[clientPolicyString.Length]; new_access.Receive(requestbytes); string requeststring = System.Text.Encoding.UTF8.GetString(requestbytes, 0, requestbytes.Length); if (requeststring == clientPolicyString) { //策略步驟二:如果客戶端請求是<policy-file-request/>,則將安全策略文件作為bytes發送給客戶端 byte[] accessbytes = GetPolicyToClient(); new_access.Send(accessbytes, accessbytes.Length, SocketFlags.None); new_access.Close(); } Thread.Sleep(100); } } static void ServerThread() { //獲取4525端口監聽的Socket服務端 Socket socket = GetSocketServer(4525); while (true) { Socket _socket = socket.Accept(); //服務步驟二:如果有客戶端請求的連接,則發送消息告知客戶端 byte[] b2 = new byte[1024]; _socket.Receive(b2); Console.WriteLine(Encoding.UTF8.GetString(b2).Replace("\0", "")); string recString = "我已經收到消息了"; _socket.Send(Encoding.UTF8.GetBytes(recString)); _socket.Close(); Thread.Sleep(100); } } //根據端口建立Socket服務器端 static Socket GetSocketServer(int serverPort) { Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Any, serverPort)); socket.Listen(40); return socket; } //獲取安全策略文件的byte[] static byte[] GetPolicyToClient() { string path = Environment.CurrentDirectory.Replace("\\bin\\Debug",""); FileStream fs = new FileStream(path+ @"\clientaccesspolicy.xml", FileMode.Open); int length = (int)fs.Length; byte[] bytes = new byte[length]; fs.Read(bytes, 0, length); fs.Close(); return bytes; } }
B.其次我們來看客戶端操作,分為以下幾個步驟:
客戶端步驟一:發起服務器連接請求。
客戶端步驟二:連接服務器成功,將需要發送的數據放入緩沖區中,然后異步向服務器發送消息請求
客戶端步驟三:消息發送成功,此時設置一個新的緩沖區實例,並且發起異步接收服務器返回的消息
客戶端步驟四:獲取到服務器返回的消息,關閉Socket
客戶端cs代碼如下:
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); } System.Net.Sockets.Socket socket; private void button1_Click(object sender, RoutedEventArgs e) { byte[] userbytes = Encoding.UTF8.GetBytes(this.tbInput.Text); socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs(); socketArgs.RemoteEndPoint = new DnsEndPoint("127.0.0.1", 4525); //將需要發送的內容轉為byte[],保存到UserToken屬性中 socketArgs.UserToken = userbytes; socketArgs.Completed += new EventHandler<SocketAsyncEventArgs>(socketArgs_Completed); //客戶端步驟一:發起服務器連接請求。 socket.ConnectAsync(socketArgs); } //每發生一個Socket操作都講激活此方法,操作包括(Connect/Send/Receive/None) void socketArgs_Completed(object sender, SocketAsyncEventArgs e) { if (e.LastOperation == SocketAsyncOperation.Connect) { //客戶端步驟二:連接服務器成功,將需要發送的數據放入緩沖區中,然后異步向服務器發送消息請求 byte[] userbytes = (byte[])e.UserToken; e.SetBuffer(userbytes, 0, userbytes.Length); socket.SendAsync(e); } else if (e.LastOperation == SocketAsyncOperation.Send) { //客戶端步驟三:消息發送成功,此時設置一個新的緩沖區實例,並且發起異步接收服務器返回的消息 byte[] userbytes = new byte[1024]; e.SetBuffer(userbytes, 0, userbytes.Length); socket.ReceiveAsync(e); } else if (e.LastOperation == SocketAsyncOperation.Receive) { //客戶端步驟四:獲取到服務器返回的消息,關閉Socket string RecevieStr = Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length).Replace("\0", ""); //因為是異步Socket請求,所以需要使用UI線程更新lbShowMessage的顯示效果 this.lbShowMessage.Dispatcher.BeginInvoke(new DoThingDele(DoThing), RecevieStr); socket.Close(); } } //更新UI public void DoThing(string arg) { this.lbShowMessage.Content = this.lbShowMessage.Content + "->" + arg; } //聲明的一個DoThing方法委托 public delegate void DoThingDele(string arg); }
客戶端Xaml前台代碼如下:
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True"> <TextBox Height="23" HorizontalAlignment="Left" Margin="20,20,0,0" Name="tbInput" VerticalAlignment="Top" Width="243" /> <Button Content="發 送" Height="23" HorizontalAlignment="Left" Margin="279,20,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" /> <sdk:Label Height="28" HorizontalAlignment="Left" Margin="20,57,0,0" Name="lbShowMessage" VerticalAlignment="Top" Width="358" /> </Grid>
最后效果如下,如需源碼請點擊 SLSocket.zip 下載,本文演示的是最簡單通訊效果: