У меня есть одно TCP-соединение с сервером, но, возможно, у меня есть несколько запросов одновременно. Большую часть времени ответ будет настолько большим, что я буду постоянно получать много фрагментов данных. Я могу проверить длину данных, чтобы определить, что это КОНЕЦ ПОТОКА. Но при множественных запросах иногда пакеты "смешиваются" с другими запросами, что вызывает много сбоев.
Например,
для обычного запроса:
в реальной жизни:
На данный момент моя единственная мысль состоит в том, чтобы сделать это последовательно, выполняя запрос один за другим. Однако это не может полностью решить мою проблему, потому что иногда я получаю подписанные события, которые не контролируются.
Как я могу решить эту проблему с TCP в целом?
Я показываю часть кода ниже (на случай, если кто-то знает erlang и elixir)
# Create TCP connection
{:ok, socket} =
:gen_tcp.connect(host_charlist, port, [:binary, active: true, keepalive: true])
# Send request
def handle_call({:send, msg}, from, state) do
:ok = :gen_tcp.send(state.socket, msg)
new_state = %{state | from: from, msg: ""}
{:noreply, new_state}
end
# When it receive packet
def handle_info({:tcp, _socket, msg}, state) do
new_state = %{state | msg: state.msg <> msg}
current_msg_size = byte_size(new_state.msg)
defined_msg_size = Response.get_body_size(new_state.msg) # this msg size can read from the first packet's header
cond do
current_msg_size == defined_msg_size ->
GenServer.reply(new_state.from, {:ok, new_state.msg})
{:noreply, %{new_state | msg: ""}}
current_msg_size > defined_msg_size ->
GenServer.reply(new_state.from, {:error, "Message size exceed."})
{:noreply, %{new_state | msg: ""}}
true ->
{:noreply, new_state}
end
end
🤔 А знаете ли вы, что...
Erlang - функциональный язык программирования, созданный в 1986 году в компании Ericsson.
На уровне TCP в соединении request
и response
не существуют, это одна трубка, передающая байты с одной стороны на другую по порядку.
Чтобы обрабатывать чередование по одному соединению, вы должны обрабатывать его на один уровень вверх по стеку.
Возможные решения включают в себя:
receive
) и назначать его соответствующему запросу.Вы можете изменить параметр TCP с active = true
на active = false
или на active = once
:gen_tcp.connect(host_charlist, port, [:binary, active: true, keepalive: true])
Тогда вы сможете контролировать получение сообщений самостоятельно, а не заваленными входящими сообщениями.
1、получить звонок от верхнего уровня, обработать ваш клиентский запрос, установить active = false
, дождаться ответа сервера.
2. Старайтесь не получать только связанные сообщения, игнорируйте их в очереди сообщений процесса или сохраняйте их в буфере.
3、получив ответ сервера, обработайте его, затем установите active = once
.
4、если тайм-аут, установите active = once
.
https://www.erlang.org/doc/man/gen_tcp.html#controlling_process-2
controlling_process(Socket, Pid) -> ok | {error, Reason} Types Socket = socket() Pid = pid() Reason = closed | not_owner | badarg | inet:posix() Assigns a new controlling process Pid to Socket. The controlling process is the process that receives messages from the socket. If called by any other process than the current controlling process, {error, not_owner} is returned. If the process identified by Pid is not an existing local pid, {error, badarg} is returned. {error, badarg} may also be returned in some cases when Socket is closed during the execution of this function.
If the socket is set in active mode, this function will transfer any messages in the mailbox of the caller to the new controlling process. If any other process is interacting with the socket while the transfer is happening, the transfer may not work correctly and messages may remain in the caller's mailbox. For instance changing the sockets active mode before the transfer is complete may cause this.