测试移动互联网应用时,因为现实的网络中,延迟非常严重,而实验环境很难出现延迟,比如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
.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%