在新版本的Beetle.NetPackage中提供了对Protobuf和Controller的支持,所以在WP8下使用Beetle.NetPackage进行基于TCP的数据交互则一件非常简单事情.下面通过组件在WP8下简单实现基于TCP通讯的订单在线查询功能.
协议定义
为了简化交互数据的处理在这里使用Protobuf来描述数据交互,通过Protobuf制订一系列的请求和应答对象来代替平常在TCP下繁琐的数据流处理过程.下面通过Protobuf来描述订单查询的通讯协议.
1 [ProtoContract] 2 public class GetCustomer 3 { 4 [ProtoMember(1)] 5 public string Name { get; set; } 6 } 7 8 [ProtoContract] 9 public class GetCustomerResponse 10 { 11 12 [ProtoMember(1)] 13 public IList<Customer> Items 14 { 15 get; 16 set; 17 } 18 19 } 20 21 [ProtoContract] 22 public class Customer 23 { 24 [ProtoMember(1)] 25 public string ID 26 { 27 get; 28 set; 29 } 30 [ProtoMember(2)] 31 public string Name 32 { 33 get; 34 set; 35 } 36 public override string ToString() 37 { 38 return Name; 39 } 40 } 41 42 [ProtoContract] 43 public class GetEmployee 44 { 45 [ProtoMember(1)] 46 public string Name { get; set; } 47 } 48 49 [ProtoContract] 50 public class GetEmployeeResponse 51 { 52 [ProtoMember(1)] 53 public IList<Employee> Items 54 { 55 get; 56 set; 57 } 58 } 59 60 [ProtoContract] 61 public class Employee 62 { 63 64 [ProtoMember(1)] 65 public string ID 66 { 67 get; 68 set; 69 } 70 [ProtoMember(2)] 71 public string Name 72 { 73 get; 74 set; 75 } 76 public override string ToString() 77 { 78 return Name; 79 } 80 81 } 82 83 [ProtoContract] 84 public class OrderSearch 85 { 86 [ProtoMember(1)] 87 public string Employee { get; set; } 88 [ProtoMember(2)] 89 public int PageIndex { get; set; } 90 [ProtoMember(3)] 91 public string Customer { get; set; } 92 [ProtoMember(4)] 93 public string FromDate { get; set; } 94 [ProtoMember(5)] 95 public string ToDate { get; set; } 96 } 97 98 [ProtoContract] 99 public class OrderSearchResponse 100 { 101 [ProtoMember(1)] 102 public IList<Order> Items 103 { 104 get; 105 set; 106 } 107 [ProtoMember(2)] 108 public int PageIndex 109 { 110 get; 111 set; 112 } 113 [ProtoMember(3)] 114 public int Pages 115 { 116 get; 117 set; 118 } 119 } 120 121 [ProtoContract] 122 public class Order 123 { 124 [ProtoMember(1)] 125 public string OrderID { get; set; } 126 [ProtoMember(2)] 127 public string Employee { get; set; } 128 [ProtoMember(3)] 129 public string Customer { get; set; } 130 [ProtoMember(4)] 131 public string OrderDate { get; set; } 132 [ProtoMember(5)] 133 public string RequiredDate { get; set; } 134 [ProtoMember(6)] 135 public string ShippedDate { get; set; } 136 [ProtoMember(7)] 137 public string ShipName { get; set; } 138 [ProtoMember(8)] 139 public string ShipAddress { get; set; } 140 [ProtoMember(9)] 141 public string ShipCity { get; set; } 142 [ProtoMember(10)] 143 public string ShipRegion { get; set; } 144 } 145 146 [ProtoContract] 147 public class GetOrderDetail 148 { 149 [ProtoMember(1)] 150 public string OrderID { get; set; } 151 } 152 153 [ProtoContract] 154 public class GetOrderDetailResponse 155 { 156 [ProtoMember(1)] 157 public IList<OrderDetail> Items 158 { 159 get; 160 set; 161 } 162 } 163 164 [ProtoContract] 165 public class OrderDetail 166 { 167 [ProtoMember(1)] 168 public string OrderID { get; set; } 169 [ProtoMember(2)] 170 public string Product { get; set; } 171 [ProtoMember(3)] 172 public double UnitPrice { get; set; } 173 [ProtoMember(4)] 174 public int Quantity { get; set; } 175 [ProtoMember(5)] 176 public float Discount { get; set; } 177 }
由于这里使用了protobuf-net,所以通过类和特性来结合,其实规范方便其他平台处理还是建议使用proto文件描述来生成对应的类,这样可以方便生成C++,JAVA等不同平台的交互对象.
定义TCP通讯对象
通过Beetle.NetPackage对protobuf的支持,在WP8下建立相应的TCP通讯是件非常简单的事情.
1 Beetle.NetPackage.ProtoPakcage.Register(typeof(MainPage).Assembly); 2 if (mClient == null) 3 { 4 mClient = new Beetle.NetPackage.NetClient("192.168.0.104", 9088, new Beetle.NetPackage.ProtoPakcage(), this); 5 mClient.LittleEndian = false; 6 } 7 mClient.Connect();
在使用protobuf前通过ProtoPakcage.Register把程序集下面对应的protobuf消息注册到组件中,然后创建相应的NetClient就可以进行数据通讯.在这里为了和android交互兼容把client的LittleEndian属性设置成false即在处理数据过程采用高字序作为一些数据类型的处理方式.
消息路由分发
在新版本的Beetle.NetPackage中提供简单的消息路由功能,因此在编写消息接收处理的时候再也不需要通过if来判断不同消息去调用方法.使用消息路由并不需开发人员定义复杂的消息规则,只需要定义相应消息类型的参数即可让组件帮助完成这个事情.
1 public void OnSearchOrderResponse(Beetle.NetPackage.NetClient client, OrderSearchResponse e) 2 { 3 if (e.Items == null) 4 lstOrders.ItemsSource = null; 5 else 6 lstOrders.ItemsSource = e.Items.ToList(); 7 mPageIndex = e.PageIndex; 8 mPages = e.Pages; 9 } 10 11 public void OnGetEmployee(Beetle.NetPackage.NetClient client, GetEmployeeResponse e) 12 { 13 mEmployees = e.Items; 14 lstEmployee.ItemsSource = e.Items.ToList(); 15 } 16 public void OnGetCustomer(Beetle.NetPackage.NetClient client, GetCustomerResponse e) 17 { 18 mCustomers = e.Items; 19 lstCustomer.ItemsSource = e.Items.ToList(); 20 21 } 22 public void OnGetOrderDetail(Beetle.NetPackage.NetClient client, GetOrderDetailResponse e) 23 { 24 DialogOrderDetail detail = new DialogOrderDetail(); 25 CustomMessageBox mOrderDetailDialog = new CustomMessageBox 26 { 27 Content = detail, 28 Title = "OrderDetail", 29 RightButtonContent = "OK" 30 }; 31 detail.ListSelector.ItemsSource = e.Items.ToList(); 32 mOrderDetailDialog.Show(); 33 }
以上是制定不同消息的处理过程,在接收消息的时候触发这些过程只需要简单地调用Controller.Invoke方法即可完成.
1 public void ClientReceive(Beetle.NetPackage.NetClient client, object message) 2 { 3 this.Dispatcher.BeginInvoke(() => 4 { 5 Beetle.NetPackage.Controller.Invoke(this, mClient, message); 6 }); 7 }
在消息接收方法中调用Controller.Invoke组件会自动匹配相应消息处理的方法并调用,由于是组件自动匹配所以在制定方法的过程也需要遵循一个规则,就是在一个对象中对应消息处理的方法必须是唯一的.
运行效果
总结
通过Beetle.NetPackage只需要很少量的代码就能完成基于TCP的对象数据交互处理,而开发者是完全不用关心下层的协议处理细节,Beetle.NetPackage不仅仅提供对wp8的支持,还提供对flash和android的支持.