인디버젼은 indy9.0.18입니다.
delphi 7입니다.
-- excute,부분입니다.
procedure TMainForm.IdTCPServerExecute(AThread: TIdPeerThread);
var
Header : TPacketHeader;
begin
try
if (AThread <> nil) and (AThread.Connection.Connected) then
begin
AThread.Connection.ReadBuffer(Header, HEADER_SIZE );
if( not SocketProc.ProcessData( AThread, Header.Code, Header.Size ) ) then begin
AThread.Connection.Disconnect;
//AThread.Stop;
end;
end;
Sleep(0);
except
on E: Exception do
begin
if not ( E is EIdConnClosedGracefully )
and not ( E is EIdNotConnected ) and not (E is EIdSilentException) then
begin
if (AThread <> nil) and AThread.Connection.Connected then
begin
AThread.Connection.Disconnect;
end;
end;
end;
end;
end;
- 서버단에서 클라언트쪽에 데이타를 줄때 다음과같이 보내줍니다.
AThread.Connection.OpenWriteBuffer;
try
AThread.Connection.WriteBuffer( pHeader^, HEADER_SIZE );
if ( Size > 0 ) then begin
AThread.Connection.WriteBuffer( pSendPacked^, Size );
end;
//데이타 리스트가 있는경우에는 해당 리스트 데이타를 Write한다.
//여기스트림부분은 현접속회원리스트(전체) 라든지 아니면 메모리스트 등을 넘깁니다..
if ( AStream <> nil ) then begin
if ( DataListSize > 0 ) then begin
AThread.Connection.WriteStream( AStream, True, False, DataListSize ); <=== 이부분이 상당히 의심스러움.. (혹시 이부분은 데이타양이 많아지면 문제가 되나요?)
end;
end;
AThread.Connection.CloseWriteBuffer;
except
on E:Exception do
begin
try
AThread.Connection.CancelWriteBuffer;
except
end;
end;
end;
받을시 클라이언트
-- 보통은 이렇게 받고
IdTCPClient.ReadBuffer( ReceivePacked, Size );
-- 스트림이 넘어올시에는 다음과같이 받습니다.
TmpMemStream := TMemoryStream.Create;
try
try
if ( Size > 0 ) then
begin
IdTCPClient.ReadStream( TmpMemStream, Size, False );
TmpMemStream.Position := 0;
for ii := 0 to (Size div ListSize) - 1 do
begin
//new(pTmpListPacked);
GetMem( pTmpListPacked, ListSize );
TmpMemStream.ReadBuffer( pTmpListPacked^, ListSize );
DataList.add( pTmpListPacked );
end;
Result := DataList;
end;
except
on E: Exception do
begin
SendJBLoggerException('ReceiveStreamFromServer Error ', E, 'PSS');
end;
end;
finally
TmpMemStream.Free;
end;
-- 회원리스트컨트롤은 하나의 criticalsection을 사용합니다.
-- 회원리스트는 클라이언트쪽은 하나의 room리스트를 만들어서 그안에 넣고 뺄때 criticalsection을 사용하구요.
-- 브로드캐스팅(모든사용자에게 어떤값들을 줄때)시에는 criticalsection을 사용하지않고 그냥 하나의room리스트를 for문돌려서 send하구요... (이부분에서 가끔 엄청난 (한 5개~10개정도 에러가납니다. 인볼케이션에러..) 헐..) , 어디보니 broadcasting할때는 lock을 해주는게 안정하다고 해서 room리스트에 걸고있는 criticalsection으로 걸어봤는데 그다지 상태가 좋지는 않았습니다.
-- 디비풀쪽은 일반적으로 생각하는 풀링을 사용하면서 항상 mssql의 프로시져를 건드립니다.
여기서 한가지 의심스러운것은 프로스져들을 보면 update문과 insert 문이 있는데 autocommit을 true로 사용하여 하나의 프로시져안에서 insert도 하고 update하고 마지막에 select도 합니다. 건드리는테이블은 그냥 이것저것 건드리구요.. 어디에서 보니 insert, update는 트렌젝션을 항상 먹여서 하나씩 하는게 좋다고 하는데.. 그렇다면 현재 사용하는 프로시져들을 하나씩 잘게 썰어서 insert, update, delete 를 따로만들고 그때마다 트랜잭션을 먹이고, select를 따로 만들어 여러개로 만들어 정확히 하나하나테이블마다 트랜잭션을 적용해야하는지요... 디비컴포넌트는 zeosdbo-6.1.5-stable 를 사용합니다.
1. 일반 update쪽할때.. (트랜젝션 적용)
Connection.SetAutoCommit(False);
Connection.SetTransactionIsolation(tiReadCommitted);
....PrepareStatement 날리고
Connection.commit;
2. update, insert 등도 하면서 마지막에 select할시 (트랜잭션 적용안함)
Connection.SetAutoCommit(True);
....PrepareStatement 날리고
Result := Statement.ExecuteQueryPrepared; 해서 ResultSet데이타를 받습니다.
-- 서버단의 에러현상
-> 1. 접속은 되는데 (로그인) , disconnect쪽이 잘안되는부분 (,로그아웃), 로그인했다가 로그아웃하고 다시 로그인하면 이전 로그인한 Athread가 남아있습니다 ( 아니면 제가 만들 room리스트에서 제대로 삭제가 안된것일수도.).
-> 2. 로그인한 회원들은 사용되는데 로그아웃했다가 다시 로그인할려하면 서버에 접속이 안되는 현상..
꼭좀 도움 부탁드리겠습니다. 델파이 쉽게만 생각했는데 너무 어렵네요...
단어하나라도 좋으니 조언부탁드립니다...
소켓이라는게 끊어라 그런다고 무조건 넵 그러진 않을 수도 있습니다.
(혹시라도 TCP Transition Diagram 같은거 대충 보셨다면... 함 진지하게 봐보세요.
FIN_WAIT 같은거 ....)
(단순히.. 예를 든것뿐입니다)
WHILE TRUE DO
BEGIN
IF NOT Socket.Connected THEN // 정말로 끊김.
BREAK;
INC(Loop_Count);
IF Loop_Count > 50 THEN // 0.1초 * 50 = 5초 맞죠? 기다릴 만큼 기다렸으니까 나갑시당.
BREAK;
SLEEP(100);
END;
서버가 다운되는 현상은 조금 더 상세히 기술하셔야 할 듯...
그리고, 소켓 프로그램이든 머든,,,
버퍼 사이즈 보다 큰 데이타를 버퍼에 밀어넣을 수 있는 부분이 있으면 안되겠죠..
Size > 0 테스트만 하시지 마시고..
Size <= Buffer_Size 같은 것도 병행하시면 좀 코드가 튼튼해 보이겠네요.
디버깅은 게임입니다.
재밌다고 생각하시고 꼭 싸워서 이겨보세요.