Silverlight Socket通信學(xué)習(xí)筆記
之前因?yàn)轫?xiàng)目的關(guān)系,涉及到與服務(wù)器實(shí)時(shí)通信,比如通過GPRS將GPS的位置信息等信息發(fā)送到服務(wù)器,然后再轉(zhuǎn)發(fā)給Silverlight應(yīng)用程序,最后在地圖上標(biāo)示出實(shí)時(shí)的地理位置,查了查相關(guān)的資料,網(wǎng)上給出的比較好的方法就是利用Socket與服務(wù)器通信。于是這兩天看了看Silverlight下的Socket通信,在此將學(xué)習(xí)的心得和實(shí)現(xiàn)過程作一個(gè)記錄,以供相互學(xué)習(xí)和交流。
園子里關(guān)于這方面的內(nèi)容已經(jīng)有很多大神寫過了,這里小小的推薦一下:
http://www.cnblogs.com/webabcd/archive/2008/12/22/1359551.html
因此本文的重點(diǎn)知識(shí)說一下具體實(shí)現(xiàn)的過程,細(xì)節(jié)和原理性的東西不會(huì)太多,因?yàn)楸救艘彩切率?,所以就不賣弄了。之前說到和地圖結(jié)合,所以本文的后續(xù)工作將會(huì)把Silverlight的Socket通信與ArcGIS 的地圖結(jié)合,來(lái)實(shí)現(xiàn)一個(gè)小小的功能,而本篇?jiǎng)t主要關(guān)于Socket的實(shí)現(xiàn)過程。下面就進(jìn)入正題吧。
一.Silverlight的Socket通信和控制臺(tái)、WinForm下的Socket通信有很大的區(qū)別。
對(duì)于后兩者的Socket通信,其過程就是開啟端口,綁定端口,監(jiān)聽端口,連接,接收數(shù)據(jù),發(fā)送數(shù)據(jù)。
而在Silverlight中則不太一樣,在Silverlight中,首先是Silverlight客戶端自動(dòng)向943端口的服務(wù)器端發(fā)送一個(gè)“<policy-file-request/>”的語(yǔ)句請(qǐng)求,然后服務(wù)器端向客戶端發(fā)送策略文件:
clientaccesspolicy.xml,例如:
- <?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>
 
發(fā)送之后,才允許和服務(wù)器進(jìn)行Socket通信,之后的過程則都是一樣。
二、服務(wù)器端
2.1、Silverligh中發(fā)送策略文件服務(wù)
上面說到,Silverlight中,服務(wù)器端會(huì)向客戶端發(fā)送策略文件,然后才能開始Socket通信,下面給出服務(wù)器端的發(fā)送策略文件服務(wù)的代碼,該代碼是在網(wǎng)上找的,是別人已經(jīng)寫好的一個(gè)類,所以在編寫Silverlight 的Socket通信程序時(shí),只要添加這個(gè)類就好了,代碼如下:
- using System;
 - using System.Net.Sockets;
 - using System.Net;
 - using System.Threading;
 - using System.IO;
 - using System.Windows.Forms;
 - namespace WindowsServer
 - {
 - class PolicySocketServer
 - {
 - TcpListener _Listener = null;
 - TcpClient _Client = null;
 - static ManualResetEvent _TcpClientConnected = new ManualResetEvent(false);
 - const string _PolicyRequestString = "<policy-file-request/>";
 - int _ReceivedLength = 0;
 - byte[] _Policy = null;
 - byte[] _ReceiveBuffer = null;
 - private void InitializeData()
 - {
 - string policyFile = Path.Combine(Application.StartupPath, "clientaccesspolicy.xml");
 - using (FileStream fs = new FileStream(policyFile, FileMode.Open))
 - {
 - _Policy = new byte[fs.Length];
 - fs.Read(_Policy, 0, _Policy.Length);
 - }
 - _ReceiveBuffer = new byte[_PolicyRequestString.Length];
 - }
 - public void StartSocketServer()
 - {
 - InitializeData();
 - try
 - {
 - _Listener = new TcpListener(IPAddress.Any, 943);
 - _Listener.Start();
 - while (true)
 - {
 - _TcpClientConnected.Reset();
 - _Listener.BeginAcceptTcpClient(new AsyncCallback(OnBeginAccept), null);
 - _TcpClientConnected.WaitOne();
 - }
 - }
 - catch (Exception)
 - {
 - }
 - }
 - private void OnBeginAccept(IAsyncResult ar)
 - {
 - _Client = _Listener.EndAcceptTcpClient(ar);
 - _Client.Client.BeginReceive(_ReceiveBuffer, 0, _PolicyRequestString.Length, SocketFlags.None,
 - new AsyncCallback(OnReceiveComplete), null);
 - }
 - private void OnReceiveComplete(IAsyncResult ar)
 - {
 - try
 - {
 - _ReceivedLength += _Client.Client.EndReceive(ar);
 - if (_ReceivedLength < _PolicyRequestString.Length)
 - {
 - _Client.Client.BeginReceive(_ReceiveBuffer, _ReceivedLength,
 - _PolicyRequestString.Length - _ReceivedLength,
 - SocketFlags.None, new AsyncCallback(OnReceiveComplete), null);
 - return;
 - }
 - string request = System.Text.Encoding.UTF8.GetString(_ReceiveBuffer, 0, _ReceivedLength);
 - if (StringComparer.InvariantCultureIgnoreCase.Compare(request, _PolicyRequestString) != 0)
 - {
 - _Client.Client.Close();
 - return;
 - }
 - _Client.Client.BeginSend(_Policy, 0, _Policy.Length, SocketFlags.None,
 - new AsyncCallback(OnSendComplete), null);
 - }
 - catch (Exception)
 - {
 - _Client.Client.Close();
 - }
 - _ReceivedLength = 0;
 - _TcpClientConnected.Set(); //Allow waiting thread to proceed
 - }
 - private void OnSendComplete(IAsyncResult ar)
 - {
 - try
 - {
 - _Client.Client.EndSendFile(ar);
 - }
 - catch (Exception)
 - {
 - }
 - finally
 - {
 - _Client.Client.Close();
 - }
 - }
 - }
 - }
 
2.2、啟動(dòng)策略文件服務(wù),聲明Socket,監(jiān)聽端口,接收數(shù)據(jù),發(fā)送數(shù)據(jù)。
啟動(dòng)策略文件服務(wù)
- #region Start The Policy Server 驗(yàn)證策略文件
 - PolicySocketServer StartPolicyServer = new PolicySocketServer();
 - Thread th = new Thread(new ThreadStart(StartPolicyServer.StartSocketServer));
 - th.IsBackground = true;
 - th.Start();
 - #endregion
 
聲明Socket,綁定端口,開始監(jiān)聽
- private void StartButton_Click(object sender, EventArgs e)
 - {//創(chuàng)建Socket
 - listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 - //獲取主機(jī)信息
 - IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
 - //把IP和端口轉(zhuǎn)換化為IPEndPoint實(shí)例,端口號(hào)取4530
 - //Win7 中開啟了IPV6的地址,因此0,1對(duì)應(yīng)的是IPV6的地址,2,3對(duì)應(yīng)IPV4地址,3對(duì)應(yīng)本機(jī)的IP地址
 - //XP中沒有開啟IPV6
 - HostIPTextBox.Text = ipHostInfo.AddressList[3].ToString();
 - if (!string.IsNullOrEmpty(PortTextBox.Text))
 - {
 - //獲取端口號(hào)
 - int port = Convert.ToInt32(PortTextBox.Text.Trim());
 - //獲得本機(jī)的IP地址
 - localEP = new IPEndPoint(ipHostInfo.AddressList[3], port);
 - }
 - else
 - {
 - //默認(rèn)4530端口
 - ipAddress = IPAddress.Parse("127.0.0.1");
 - localEP = new IPEndPoint(ipHostInfo.AddressList[3], 4530);
 - }
 - try
 - {
 - //綁定指定的終結(jié)點(diǎn)
 - listener.Bind(localEP);
 - //開始監(jiān)聽
 - listener.Listen(10);
 - //一直循環(huán)接收客戶端的消息,開啟監(jiān)聽端口線程
 - ThreadStart threadwatchStart = new ThreadStart(WatchConnecting);
 - threadWatch = new Thread(threadwatchStart);
 - threadWatch.IsBackground = true;
 - threadWatch.Start();
 - }
 - catch (Exception ex)
 - {
 - MessageBox.Show(ex.Data.ToString());
 - }
 - }
 
連接端口,接收數(shù)據(jù)
- private void WatchConnecting()
 - {
 - ChangeStatue("等待Silverlight客戶端連接.....");
 - while (true) //持續(xù)不斷監(jiān)聽客戶端發(fā)來(lái)的請(qǐng)求
 - {
 - listener.BeginAccept(AcceptCallBack, listener);
 - _flipFlop.WaitOne();
 - }
 - }
 
- private void AcceptCallBack(IAsyncResult asyresult)
 - {
 - Socket listener = (Socket)asyresult.AsyncState;
 - Socket socket = listener.EndAccept(asyresult);
 - ChangeStatue("連接到Silverlight客戶端....");
 - _flipFlop.Set();
 - var state = new StateObject();
 - state.Socket = socket;
 - socket.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReciverCallBack, state);
 - }
 
- private void ReciverCallBack(IAsyncResult asyResult)
 - {
 - StateObject state = (StateObject)asyResult.AsyncState;
 - Socket socket = state.Socket;
 - int read = socket.EndReceive(asyResult);
 - if (read > 0)
 - {
 - string chunk = Encoding.UTF8.GetString(state.Buffer, 0, read);
 - state.StringBuilder.Append(chunk);
 - if (state.StringBuilder.Length > 0)
 - {
 - string result = state.StringBuilder.ToString();
 - ChangeStatue("成功接收到消息:"+result);
 - ChangeReciveText(result);
 - Send(socket, SendTextBox.Text);
 - AddListItems("接收消息:"+result+"\n");
 - AddListItems("發(fā)送消息:" + SendTextBox.Text + "\n");
 - }
 - }
 - }
 
發(fā)送數(shù)據(jù)
- private void Send(Socket handler, String data)
 - {
 - byte[] byteData = Encoding.UTF8.GetBytes(data);
 - handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallBack), handler);
 - }
 - private void SendCallBack(IAsyncResult asyResult)
 - {
 - try
 - {
 - Socket handler = (Socket)asyResult.AsyncState;
 - int byteSent = handler.EndSend(asyResult);
 - if (byteSent > 0)
 - {
 - ChangeStatue("發(fā)送數(shù)據(jù)成功!");
 - }
 - }
 - catch (Exception ex)
 - {
 - MessageBox.Show(ex.Data.ToString());
 - }
 - }
 
StateObject類:
- public class StateObject
 - {
 - public Socket Socket;
 - public StringBuilder StringBuilder = new StringBuilder();
 - public const int BufferSize = 1024;
 - public byte[] Buffer = new byte[BufferSize];
 - public int TotalSize;
 - }
 
客戶端:
和服務(wù)器端類似,客戶端的操作包括:聲明Socket,指定服務(wù)器地址和端口,連接到指定的服務(wù)器端口,發(fā)送數(shù)據(jù),接收數(shù)據(jù)。
下面是具體的實(shí)現(xiàn)代碼:
聲明Socket
- private Socket socket;
 
指定服務(wù)器地址和端口,開始連接
- private void SendButton_Click(object sender, RoutedEventArgs e)
 - {
 - if(string.IsNullOrEmpty(IPTextBox.Text)||string.IsNullOrEmpty(PortTextBox.Text))
 - {
 - MessageBox.Show ("請(qǐng)輸入主機(jī)IP地址和端口號(hào)!");
 - return;
 - }
 - //ip地址
 - string host=IPTextBox.Text.Trim();
 - //端口號(hào)
 - int port=Convert.ToInt32(PortTextBox.Text.Trim());
 - //建立終結(jié)點(diǎn)對(duì)象
 - DnsEndPoint hostEntry=new DnsEndPoint(host,port);
 - //創(chuàng)建一個(gè)Socket對(duì)象
 - socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
 - //創(chuàng)建Socket異步事件參數(shù)
 - SocketAsyncEventArgs socketEventArg=new SocketAsyncEventArgs ();
 - //將消息轉(zhuǎn)化為發(fā)送的byte[]格式
 - byte[]buffer=Encoding.UTF8.GetBytes(MessageTextBox.Text);
 - //注冊(cè)Socket完成事件
 - socketEventArg.Completed+=new EventHandler<SocketAsyncEventArgs>(socketEventArg_Completed);
 - //設(shè)置Socket異步事件遠(yuǎn)程終結(jié)點(diǎn)
 - socketEventArg.RemoteEndPoint=hostEntry;
 - //將定義好的Socket對(duì)象賦值給Socket異步事件參數(shù)的運(yùn)行實(shí)例屬性
 - socketEventArg.UserToken = buffer;
 - try
 - {
 - socket.ConnectAsync(socketEventArg);
 - }
 - catch(SocketException ex)
 - {
 - throw new SocketException((int)ex.ErrorCode);
 - }
 - }
 
向服務(wù)器發(fā)送數(shù)據(jù),并接受服務(wù)器回復(fù)的消息。
- private void socketEventArg_Completed(object sender, SocketAsyncEventArgs e)
 - {
 - //檢查是否發(fā)送出錯(cuò)
 - if (e.SocketError != SocketError.Success)
 - {
 - if (e.SocketError == SocketError.ConnectionAborted)
 - {
 - Dispatcher.BeginInvoke(() => MessageBox.Show("連接超時(shí)....請(qǐng)重試!"));
 - }
 - else if (e.SocketError == SocketError.ConnectionRefused)
 - {
 - Dispatcher.BeginInvoke(() => MessageBox.Show("無(wú)法連接到服務(wù)器端:"+e.SocketError));
 - }else
 - {
 - Dispatcher.BeginInvoke(() => MessageBox.Show("出錯(cuò)了!"+e.SocketError));
 - }
 - return;
 - }
 - //如果連接上,則發(fā)送數(shù)據(jù)
 - if (e.LastOperation == SocketAsyncOperation.Connect)
 - {
 - byte[] userbytes = (byte[])e.UserToken;
 - e.SetBuffer(userbytes, 0, userbytes.Length);
 - socket.SendAsync(e);
 - }//如果已發(fā)送數(shù)據(jù),則開始接收服務(wù)器回復(fù)的消息
 - else if (e.LastOperation == SocketAsyncOperation.Send)
 - {
 - Dispatcher.BeginInvoke(() =>
 - {
 - listBox1.Items.Add("客戶端在" + DateTime.Now.ToShortTimeString() + ",發(fā)送消息:" + MessageTextBox.Text);
 - });
 - byte[] userbytes = new byte[1024];
 - e.SetBuffer(userbytes, 0, userbytes.Length);
 - socket.ReceiveAsync(e);
 - }//接收服務(wù)器數(shù)據(jù)
 - else if (e.LastOperation == SocketAsyncOperation.Receive)
 - {
 - string RecevieStr = Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length).Replace("\0", "");
 - Dispatcher.BeginInvoke(() =>
 - {
 - listBox1.Items.Add("服務(wù)器在" + DateTime.Now.ToShortTimeString() + ",回復(fù)消息:" + RecevieStr);
 - });
 - socket.Close();
 - }
 - }
 
xaml代碼:
- <UserControl
 - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 - xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 - xmlns:esri="http://schemas.esri.com/arcgis/client/2009" x:Class="SilverlightSocket.MainPage"
 - mc:Ignorable="d"
 - d:DesignHeight="417" d:DesignWidth="530">
 - <Grid x:Name="LayoutRoot" Background="White">
 - <Grid.ColumnDefinitions>
 - <ColumnDefinition Width="0.868*"/>
 - <ColumnDefinition Width="0.135*"/>
 - </Grid.ColumnDefinitions>
 - <Grid.RowDefinitions>
 - <RowDefinition/>
 - </Grid.RowDefinitions>
 - <esri:Map Background="White" WrapAround="True" Grid.ColumnSpan="2">
 - <esri:ArcGISTiledMapServiceLayer Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"/>
 - </esri:Map>
 - <StackPanel Grid.Column="1" Background="#7F094870">
 - <StackPanel.Effect>
 - <DropShadowEffect/>
 - </StackPanel.Effect>
 - <TextBlock x:Name="textBlock1" Text="主機(jī)IP" Grid.Column="1" Margin="5,5,0,0" Foreground="#FFE7D4E3" FontWeight="Bold" />
 - <TextBox x:Name="IPTextBox" Text="169.254.57.67" Grid.Column="1" d:LayoutOverrides="Width" Margin="5,5,0,0" HorizontalAlignment="Left"/>
 - <TextBlock x:Name="textBlock2" Text="端口號(hào)" Grid.Column="1" Margin="5,5,0,0" Foreground="#FFE7D4E3" FontWeight="Bold" />
 - <TextBox x:Name="PortTextBox" Width="51" Text="4530" Grid.Column="1" Margin="5,5,0,0" HorizontalAlignment="Left"/>
 - <TextBlock x:Name="textBlock4" Text="消息記錄:" Height="23" Grid.Column="1" Margin="5,5,0,0" Foreground="#FFE7D4E3" FontWeight="Bold" />
 - <ListBox x:Name="listBox1" Grid.Column="1" Margin="5,5,0,0" Height="150" />
 - <TextBlock x:Name="textBlock3" Text="發(fā)送信息內(nèi)容" Height="16" Grid.Column="1" d:LayoutOverrides="Width" Margin="5,5,0,0" Foreground="#FFE7D4E3" FontWeight="Bold" />
 - <TextBox x:Name="MessageTextBox" Grid.Column="1" Height="50" Margin="5,5,0,0" />
 - <Button Content="發(fā)送" Height="23" x:Name="SendButton" Grid.Column="1" Margin="5,5,0,0" />
 - <Button Content="清空" Height="23" x:Name="ClearButton" Grid.Column="1" Margin="5,5,0,0" />
 - </StackPanel>
 - </Grid>
 - </UserControl>
 
最后效果示意圖:
服務(wù)器端:

Silverlight客戶端:

后續(xù)工作中將結(jié)合地圖來(lái)實(shí)現(xiàn)模擬實(shí)時(shí)位置的顯示功能。。。。
原文鏈接:http://www.cnblogs.com/potential/archive/2013/01/23/2873035.html















 
 
 




 
 
 
 