测试移动互联网应用时,因为现实的网络中,延迟非常严重,而实验环境很难出现延迟,比如ssl连接时,在实验室很难出现握手很慢的情况。而直接现网验证风险又很大,心里没底。
所以在网络中增加一个tcp proxy,终端连接到proxy,proxy连接到服务端,在proxy中人为的随机延迟报文。所有基于TCP的协议都可以使用这个代理,比如http、ssl等。
从开发的难度、并发要求等情况考虑,使用erlang是一个比较合适的选择。
下面是代码,c(tcp_proxy).后,运行
tcp_proxy:start(客户端连接的端口, "192.168.9.145", 服务端的端口).
然后终端连接“客户端连接的端口”,每建一个连接,代理都起一个独立的进程,连接到服务端,然后在终端与服务端之间转发报文,不做任何解析。
在process中,可以延迟,也可以丢包,甚至可以改写报文。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(tcp_proxy).
-export([
start/3,
server/3,
process/1,
loop/2
]).
start(LPort, DHost, DPort) ->
case gen_tcp:listen(LPort,[{active, true},{packet,raw}]) of
{ok, ListenSock} ->
spawn(?MODULE, server, [ListenSock, DHost, DPort]),
{ok, Port} = inet:port(ListenSock),
Port;
{error,Reason} ->
{error, Reason}
end
.
%% tcp_proxy:start(客户端连接的端口, "192.168.9.145", 服务端的端口).
server(LSocket, DHost, DPort) ->
io:format("Server started ~n"),
case gen_tcp:accept(LSocket) of
{ok, SrcSocket} ->
io:format("Got connect ~n"),
case gen_tcp:connect(DHost, DPort, [binary, {packet,raw}, {active, true}]) of
{ok, DstSocket} ->
io:format("Connect to dest server ~n"),
Pid = spawn_link(fun() -> loop(SrcSocket, DstSocket) end),
gen_tcp:controlling_process(DstSocket, Pid),
gen_tcp:controlling_process(SrcSocket, Pid);
E ->
io:format("Error:Connect failed!~n"),
E
end,
ok;
Other ->
io:format("Error: accept returned ~w - finita!~n",[Other]),
ok
end,
server(LSocket, DHost, DPort)
.
loop(SrcSocket, DstSocket) ->
%% inet:setopts(SrcSocket, [{active,once}]),
receive
{tcp, SrcSocket, Data} ->
%%Len = length(Data),
%%io:format("Received tcp(SrcSocket,~p) ~p ~n",[Len, Data]),
case process(Data) of % you can poke around with the data
{ok, RetData} ->
%%io:format("Send to tcp(DstSocket,~p) ~p ~n",[Len, RetData]),
gen_tcp:send(DstSocket, RetData);
error ->
io:format("discarded~n"),
ok
end,
loop(SrcSocket, DstSocket); % erlang awesomeness. no loops ;)
{tcp, DstSocket, Data} ->
%%Len = size(Data),
%%io:format("Received tcp(DstSocket,~p) ~p ~n", [Len, Data]),
case process(Data) of % you can poke around with the data
{ok, RetData} ->
%%io:format("Send to tcp(SrcSocket,~p) ~p ~n",[Len, RetData]),
gen_tcp:send(SrcSocket, RetData);
error ->
io:format("discarded~n"),
ok
end,
loop(SrcSocket, DstSocket);
{tcp_closed, SrcSocket} ->
gen_tcp:close(DstSocket),
io:format("DstSocket ~w closed [~w]~n",[SrcSocket,self()]),
ok;
{tcp_closed, DstSocket}->
gen_tcp:close(SrcSocket),
io:format("DstSocket (SrcSocket) ~w closed [~w]~n",[SrcSocket,self()]),
ok
end
.
%% 改这里,模拟不同的网络环境,当前实现:50%的情况下,报文会延迟0-10秒
process(Data) ->
R = random:uniform(100),
if
R > 50 ->
timer:sleep(random:uniform(10000)),
{ok, Data};
true ->
{ok, Data}
end
.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
上一篇文章中,我们吹了一下资源和本地化,同时也做了一个实例,本文我们再深入探索一下资源限定符和资源路径的映射。这两个玩意儿也许我们在实际开发中并不十分关注,不过,了解一下,还是有好处的。
这两个名词看起来就抽象,或者,我们会感觉到,从文字描述无法理解它们,那么,老规矩,我们还是用实验来看看是否能将抽象的概念形象化。
1、启动VS,新建一个Modern风格的应用程序项目(也就前面说过的板砖风格)。
2、在“资源管理器”中的项目节点上右击,从快捷菜单中依次选择“添加”-“新建项”,在模板列表中找到资源文件(.resw),文件名按默认Resources即可,确定。
3、在刚才新建的资源文件中,随便输入一些资源,如图:
4、保存并关闭资源文件,另外,在项目中新建两个空白页面,分别为PageQt.xaml和PageMaps.xaml,现在,你的结构应该类似下图所示。
(1)打开PageQt.xaml,界面布局参考下面的XAML。
切换到PageQt.xaml.cs,C#代码如下所示。
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using Windows.Foundation;
- using Windows.Foundation.Collections;
- using Windows.UI.Xaml;
- using Windows.UI.Xaml.Controls;
- using Windows.UI.Xaml.Controls.Primitives;
- using Windows.UI.Xaml.Data;
- using Windows.UI.Xaml.Input;
- using Windows.UI.Xaml.Media;
- using Windows.UI.Xaml.Navigation;
- // 引入以下命名空间
- using Windows.ApplicationModel.Resources;
- using Windows.ApplicationModel.Resources.Core;
- namespace MyApp
- {
- /// <summary>
- /// 可用于自身或导航至 Frame 内部的空白页。
- /// </summary>
- public sealed partial class PageQt : Page
- {
- public PageQt()
- {
- this.InitializeComponent();
- this.Loaded += PageQt_Loaded;
- }
- void PageQt_Loaded(object sender, RoutedEventArgs e)
- {
- ResourceContext context = ResourceManager.Current.DefaultContext;
- string resultStr = string.Empty;
- foreach (var item in context.QualifierValues)
- {
- resultStr += string.Format("{0} => {1}", item.Key, item.Value);
- resultStr += "\n";
- }
- this.tb.Text = resultStr;
- }
- }
-
}
- <Page
- x:
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="using:MyApp"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- mc:Ignorable="d">
- <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
- <TextBlock Name="tb" FontSize="20" TextWrapping="Wrap" Margin="3"/>
- </Grid>
-
</Page>
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using Windows.Foundation;
- using Windows.Foundation.Collections;
- using Windows.UI.Xaml;
- using Windows.UI.Xaml.Controls;
- using Windows.UI.Xaml.Controls.Primitives;
- using Windows.UI.Xaml.Data;
- using Windows.UI.Xaml.Input;
- using Windows.UI.Xaml.Media;
- using Windows.UI.Xaml.Navigation;
- // 引用以下命名空间
- using Windows.ApplicationModel.Resources;
- using Windows.ApplicationModel.Resources.Core;
- namespace MyApp
- {
- public sealed partial class PageMaps : Page
- {
- public PageMaps()
- {
- this.InitializeComponent();
- this.Loaded += (sender, args) =>
- {
- string str = "";
- foreach (var item in ResourceManager.Current.AllResourceMaps)
- {
- str += "-------------------- " + item.Key + " --------------------\n";
- foreach (var x in item.Value)
- {
- str += string.Format("{0} => {1}\n", x.Key, x.Value.Uri);
- }
- str += "\n\n";
- }
- this.tb.Text = str;
- };
- }
- }
-
}
- <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="150"/>
- <ColumnDefinition/>
- </Grid.ColumnDefinitions>
- <ListBox Name="lb" Grid.Column="0" SelectionChanged="onSelectionChanged">
- <ListBoxItem>资源限定符</ListBoxItem>
- <ListBoxItem>资源映射</ListBoxItem>
- </ListBox>
- <Frame x:Name="frameRight" Grid.Column="1" Margin="2"/>
-
</Grid>
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using Windows.Foundation;
- using Windows.Foundation.Collections;
- using Windows.UI.Xaml;
- using Windows.UI.Xaml.Controls;
- using Windows.UI.Xaml.Controls.Primitives;
- using Windows.UI.Xaml.Data;
- using Windows.UI.Xaml.Input;
- using Windows.UI.Xaml.Media;
- using Windows.UI.Xaml.Navigation;
- // “空白页”项模板在http://go.microsoft.com/fwlink/?LinkId=234238上有介绍
- namespace MyApp
- {
- /// <summary>
- /// 可用于自身或导航至 Frame 内部的空白页。
- /// </summary>
- public sealed partial class MainPage : Page
- {
- public MainPage()
- {
- this.InitializeComponent();
- }
- /// <summary>
- /// 在此页将要在 Frame 中显示时进行调用。
- /// </summary>
- /// <param name="e">描述如何访问此页的事件数据。Parameter
- /// 属性通常用于配置页。</param>
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- lb.SelectedIndex = 0;
- }
- private void onSelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- ListBox lb = sender as ListBox;
- if (lb!=null)
- {
- int index = lb.SelectedIndex;
- switch (index)
- {
- case 0:
- this.frameRight.Navigate(typeof(PageQt));
- break;
- case 1:
- this.frameRight.Navigate(typeof(PageMaps));
- break;
- default:
- this.frameRight.Navigate(typeof(PageQt));
- break;
- }
- }
- }
- }
-
}
现在,运行一下。
- <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
- <StackPanel Margin="25">
- <TextBlock FontSize="20" Text="第一个值:"/>
- <TextBlock FontSize="24" Name="tb1"/>
- <TextBlock FontSize="20" Margin="0,20,0,0" Text="第二个值:"/>
- <TextBlock FontSize="24" Name="tb2"/>
- </StackPanel>
-
</Grid>
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- ResourceMap map = ResourceManager.Current.MainResourceMap.GetSubtree("Resources");
- tb1.Text = map.GetValue("v1").ValueAsString;
- tb2.Text = map.GetValue("v2").ValueAsString;
-
}
然后,运行程序,查看效果
3.png (9.05 KB, 下载次数: 0)
用了一阵之后才注意到经常会出现一些乱码,当时也没太在意,后来无意中发现同事使用的汉化版SecureCRT上没有出现这种情况,我用的英文原版的上面显示乱码的地方在汉化版上显示出来的竟然是中文,如下图所示:
一开始以为是因为我的英文版不支持中文显示而乱码。后来仔细考虑了一下发现这个问题没有这么简单,问题的关键是为什么用SecureCRT通过命令行进行配置默认会显示中文呢,查阅了一些资料后发现这牵涉到一个文件/etc/sysconfig/i18n,“i18n”这个文件名称是internationalization的缩写,意即在i和n之间有18 个字母,本意是指软件的“国际化”。
这个文件只有一行内容
我用的环境是(RHEL Server release 5.1,Kernel 2.6.18-53.e15)这一行的意思表明你当前系统的语言环境变量设置。
从上面我们可以清楚的看到系统默认的语言环境变量是zh_CN.UTF-8,英文版的SecureCRT根据这个设置来显示相关中文信息时则会出现乱码。怎么解决呢?只需把LANG="zh_CN.UTF-8" 改为LANG="en_US.UTF-8"或者LANG="zh_CN.GB18030"即可。
接下来最好在SecureCRT的Options选项中选择Session Options,修改Appearance中的字符编码为
UTF-8。