【发布时间】:2015-09-22 16:37:53
【问题描述】:
我非常需要帮助。
我有一个基于 Indy 10 的 TIdTCPServer 组件的 TCP 服务器应用程序,我想在 Win32 和 Android 上运行。我正在使用 Delphi XE7。
服务器必须处理 10 个客户端。
应用程序在 Windows 和 Android 上运行良好:CONNECTING、SENDING、RECEIVING 数据,但
OnDisconnect事件仅在 Android 上存在问题。该应用程序在 Windows 上运行良好,但在 Android 上断开客户端和事件时存在很大问题:TCPServer.Active := FALSE。在 90% 的情况下,当我断开客户端连接时,应用程序会自动关闭。当我启动服务器时:
TCPServer1.Active := TRUE,然后我关闭它TCPServer1.Active := FALSE,没有连接客户端,应用程序工作正常。
我在下面添加我的代码。我使用了 Remy Lebeau 的提示。
- 我正在使用两个客户端测试应用程序。
- 我在 ListView 中显示连接的客户端。
- 我不是从服务器事件中更新 ListView,而是在 Timer 事件中。
- App 有 3 个按钮:Server Listen、Server Close、Send(在 ListView 中选择的客户端编号)
请帮忙。
// TMyContext
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
inherited;
FQueue := TIdThreadSafeStringList.Create;
FEvent := TEvent.Create(nil, True, False, '');
end;
destructor TMyContext.Destroy;
begin
FQueue.Free;
FEvent.Free;
inherited;
end;
procedure TMyContext.AddMsgToQueue(const Msg: String);
begin
with FQueue.Lock do
try
Add(Msg);
FEvent.SetEvent;
finally
FQueue.Unlock;
end;
end;
function TMyContext.GetQueuedMsgs: TStrings;
var
List: TStringList;
begin
Result := nil;
if FEvent.WaitFor(1000) <> wrSignaled then Exit;
List := FQueue.Lock;
try
if List.Count > 0 then
begin
Result := TStringList.Create;
try
Result.Assign(List);
List.Clear;
except
Result.Free;
raise;
end;
end;
FEvent.ResetEvent;
finally
FQueue.Unlock;
end;
end;
// TCPServer
procedure THeaderFooterwithNavigation.TCPServer1Connect(AContext: TIdContext);
var
client : String;
datetime : TDateTime;
begin
datetime := now;
// CLIENT CON INFO
client := AContext.Binding.PeerIP;
TThread.Queue(nil,
procedure
begin
TCPServer1.Contexts.LockList();
mmoLog.Lines.Add ('CONNECT: ' + AContext.Connection.Socket.Binding.PeerIP
+ ' : ' +
IntToStr(AContext.Connection.Socket.Binding.PeerPort) + ' ' +
DateToStr (datetime) + ' ' + TimeToStr (datetime)
);
TCPServer1.Contexts.UnlockList();
if TCPServer1.Contexts.Count = 1 then
edtPort1.Text := IntToStr(AContext.Connection.Socket.Binding.PeerPort);
if TCPServer1.Contexts.Count = 2 then
edtPort2.Text := IntToStr(AContext.Connection.Socket.Binding.PeerPort);
AContext.Connection.Socket.Binding.Send('HELLO');
// CLIENTSDATA LIST
ClientsList.Add (' ', AContext.Connection.Socket.Binding.PeerIP, AContext.Connection.Socket.Binding.PeerPort);
LV_Refresh ();
end
);
end;
procedure THeaderFooterwithNavigation.TCPServer1Disconnect(
AContext: TIdContext);
var
cl_item : Integer;
datetime : TDateTime;
begin
try
datetime := now;
if fSvrClose = FALSE then begin
fClDiscon := TRUE;
buff_discon [pos_ip] := AContext.Connection.Socket.Binding.PeerIP;
buff_discon [pos_port] := IntToStr (AContext.Connection.Socket.Binding.PeerPort);
buff_discon [pos_date] := DateToStr (datetime);
buff_discon [pos_time] := TimeToStr (datetime);
end;
finally
AContext.Connection.Socket.InputBuffer.Clear;
AContext.Connection.Disconnect;
end;
end;
procedure THeaderFooterwithNavigation.TCPServer1Exception(AContext: TIdContext;
AException: Exception);
begin
ShowMessage ('Error');
end;
procedure THeaderFooterwithNavigation.TCPServer1Execute(AContext: TIdContext);
var
buff : String;
List : TStrings;
I : Integer;
buffout : String;
n : Integer;
// FOR DISCONNECT
{$IFDEF MSWINDOWS}
clist : TList;
{$ENDIF MSWINDOWS}
{$IFDEF Android}
clist : TList <TIdContext>;
{$ENDIF Android}
begin
if fSvrClose = FALSE then begin
// READ MESSAGES FROM THE CLIENTS
fDisconAccess := FALSE;
// SEND MESSAGES TO THE CLIENTS
List := TMyContext(AContext).GetQueuedMsgs;
if List <> nil then begin
try
for I := 0 to List.Count-1 do
AContext.Connection.IOHandler.Write(List[I]);
finally
List.Free;
end;
end;
// READ MESSAGE FROM CLIENTS
if AContext.Connection.IOHandler.CheckForDataOnSource(200) then begin
buffout := AContext.Connection.IOHandler.ReadLn();
TThread.Queue(nil,
procedure
begin
if AContext.Connection.Socket.Binding.PeerPort = StrToInt(edtPort1.Text) then begin
edtRec1.Text := buffout;
end;
if AContext.Connection.Socket.Binding.PeerPort = StrToInt(edtPort2.Text) then begin
edtRec2.Text := buffout;
end;
end
);
end;
fDisconAccess := TRUE;
end;
end;
// USER INTERFACE
procedure THeaderFooterwithNavigation.SendMessage (const IP : String; port : Word; Msg: string);
var
I: Integer;
begin
with TCPServer1.Contexts.LockList do
try
for I := 0 to Count-1 do begin
with TMyContext(Items[I]) do begin
if (Binding.PeerIP = IP) and (Binding.PeerPort = port) then begin
AddMsgToQueue(Msg);
Break;
end;
end;
end;
finally
TCPServer1.Contexts.UnlockList;
end;
end;
procedure THeaderFooterwithNavigation.Timer1Timer(Sender: TObject);
begin
Get_ClientsNum ();
// UPDATE UI (USER INTERFACE)
UpdateUI ();
// BUTTONS
if TCPServer1.Active = TRUE then begin
btnListen.Enabled := FALSE;
edtStatus.Text := 'LISTENING';
end else begin
btnListen.Enabled := TRUE;
edtStatus.Text := 'CLOSED';
end;
// ON SINGLE CLIENT DISCONNECT
if fClDiscon = TRUE then begin
fClDiscon := FALSE;
CL_DeleteClient (buff_discon [pos_ip], StrToInt (buff_discon [pos_port]));
LV_Refresh ();
mmoLog.Lines.Add ('DISCON: ' + buff_discon [pos_ip] + ' : ' + buff_discon [pos_port] + ' ' +
buff_discon [pos_date] + ' ' + buff_discon [pos_time] );
end;
end;
procedure THeaderFooterwithNavigation.TitleActionUpdate(Sender: TObject);
begin
if Sender is TCustomAction then
begin
if TabControl1.ActiveTab <> nil then
TCustomAction(Sender).Text := TabControl1.ActiveTab.Text
else
TCustomAction(Sender).Text := '';
end;
end;
procedure THeaderFooterwithNavigation.btnCloseClick(Sender: TObject);
var
{$IFDEF MSWINDOWS}
clist : TList;
{$ENDIF MSWINDOWS}
{$IFDEF Android}
clist : TList <TIdContext>;
{$ENDIF Android}
i : Integer;
ip : String;
port : Word;
datetime : TDateTime;
begin
TThread.Queue(nil,
procedure
var
n : Integer;
begin
datetime := now;
if Clients_Num = 0 then begin
TCPServer1.StopListening();
TCPServer1.Active := FALSE;
end else begin
fSvrClose := TRUE;
// SERVER CLOSE
if fSvrClose = TRUE then begin
while fDisconAccess = FALSE do begin
end;
try
clist := TCPServer1.Contexts.LockList;
for n := 0 to (clist.Count - 1) do begin
try
TIdContext (clist[n]).Connection.Socket.WriteBufferClear;
TIdContext (clist[n]).Connection.Socket.InputBuffer.Clear;
ip := TIdContext (clist[n]).Connection.Socket.Binding.PeerIP;
port := TIdContext (clist[n]).Connection.Socket.Binding.PeerPort;
TIdContext (clist[n]).Connection.Disconnect;
CL_DeleteClient (ip, port);
mmoLog.Lines.Add ('DISCON: ' + ip + ' : ' + IntToStr(port) + ' ' +
DateToStr (datetime) + ' ' + TimeToStr (datetime) );
sleep (100);
except
end;
end;
finally
TCPServer1.Contexts.UnlockList;
TCPServer1.Active := FALSE;
fSvrClose := FALSE;
LV_Refresh ();
end;
end;
end
);
end;
procedure THeaderFooterwithNavigation.btnListenClick(Sender: TObject);
var
port : Word;
begin
port := StrToInt (edtPort.Text);
TCPServer1.Contexts.Clear;
TCPServer1.Bindings.Clear();
if (port > 200) and (port < 65535) then begin
TCPServer1.DefaultPort := StrToInt (edtPort.Text);
end else
TCPServer1.DefaultPort := 30000;
TCPServer1.Bindings.Add.IPVersion := Id_IPv4;
if TCPServer1.Active = FALSE then begin
TCPServer1.Active := TRUE;
end;
end;
procedure THeaderFooterwithNavigation.btnSendClick(Sender: TObject);
var
ip : string;
port : Word;
item : Integer;
begin
item := LV.ItemIndex;
if (item > -1) then begin
ip := ClientsList.Items[item].IP;
port := ClientsList.Items[item].Port;
SendMessage (ip, port, edtSend.Text);
end;
end;
procedure THeaderFooterwithNavigation.Get_ClientsNum ();
var
{$IFDEF MSWINDOWS}
clist : TList;
{$ENDIF MSWINDOWS}
{$IFDEF Android}
clist : TList <TIdContext>;
{$ENDIF Android}
begin
try
clist := TCPServer1.Contexts.LockList();
Clients_Num := TCPServer1.Contexts.Count;
finally
TCPServer1.Contexts.UnlockList;
end;
end;
【问题讨论】:
标签: android delphi delphi-xe7 indy10