TServerSocket: Как проверить, подключен ли конкретный клиент в ListView перед отправкой сообщения? - PullRequest
2 голосов
/ 01 октября 2019

У меня есть Timer и я хочу отправить сообщение каждому клиенту ListView, чтобы определить, например, время пинга. Тогда у меня есть следующий код:

procedure TMainForm.Timer1Timer(Sender: TObject);
var
  i: Integer;
begin
  try
    for i := 0 to ListView1.Items.count - 1 do
    begin
      ListView1.Items.Item[i].SubItems.Objects[2] := TObject(GetTickCount);
      ServerSocket1.Socket.Connections[i].SendText('ping' + #13#10);
    end;
  except
    exit;
  end;
end;

Перед отправкой, может быть более целесообразно проверить, действительно ли клиент подключен или что-то подобное. Как сделать это? Спасибо заранее.

1 Ответ

5 голосов
/ 01 октября 2019

Нет необходимости проверять соединение. Если бы клиент был фактически отключен, он больше не был бы в списке Connections[] сервера, когда ваш обработчик OnTimer запущен. У вас должен быть обработчик OnClientDisconnect, назначенный для TServerSocket для удаления клиента из TListView.

Если по какой-то причине клиент все еще был в списке Connections[] (т. Е. Потому чтобазовое соединение было потеряно, но TServerSocket еще не обнаружило его), тогда сокет будет просто кэшировать все исходящие данные до тех пор, пока его исходящий буфер не заполнится, затем он начнет возвращать WSAWOULDBLOCK ошибок для каждой отправки. В конце концов, ОС истечет время ожидания мертвого соединения и TServerSocket удалит его из списка Connections[], вызвав событие OnClientDisconnect.

По крайней мере, в показанном вами коде вы должныобновите ваш цикл отправки на Close() любой сокет, который на самом деле не удается отправить, вызывая событие OnClientDisconnect для удаления этого клиента из TListView, например:

procedure TMainForm.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
var
  Item: TListItem;
begin
  Item := ListView1.Items.Add;
  Item.Data := Socket;
  ...
end;

procedure TMainForm.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
var
  Item: TListItem;
begin
  Item := ListView1.FindData(0, Socket, True, False);
  if Item <> nil then
    Item.Delete;
end;

procedure TMainForm.Timer1Timer(Sender: TObject);
const
  s: AnsiString = 'ping' + #13#10;
var
  Item: TListItem;
  Socket: TCustomWinSocket;
  p: PAnsiChar;
  i, len, sent: Integer;
begin
  for i := 0 to ListView1.Items.Count - 1 do
  begin
    Item := ListView1.Items[i];
    Item.SubItems.Objects[2] := TObject(GetTickCount);
    Socket := TCustomWinSocket(Item.Data);
    try
      // SendText() does not handle partial sends, or Unicode strings...
      //Socket.SendText('ping' + #13#10);
      p := PAnsiChar(s);
      len := Length(s);
      repeat
        sent := Socket.SendBuf(p^, len);
        if sent = -1 then
        being
          if WSAGetLastError() <> WSAEWOULDBLOCK then
            Break;
          // TODO: stop trying after several attempts fail...
          Continue;
        end;
        Inc(p, sent);
        Dec(len, sent);
      until len = 0;
      if len = 0 then
        Continue;
    except
    end;
    Socket.Close;
  end;
end;
...