안녕하세요.
idTCPClient를 이용한 RecvThread 인데요. 구조가 잘못되었나요?
목적 :
서버는 1개의 세션밖에 허용하지 않기 때문에
idTCPClient 한개로 TCP Connection을 유지한채 데이터를 보내고 받고 싶습니다.
비동기 전송을 위해서 보내고 받는 부분을 분리해서
수신부를 Thread로 만들어서 수신된 데이터를 Queue에 넣게 됩니다.
수신시 락이나 cpu를 점유하지 않기 위해서 0.1초의 시간 단위로 데이터를 받습니다.
//수신 쓰레드.
type
TTCPRecvThread = class(TThread)
private
FClient: TIdTCPClient;
FData: string;
FHostIP : String;
protected
procedure Execute; override;
public
constructor Create(AClient: TIdTCPClient); reintroduce;
end;
// 구현부.
constructor TTCPRecvThread.Create(AClient: TIdTCPClient);
var
sName : String;
begin
inherited Create(True);
sName := 'TCP RECV Thread:'+AClient.Host;
self.NameThreadForDebugging(sName);
FClient := AClient;
FHostIP := AClient.Host;
end;
procedure TTCPRecvThread.Execute;
var
t: cardinal;
// idRecvBuf: TIdBytes;
iRecvLen: integer;
sRecvStr: AnsiString;
i: integer;
RecvByte: Byte;
begin
try
try
FreeOnTerminate := True;
frmMDRServer.Printlog('TTCPRecvThread Start;', False);
while not Terminated do
begin
WaitForSingleObject(handle, 100); //루프시 0.1의 딜레이를 가지고 돌기 위해.
if gv_AppClose then exit; //전역 프로그램 종료 변수.
// 연결되지 않을때. 루프 대기를 위하여.
if (not(Assigned(FClient))) or (not FClient.Connected) then
begin
WaitForSingleObject(handle, 100);
Continue;
end;
FClient.IOHandler.CheckForDisconnect(True, True);
// 버퍼 체크.
if FClient.IOHandler.InputBufferIsEmpty then
begin
FClient.IOHandler.CheckForDataOnSource(100);
if FClient.IOHandler.InputBufferIsEmpty then
Continue;
end;
WaitForSingleObject(handle, 30);
iRecvLen := FClient.IOHandler.InputBuffer.Size;
sRecvStr := '';
// H/W가 전송하는 방식이 시리얼에서 tcp로 바뀌어 혹시나 하는 마음에
// 중간에 데이터가 있어도 조작하기 쉽도록 Byte단위처리함.
for i := 1 to iRecvLen do
begin
RecvByte := FClient.IOHandler.ReadByte;
if RecvByte = 13 then
Continue;
if RecvByte = 10 then // #10 단위의 문자열을 처리하게 됩니다.
begin
>함수전송처리Quue로Function(sRecvStr, FClient.Host); // 큐로 전송하여 처리함 Push pop시 CriticalSection거침.
// 전송받은 문자열을 다른 쓰레드큐에서 직렬로 처리합니다. Message 처리도 같은 효과라고 생각합니다.
sRecvStr := ''; // 문자열 초기화.
end
else
begin
sRecvStr := sRecvStr + Chr(RecvByte);
end;
if gv_AppClose then exit;
end;
frmManForm.Printlog(format('Recv Msg : %s %d', [FClient.Host,iRecvLen]), False ); //메인폼에 로그큐에 넘김.
end;
except
// raise;
on E: EidException do // IdException
begin
// 예외가 발생한다면 출력하고.
frmMDRServer.PrintLog('TTCPRecvThread E :' + FClient.Host + ' > ' + E.Message, False); // 로그 큐에 메시지.
end;
end;
finally
frmMDRServer.PrintLog('TTCPRecvThread Exit;' + FClient.Host, False); // 로그 큐에 메시지.
// 만약 종료한다면 접속을 끊고 나감.
FClient.IOHandler.InputBuffer.Clear;
FClient.Disconnect
// Thread가 해제 될때 FClient에 할당된 idFTPCleint가 해제 되지 않도록 Nil로 세팅.
FClient := nil;
end;
end;
// 연결시 . ============================
var
aRecvThread : TTCPRecvThread; //서버 1에 접속.
aRecvThread2 : TTCPRecvThread; //서버 2에 접속.
begin
// .. 접속값.
frmMainform.IdTCPClient_N1.Connect;
if aRecvThread = nil then
begin
aRecvThread := TTCPRecvThread.Create(frmMainform .IdTCPClient_N1); // FClient 에 할당.
aRecvThread .Resume;
end;
// .. 접속값.
frmMainform.IdTCPClient_N2.Connect;
if aRecvThread2 = nil then
begin
aRecvThread2 := TTCPRecvThread.Create(frmMainform .IdTCPClient_N2); // FClient 에 할당.
aRecvThread2.Resume;
end;
이런식으로 사용하고 있습니다.
이게 잘못된거 같아서 질문드립니다.
이게 잘못되어서 app.exe 는 응답없음 으로 빠지고.
델파이는 Ctrl+F2해서 중지하면. 다음 실행시 10048 소켓 Use 에러가 나서 PID는 죽은 건데. Establish 커넥션이 남아 있어서
재부팅을 해야 합니다. ㅠ. tcpview 같은 것으로 죽여지지 않더라구요. Pid가 이미 없는 것이라서.
그래서 에러가 걸렸을때. CTRL+F2로 중지하지 말고. bds.exe를 죽여 버려야 이런게 안남아 있더라구요.
먼가 이 쓰레드가 죽을때..
잘못처리된듯 한데. 찍히지가 않네요.
|