之前因为项目的关系,涉及到与服务器实时通信,比如通过GPRS将GPS的位置信息等信息发送到服务器,然后再转发给Silverlight应用程序,最后在地图上标示出实时的地理位置,查了查相关的资料,网上给出的比较好的方法就是利用Socket与服务器通信。于是这两天看了看Silverlight下的Socket通信,在此将学习的心得和实现过程作一个记录,以供相互学习和交流。
园子里关于这方面的内容已经有很多大神写过了,这里小小的推荐一下:
http://www.cnblogs.com/webabcd/archive/2008/12/22/1359551.HTML
因此本文的重点知识说一下具体实现的过程,细节和原理性的东西不会太多,因为本人也是新手,所以就不卖弄了。之前说到和地图结合,所以本文的后续工作将会把Silverlight的Socket通信与ArcGIS 的地图结合,来实现一个小小的功能,而本篇则主要关于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通信,之后的过程则都是一样。
@H_502_121@二、服务器端
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,_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,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,_Policy.Length,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、启动策略文件服务,声明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,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,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,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; }
@H_502_121@客户端:
和服务器端类似,客户端的 *** 作包括:声明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,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,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,userbytes.Length); socket.ReceiveAsync(e); }//接收服务器数据 else if (e.Lastoperation == SocketAsyncoperation.Receive) { string RecevIEStr = EnCoding.UTF8.GetString(e.Buffer,e.Buffer.Length).Replace("""",); 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" Foreground="#FFE7D4E3" FontWeight="Bold"/> < TextBox x:name="IPTextBox" Text="169.254.57.67" GrID.Column="1" d:LayoutOverrIDes="WIDth" margin="5,0" HorizontalAlignment="left"/>< TextBlock x:name="textBlock2" Text="端口号" GrID.Column="1" margin="5,0" Foreground="#FFE7D4E3" FontWeight="Bold"/> < TextBox x:name="PortTextBox" WIDth="51" Text="4530" GrID.Column="1" margin="5,0" HorizontalAlignment="left"/>< TextBlock x:name="textBlock4" Text="消息记录:" Height="23" GrID.Column="1" margin="5,0" Foreground="#FFE7D4E3" FontWeight="Bold"/> < ListBox x:name="ListBox1" GrID.Column="1" margin="5,0" Height="150"/> < TextBlock x:name="textBlock3" Text="发送信息内容" Height="16" GrID.Column="1" d:LayoutOverrIDes="WIDth" margin="5,0" Foreground="#FFE7D4E3" FontWeight="Bold"/> < TextBox x:name="MessageTextBox" GrID.Column="1" Height="50" margin="5,0"/> < button Content="发送" Height="23" x:name="Sendbutton" GrID.Column="1" margin="5,0"/> < button Content="清空" Height="23" x:name="Clearbutton" GrID.Column="1" margin="5,0"/> </ StackPanel></ GrID></ UserControl>
最后效果示意图:
服务器端:
Silverlight客户端:
后续工作中将结合地图来实现模拟实时位置的显示功能。。。。
(版权所有,转载请标明出处)
总结以上是内存溢出为你收集整理的Silverlight Socket通信学习笔记全部内容,希望文章能够帮你解决Silverlight Socket通信学习笔记所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)