Назад | Перейти на главную страницу

Путаница с эфемерными портами (плюс некоторый erlang)

Итак, мое понимание системы эфемерных портов состоит в том, что существует кортеж из четырех значений, идентифицирующих каждое соединение: {исходный IP-адрес, исходный порт, целевой IP-адрес, целевой порт}, причем исходный порт является одним из эфемерных портов в системе. Соединение не может иметь тот же кортеж, что и любое другое, поэтому, если вы делаете несколько одновременных подключений со своего компьютера к одному и тому же порту на другом, вы можете сделать столько подключений, сколько у вас есть свободных эфемерных портов, так как это единственный параметр, который можно изменить. Все это имеет для меня смысл (но, пожалуйста, поправьте меня, если я ошибаюсь в чем-либо).

Однако теоретически, если бы вы использовали два разных исходных IP-адреса, это удвоило бы количество потенциально возможных подключений. Я решил, что хочу это протестировать, поэтому написал тест клиент / сервер на erlang, который устанавливает и поддерживает столько соединений, сколько может. Используя только один IP-адрес, мои соединения ограничивались примерно 52 КБ. Я использовал IP-адрес 127.0.0.1.

Затем я изменил сценарий, чтобы он также использовал локальный сетевой адрес машины (172.16.202.132). Это определенно было связано с обоими IP-адресами:

# lsof -Pnl +M -i4
beam.smp  7528     1000  947u  IPv4 3441692      0t0  TCP 172.16.202.132:32064->172.16.202.132:8888 (ESTABLISHED)
beam.smp  7528     1000  948u  IPv4 3441695      0t0  TCP 127.0.0.1:37225->127.0.0.1:8888 (ESTABLISHED)

(повторяется, казалось бы, бесконечно)

Но снова мои соединения упали до 52k. Я изменил две программы так, чтобы сервер и клиент использовали порт 8889, а также 8888. Я был почти уверен, что это даст мне больше, но снова я ограничился 52k. Подключения выполнялись правильно:

# lsof -Pnl +M -i4
beam.smp  7528     1000  946u  IPv4 3441689      0t0  TCP 172.16.202.132:26620->172.16.202.132:8889 (ESTABLISHED)
beam.smp  7528     1000  947u  IPv4 3441692      0t0  TCP 172.16.202.132:32064->172.16.202.132:8888 (ESTABLISHED)
beam.smp  7528     1000  948u  IPv4 3441695      0t0  TCP 127.0.0.1:37225->127.0.0.1:8888 (ESTABLISHED)
beam.smp  7528     1000  949u  IPv4 3441698      0t0  TCP 127.0.0.1:27965->127.0.0.1:8889 (ESTABLISHED)

(повторяется, казалось бы, бесконечно)

Может ли кто-нибудь пролить свет на то, почему это может происходить? Я использую Ubuntu 10.04, erlang R13B03. Вот код, который я также использую для клиента / сервера:

Сервер:

-module(contest).
-compile(export_all).
-define(TCP_OPTS, [binary, {packet, raw}, {nodelay, true}, {reuseaddr, true}, {active, false},{keepalive,true}]).

start() ->
    erlang:register(counter,spawn(fun()->?MODULE:counter(0) end)),

    %Gets the listen socket, generates acceptor threads
    case gen_tcp:listen(8888, ?TCP_OPTS) of
    {ok, Listen1} -> 
        ?MODULE:gen_accepts(10,Listen1)
    end,

    case gen_tcp:listen(8889, ?TCP_OPTS) of
    {ok, Listen2} -> 
        ?MODULE:gen_accepts(10,Listen2)
    end,

    ?MODULE:supervisor_loop({Listen1,Listen2}).

%Serves the purpose of keeping the listen socket open
%indefinitely
supervisor_loop(LS) ->
    receive
    _ -> ?MODULE:supervisor_loop(LS)
    end.    

%Generates I acceptor threads which constantly listen for
%new connections. Upon getting one, a new acceptor thread
%is spawned and the one which receieved a connection 
%continues on to process the connection
gen_accepts(0,_) -> ok;
gen_accepts(I,LS) ->
    spawn(?MODULE,accept_loop,[LS]),
    ?MODULE:gen_accepts(I-1,LS).

%Acceptor loop which spawns off sock processors when connections
%come in
accept_loop(Listen) ->
    case gen_tcp:accept(Listen) of
        {ok, Socket} ->
                Pid = spawn(fun()->?MODULE:process_sock(Socket) end),
                gen_tcp:controlling_process(Socket,Pid),
        whereis(counter)!plus;
        {error,_} -> ok
        end,
    ?MODULE:accept_loop(Listen).

%Holds socket, doesn't do anything
process_sock(Sock) ->
    receive
    _ -> process_sock(Sock)
        end.

counter(C) ->
    receive
    plus ->
        io:fwrite("~p\n",[C+1]),
        counter(C+1)
    end.

Клиент:

-module(flooder).
-compile(export_all).

start() -> 
    spawn(fun()->start("172.16.202.132",8888)end),
    spawn(fun()->start("172.16.202.132",8889)end).

start(Ip,Port) ->
    spawn(?MODULE,connect,[Ip,Port]),
    timer:sleep(2),

    case Ip of
    "127.0.0.1" -> ?MODULE:start("172.16.202.132",Port);
    "172.16.202.132" -> ?MODULE:start("127.0.0.1",Port)
    end.

connect(Ip,Port) ->
    case gen_tcp:connect(
        Ip,
        Port,
        [list,{active,true}]
    ) of
    {ok, Sock} -> io:fwrite("Connected on ~s\n",[Ip]),loop(Sock);
    {error,E} -> eMessage("connect",E)
    end.

loop(Sock) ->
    receive
    _ -> loop(Sock)
    end.

eMessage(W,E) ->
    io:fwrite("~w at ~s: ~s\n",[self(),W,E]).

Ваша ОС имеет максимальное количество подключений IPv4, независимо от того, сколько IP-адресов вы слушаете. Вероятно, это предел, с которым вы столкнулись. Также могут быть ограничения на процесс и на пользователя. Проверьте все это.