TServerSocket:如何在发送消息之前检查ListView上的特定客户端是否仍处于连接状态?

时间:2019-10-01 00:44:41

标签: sockets delphi delphi-10.3-rio

我有一个Timer,并且想向ListView的每个客户端发送一条消息,例如以确定ping时间。然后我有以下代码:

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 个答案:

答案 0 :(得分:5)

无需检查连接。如果客户端实际上已断开连接,则在触发Connections[]处理程序时,该客户端将不再位于服务器的OnTimer列表中。您应该为OnClientDisconnect分配一个TServerSocket处理程序,以从TListView中删除客户端。

如果由于某种原因,客户端仍处于Connections[]列表中(即,由于基础连接已丢失,但TServerSocket尚未检测到),则套接字将简单地缓存所有传出数据,直到其出站缓冲区填满为止,然后它将开始为每次发送返回WSAWOULDBLOCK错误。最终,操作系统将使死连接超时,TServerSocket将从Connections[]列表中将其删除,从而触发OnClientDisconnect事件。

至少,在显示的代码中,您应该将send循环更新为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;