这篇文章将为大家详细讲解有关Silverlight中怎么利用Socket实现通信,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
一.Silverlight的Socket通信和控制台、WinForm下的Socket通信有很大的区别。
对于后两者的Socket通信,其过程就是开启端口,绑定端口,监听端口,连接,接收数据,发送数据。
而在Silverlight中则不太一样,在Silverlight中,首先是Silverlight客户端自动向943端口的服务器端发送一个“<policy-file-request/>”的语句请求,然后服务器端向客户端发送策略文件:
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>
发送之后,才允许和服务器进行Socket通信,之后的过程则都是一样。
二、服务器端
2.1、Silverligh中发送策略文件服务
上面说到,Silverlight中,服务器端会向客户端发送策略文件,然后才能开始Socket通信,下面给出服务器端的发送策略文件服务的代码,该代码是在网上找的,是别人已经写好的一个类,所以在编写Silverlight 的Socket通信程序时,只要添加这个类就好了,代码如下:
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、启动策略文件服务,声明Socket,监听端口,接收数据,发送数据。
启动策略文件服务
#region Start The Policy Server 验证策略文件 PolicySocketServer StartPolicyServer = new PolicySocketServer(); Thread th = new Thread(new ThreadStart(StartPolicyServer.StartSocketServer)); th.IsBackground = true; th.Start(); #endregion
声明Socket,绑定端口,开始监听
private void StartButton_Click(object sender, EventArgs e) {//创建Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //获取主机信息 IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); //把IP和端口转换化为IPEndPoint实例,端口号取4530 //Win7 中开启了IPV6的地址,因此0,1对应的是IPV6的地址,2,3对应IPV4地址,3对应本机的IP地址 //XP中没有开启IPV6 HostIPTextBox.Text = ipHostInfo.AddressList[3].ToString(); if (!string.IsNullOrEmpty(PortTextBox.Text)) { //获取端口号 int port = Convert.ToInt32(PortTextBox.Text.Trim()); //获得本机的IP地址 localEP = new IPEndPoint(ipHostInfo.AddressList[3], port); } else { //默认4530端口 ipAddress = IPAddress.Parse("127.0.0.1"); localEP = new IPEndPoint(ipHostInfo.AddressList[3], 4530); } try { //绑定指定的终结点 listener.Bind(localEP); //开始监听 listener.Listen(10); //一直循环接收客户端的消息,开启监听端口线程 ThreadStart threadwatchStart = new ThreadStart(WatchConnecting); threadWatch = new Thread(threadwatchStart); threadWatch.IsBackground = true; threadWatch.Start(); } catch (Exception ex) { MessageBox.Show(ex.Data.ToString()); } }
连接端口,接收数据
private void WatchConnecting() { ChangeStatue("等待Silverlight客户端连接....."); while (true) //持续不断监听客户端发来的请求 { 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("发送消息:" + SendTextBox.Text + "\n"); } } }
发送数据
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("发送数据成功!"); } } 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; }
客户端:
和服务器端类似,客户端的操作包括:声明Socket,指定服务器地址和端口,连接到指定的服务器端口,发送数据,接收数据。
下面是具体的实现代码:
声明Socket
private Socket socket;
指定服务器地址和端口,开始连接
private void SendButton_Click(object sender, RoutedEventArgs e) { if(string.IsNullOrEmpty(IPTextBox.Text)||string.IsNullOrEmpty(PortTextBox.Text)) { MessageBox.Show ("请输入主机IP地址和端口号!"); return; } //ip地址 string host=IPTextBox.Text.Trim(); //端口号 int port=Convert.ToInt32(PortTextBox.Text.Trim()); //建立终结点对象 DnsEndPoint hostEntry=new DnsEndPoint(host,port); //创建一个Socket对象 socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); //创建Socket异步事件参数 SocketAsyncEventArgs socketEventArg=new SocketAsyncEventArgs (); //将消息转化为发送的byte[]格式 byte[]buffer=Encoding.UTF8.GetBytes(MessageTextBox.Text); //注册Socket完成事件 socketEventArg.Completed+=new EventHandler<SocketAsyncEventArgs>(socketEventArg_Completed); //设置Socket异步事件远程终结点 socketEventArg.RemoteEndPoint=hostEntry; //将定义好的Socket对象赋值给Socket异步事件参数的运行实例属性 socketEventArg.UserToken = buffer; try { socket.ConnectAsync(socketEventArg); } catch(SocketException ex) { throw new SocketException((int)ex.ErrorCode); } }
向服务器发送数据,并接受服务器回复的消息。
private void socketEventArg_Completed(object sender, SocketAsyncEventArgs e) { //检查是否发送出错 if (e.SocketError != SocketError.Success) { if (e.SocketError == SocketError.ConnectionAborted) { Dispatcher.BeginInvoke(() => MessageBox.Show("连接超时....请重试!")); } else if (e.SocketError == SocketError.ConnectionRefused) { Dispatcher.BeginInvoke(() => MessageBox.Show("无法连接到服务器端:"+e.SocketError)); }else { Dispatcher.BeginInvoke(() => MessageBox.Show("出错了!"+e.SocketError)); } return; } //如果连接上,则发送数据 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) { Dispatcher.BeginInvoke(() => { listBox1.Items.Add("客户端在" + DateTime.Now.ToShortTimeString() + ",发送消息:" + MessageTextBox.Text); }); byte[] userbytes = new byte[1024]; e.SetBuffer(userbytes, 0, userbytes.Length); socket.ReceiveAsync(e); }//接收服务器数据 else if (e.LastOperation == SocketAsyncOperation.Receive) { string RecevieStr = Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length).Replace("\0", ""); Dispatcher.BeginInvoke(() => { listBox1.Items.Add("服务器在" + DateTime.Now.ToShortTimeString() + ",回复消息:" + 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="主机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="端口号" 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="发送信息内容" 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="发送" 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>
最后效果示意图:
服务器端:
关于Silverlight中怎么利用Socket实现通信就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。