#1
지금부터 구현해 볼 프로그램은 Telnet Clinet 가 접속했을때 Client 가 DATE, TIM
E,
QUIT Command 를 이용하여 날짜와 시간을 알려주는 TCP Server 를 구현해보고자 합니
다.
============================================================================
1. 먼저, 새로운 프로젝트로 Application 을 하나 생성하십시오...
============================================================================
그런 다음, Component 의 Internet tab의 ServerSocket 을 하나 폼에다 끌어다 놓
습니다.
이름이 기본적으로 ServerSocket1 으로 설정이 될것입니다.
그럼, TServerSocket 의 속성을 살펴보도록 하겠습니다.
i) Active
: ServerSocket 이 Open 된 상태에서는 True 로 설정이 됩니다. 이값은 상태 읽
기
목적으로 사용하시면 됩니다.
ii) Port
: TCP 통신을 하기 위한 Port 번호입니다. TV 에서 Channel 에 해당하겠지요...
이 강좌에서는 telnet 과 같은 23 번을 사용하겠습니다.
iii) ServerType
: stNonBlocking 은 UNIX 계열에서 사용하는 NonBlocking Socket 과 유사한 Dat
a
처리방법입니다. UNIX 계열에서는 recv 함수는 기본적으로 Data 가 올때까지
대기하고 있다가 데이타가 오면 recv 함수를 빠져나와서 다음 과정을 수행하
도록
합니다. 그러나 이렇게 되면 데이타가 오기전까지는 다른 처리를 하지 못합니
다.
그러므로, fcntl 을 이용해서 Socket 을 Nonblocking 으로 설정하면, recv 함
수
에서 데이타가 없을 경우 바로 함수를 빠져나와서 다른 과정을 계속 처리할
수
있도록 해줍니다.
Windows 에서는 AsyncSocket 이라는 것으로 Unix 의 NonBlocking 과 비슷하게
처리한다고 합니다.
Delphi 의 TServerSocket 에서는 stNonBlocking 으로 설정하면 OnClientRead,
OnClientWrite Event 가 발생하여 EventHandler 에서 모든 Client 와의 통신
을
관리할 수 있습니다. Client에서 서버에 접속이 되면, OnAccept, OnClientCon
nect
Event 가 생성이 되고, Client와 접속이 끊어지면 OnDisconnect 가 생성됩니
다.
Client 에서 데이타가 오면 OnClientRead Event 가 발생하는데 Client 와 데
이타를
주고받기 위해서는 EvnetHandler 의 두번째 파라미터인 Socket 을 이용하시면
됩니
다(Socket.ReceiveText, Socket.Socket.SendText()).
제 생각에는 Chating 서버 프로그램에서 이러한 방식을 쓰면 좋을것 같군요.
Client
간의 주고받는 메시지를 하나의 Event Handler 에서 관리할 수 있으니까요...
: stThreadBlocking 은 UNIX 계열에서 일반적으로 처리하는 방식인 fork 를 이
용해서
프로세스를 생성하는 것과 유사한 방법입니다. Windows 에서는 프로세스를 생
성하는
것보다는 Thread 를 생성합니다. 그러니까 Client 가 접속이 되면 Client 하
나당
하나의 Thread 가 떠서 그 Client 전용으로 데이타를 처리해 주는 방식입니다
. 예를
들어 Telnet 이나 FTP 서버처럼 1:1 통신을 담당하는 TCP 서버 프로그램에 적
당하겠
지요.. 이 강좌에서는 이 stThreadBlocking 을 사용할 것입니다.
* TServerSocket 에서 ServerType 에 따라서 발생하는 Event
stNonBlocking : OnAccept, OnClientConnect, OnClientDisconnect, OnClie
ntRead,
OnClientWrite
stThreadBlocking : OnAccept, OnClientDisconnect, OnGetThread, OnGetThrea
dStart,
OnGetThreadEnd
vi) Service
: Windows 에서는 Services file 에 이미 알려진 Application 에 대한 포트번호
가 있습
니다. 예를들어 FTP 는 21, telnet 은 23 번과 같은 것입니다. 이 Service에
해당하는
서비스명을 써주면 됩니다. 그러나 이 값은 Setting 하지 않아도 상관없습니다
.
v) ThreadCacheSize
: stThreadBlocking 일때 사용하는 값으로 Application 이 생성될때 미리 생성하
는 Thread
갯수입니다. 앞에서도 설명하였듯이 Client 가 접속을 하면 그에 해당하는 Thr
ead를
생성한다고 하였습니다. 그리고, Client 가 접속이 끊어지면 Thread 를 다시
없앱니다.
그러므로, Clinet 의 접속이 매우 빈번한 Server 의 경우에는 매번 Thread 를
생성하고
없애므로 매우 비효율적입니다. 그러므로, 이 값을 설정해 두면, 항상 이 갯수
의 Thread
는 메모리에 상주하고 있다가 Client 가 접속을 하면 이 Thread 가 서비스를
담당하게
됩니다. 제 개인적인 생각으로는 Delphi 의 매우 뛰어난 기능이 아닌가 생각됩
니다.
================================================================
2. Type 설정 부분에 Thread 에 해당하는 class type을 선언합니다.
================================================================
Thread 는 TServerClientThread 를 상속받아야 하며, ClientExecute를 반드시 ove
rride
해야 합니다. ClientExecute Method 가 Thread 의 Main Routine 이 됩니다.
type
TMyServerThread = class(TServerClientThread)
public
procedure ClientExecute; override;
end;
=========================================================================
3. Form의 Create와 Close에 ServerSocket의 설정과 종료코드를 넣습니다.
=========================================================================
ServerSocket 은 Open 과 Close Method 를 이용합니다.
procedure TForm1.FormCreate(Sender: TObject);
begin
ServerSocket1.Port:=23; // Telnet Port
ServerSocket1.ServerType:=stThreadBlocking;
ServerSocket1.ThreadCacheSize:=10;
ServerSocket1.Open;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ServerSocket1.Close; // 연결된 모든 Connection 을 닫고, ServerSocket 을 닫는
다.
end;
=====================================================================
4. OnGetThread Event Handler 에서 Thread 를 생성합니다.
=====================================================================
ServerSocket1 에서 Events 의 OnGetThread 를 더블클릭하면 OnGetThread Event 에
해당하는 Event Handler 가 생성됩니다. Procedure 안에 위에서 선언한 TMyServerThr
ead
type 으로 SocketThread 를 생성합니다. 생성시에 Thread 에 연결된 ClientSocket을
넘겨
줍니다.
SocketThread := TMyServerThread.Create(False, ClientSocket);
연결되는 Client 수를 제한하기 위해서는 ServerSocket1.Socket.ActiveConnections
의
값을 이용하면 됩니다.
procedure TForm1.ServerSocket1GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
var
iMaxClient: Integer;
begin
iMaxClient:=2;
if ServerSocket1.Socket.ActiveConnections>iMaxClient then
begin
ClientSocket.SendText('Sorry! Max Client Connected.'+#13#10);
ClientSocket.Close;
end
else
begin
SocketThread := TMyServerThread.Create(False, ClientSocket);
end;
end;
====================================================================
5. ClientExecute Pocedure 를 구현합니다.
====================================================================
아래는 Delphi Help 에 나와있는 ClientExecute Procedure 예제입니다. 기본적인
골격은
아래와 같이 코딩하시고 User Code 부분에 원하시는 코드를 첨가하시면 됩니다. Clie
nt 와
데이타를 교환하기 위해서는 Stream.Read, Stream.Write 를 사용하시면 됩니다.
procedure TMyServerThread.ClientExecute;
var
Stream : TWinSocketStream;
Buffer : array[0 .. 9] of Char;
begin
{ Client 와 연결되어 있을경우에만 수행한다. }
while (not Terminated) and ClientSocket.Connected do
begin
try
{ ClientSocket 과 통신하기 위해서 SocketStream 을 생성한다.}
{ TimeOut 을 60초로 설정한다. }
Stream := TWinSocketStream.Create(ClientSocket, 60000);
try
FillChar(Buffer, 10, 0); { buffer 를 Clear 한다. }
{ 60초 동안 데이타 수신을 기다린다. }
if Stream.WaitForData(60000) then
begin
if Stream.Read(Buffer, 10) = 0 then { 수신된 데이타가 없으면 }
ClientSocket.Close; { 연결 종료. }
{ 이 부분에 User Code 를 넣으시면 됩니다. }
...
end
else
ClientSocket.Close; { Socket 연결이 Not Ready 상태이면 연결종료. }
finally
Stream.Free;
end;
except
HandleException;
end;
end;
end;
그런데 위의 코드는 while loop 문에서 매번 TWinSocketStream 을 생성하므로 비효
율
적입니다. 그러므로, WinSocketStream 을 먼저 생성하는 것이 좋을 것 같군요..
그리고, Telnet Client 로 접속을 하면 Clinet 를 키를 하나 누를때마다 데이타가 전
송이
됩니다. 그러므로, 서버에서 데이타를 처리하기 위해서는 CRLF(#13#10) 이 올때까지
전송된 데이타를 Buffer 에 넣어두었다가 처리하시면 됩니다.
아래코드는 Clinet 에서 TIME, DATE, QUIT 명령에 따라서 처리하는 것을 보여줍니
다.
procedure TMyServerThread.ClientExecute;
var
Stream: TWinSocketStream;
iTimeOut: Integer;
InBuf, OutBuf: array [0..1024] of Char;
Buffer, strCmd, strRet: String;
i: Integer;
begin
iTimeOut:=60000;
try
Stream := TWinSocketStream.Create(ClientSocket, iTimeOut);
{ Welcome Message .... }
if ClientSocket.Connected then
begin
FillChar(OutBuf, 1024, 0);
StrPCopy(OutBuf, 'Welcome ! TimeServer written by Delphi.'+#13#10#13#10+
'Method : TIME, DATE, QUIT'+#13#10#13#10);
Stream.Write(OutBuf, StrLen(OutBuf));
end;
Buffer:='';
while (not Terminated) and ClientSocket.Connected do
begin
if not Stream.WaitForData(iTimeOut) then
begin
ClientSocket.Close;
Break;
end;
try
i:=Stream.Read(InBuf, 1024);
except
ClientSocket.Close;
Break;
end;
if i = 0 then
begin
ClientSocket.Close;
Break;
end;
InBuf[i]:=#0;
Buffer:=Buffer+StrPas(InBuf);
Stream.Write(InBuf, StrLen(InBuf)); // Echo to Client
i:=Pos(#13#10, Buffer);
if i>0 then
begin
strCmd:=UpperCase(Copy(Buffer, 1, i-1));
Buffer:='';
if strCmd='DATE' then
strRet:=FormatDateTime('yyyy-mm-dd', Now)
else if strCmd='TIME' then
strRet:=FormatDateTime('hh:nn:ss', Now)
else if strCmd='QUIT' then
begin
StrPCopy(OutBuf, 'Good Bye !!!'+#13#10#0);
Stream.Write(OutBuf, StrLen(OutBuf));
ClientSocket.Close;
Break;
end
else
strRet:='Unknown Command.';
StrPCopy(OutBuf, strRet+#13#10#0);
Stream.Write(OutBuf, StrLen(OutBuf));
end;
end;
except
HandleException;
end;
Stream.Free;
end;
====================================================================
6. 프로젝트를 실행합니다.
====================================================================
이제 코드의 구현은 끝났습니다. 프로젝트를 실행하고 도스창을 열어서,
telnet localhost
하면 화면에 다음과 같은 메시지가 나올 것입니다.(만일 unknown host 라는 메시지
가
나오면 telnet 127.0.0.1 와 같이 입력하세요..)
Welcome ! TimeServer written by Delphi.
Method : TIME, DATE, QUIT
그럼, time 을 친 다음 를 누르십시오... 화면에 결과가 제대로 나오나요?
축하합니다. 이제 여러분은 TCP Socket 구현하실 수 있게 됐습니다...
다음번엔 NT Service 로 한번 만들어 보도록 하겠습니다. Windows95, 98 사용자
에게는 해당되지 않는 내용이겠군요.
NT Service 란 UNIX 계열의 deamon 과 비슷한 프로그램을 말합니다.
NT 에서 일반 Application 은 사용자 Login 이 되어야지만 프로그램을 기동합니다.
그러나 NT Service 로 만들면 Kernel 이 올라온 다음, Kernel 에 의해서 Service
프로그램이 기동되므로 사용자가 일일히 Login 을 하지 않아도 됩니다.
그럼, 다음회에 뵙도록 하지요...
끝까지 읽어주셔서 감사합니다.....
#2
먼저 TServerSocket의 threadblocking mode를 이해하기 위해 실행순서를 분석하
므로 non-blocking부분은 제외함을 말힌다.
TServerSocket의 실행 순서를 알기 전에 간단히 server의 동작 방식을 이해해야
한다. server는 무엇인가를 service하기 위해 있는거다. service를 효율적으로 하는
방식은 꼭 computer안에만 있는건 아니다.
114안내를 보자. 전화를 걸면 맨 먼저 듣는 소리는 "안내 1234호 입니다" 다.
즉 많은 안내원중에 1234번 안내원에게 연결해 준다는 소릴게다. 그리고 안내원의
"네네 안녕하십니까?"가 나오고, 내가 번호를 물으면 안내를 해주고 전화를 끋는다.
이게 server하고 똑같다. 그럼 server는 어떻게 동작하나, 먼저 client의 연결이
오면 연결을 받고(accept) 그리고 대기하고 있는 service에게 연결을 전달해 준다.
그리고 또 기다리다 연결을 받고(accept)또 service에게 전해주고 이걸 반복한다.
114의 "안내 1234호 입니다" 처럼. 그리고 나면 service가 그 연결을(socket) 받아
요청을 듣고, 응답을 하고, 이를 일정회 반복하고 연결을 종료한다. 그리고 114는
연중 무휴이겠지만 처음 server를 시작하는걸 Listen이라고 한다.
자그럼 TServerSocket을 보자 위의 예는 threadbloking mode의 예이다. 이 경우
TServerSocket의 TServerAcceptThread가 첫 accept를 하는 부분이고,
TServerClientThread는 service를 하는 부분이다. server를 만드는 것은 이
service를 구현 하는 것이다. 그리고 TServerWinSocket이 첫 연결하는 전화기라면
TServerClietnWinSocket이 안내양이 사용하는 전화기다.
그럼 TServerSocket의 실행 순서를 확인해 보자.
1. TServerSocket의 생성
Form이 생성 될때 자동으로 호출되며, FServerSocket := TServerWinSocket.Create;
로 TServerWinSocket을 생성한다.
2. TServerSocket.Active := true로 설정
TServerSocket.SetActive(true)를 호출하고, 이함수는 내부에서 FActive := true;
를 지정하고 DoActivate(true)를 호출한다.
DoActivate(true)는 TServerWinSocket.Listen(FHost,FAddress,FService,FPort,5)를
호출한다.
TServerWinSocket.Listen은 TCustomWinSocket.Listen(FHost,FAddress,FService,
FPort,5)를 호출하고, FServerAcceptThread := TServerAcceptThread.Create를
생성한다.
TCustomWinSocket.Listen은 FSocket := socket(PF_INET,SOCK_STREAM,IPPROTO_IP)로
실재 WinSock32를 호출하고, InitSocket(FHost,FAddress,FService,FPort,False)에
서 TSockAddrIn를 구한다. DoSetASyncStyle에서 WSAAsyncSelect(FSocket,0,0,
Longint(Byte(FAsyncStyles)))와 ioctlsocket(FSocket,FIONBIO,0)를 WinSock32
함수를 호출하고 Event(Self,seListen)를 호출한다.
TCustomWinSocket.Event(Self,seListen)은 FOnListen(Self,Socket)에 등록된 event
handler를 출한다.
3. TServerAcceptThread의 Execute 실행
TServerWinSocket.Accept(FServerSocket.SocketHandle)를 계속 실행한다.
TServerWinSocket.Accept는 getsockopt(INVALID_SOCKET,SOL_SOCKET,SO_OPENTYPE,
PChar(@OldOpenType),Len)로 시작하고, setsockopt(INVALID_SOCKET,SOL_SOCKET,
SO_OPENTYPE,PChar(@OldOpenType),Len)로 끝낸다 즉 OpenType을 보관해 놓았다가
다시 되 돌린다. 그 사이에서는 setsockopt(INVALID_SOCKET,SOL_SOCKET,
SO_OPENTYPE,PChar(@SO_SYNCHRONOUS_NONALERT),Len)로 setting을 변경하고,
ClientWinSocket := WinSock.accept(Socket, @Addr, @Len)인 WinSock32함수를
호출해 실재 connection을 기다리게 된다. 그리고 ClientSocket :=
GetClientSocket(ClientWinSocket)을 호출해 TServerClientWinSocket을 얻고
FOnSocketEvent(Self, ClientSocket, seAccept)로 지정된 event handler를 호출
하고 ClientSocket.ASyncStyles := []를 지정하고 GetServerThread(ClientSocket)
로 TServerClinetThread를 얻는다.
GetClientSocket은 FOnGetSocket(Self,Socket,Result)로 지정된 event handler로
TServerClinetWinSocket을 얻을려고 시도해보고,
안되면 TServerClientWinSocket.Create(Socket, Self)로 직접 생성한다.
GetServerThread는 먼저 FActiveThreads에서 ClientSocket = nil인 즉 현재
service를 않하는 TServerClientThread를 얻어보고, 만일 있으면
TServerClientThread.ReActivate(ClientSocket)를 호출한다. 만일 현재 노는게
없으면 FOnGetThread(Self,ClientSocket,Result)로 지정된 event handler를 호출
해서 얻도록하고 안되면, DoCreateThread(ClientSocket)로 직접 생성한다.
DoCreateThread은 TServerClientThread.Create를 호출해 생성하고,
TServerClientThread.Create내에서 ReActivate(ASocket)을 호출한다. 즉 어떻게
한던 TServerClientThread.ReActivate(ClientSocket)은 항상 호출 된다.
ReActivate은 TServerClientWinSocket과 TServerWinSocket를 저장하고
TServerWinSocket.AddThread(Self)로 자신을 등록하고,
TServerClientWinSocket.OnSocketEvent := HandleEvent과
TServerClientWinSocket.OnErrorEvent := HandleError의 event handler를 연결
한다. 마지막으로 Windows.SetEvent(Handle)를 호출한다.
4. TServerClientThread의 Execute 실행
TServerWinSocket.ThreadStart(Self)로 FOnThreadStart(Self,AThread)인 등록된
event handle을 맨 먼제 호출하고, TServerWinSocket.ThreadEnd(Self)로
FOnThreadEnd(Self, AThread)인 등록된 event handler를 마지막으로 호출한다.
그리고 IF StartConnect THEN ClientExecute;
IF EndConnect THEN Break;
를 무한 반복한다.
StartConnect는 WaitForSingleObject(Handle,INFINITE)로 기다리다. terminated를
확인하여 terminate가 않됬으면 true를 돌려준다.
EndConnect는 terminate이고 KeepInCache가 아니면 true를 돌려준다.
ClientExecute는 IF select(0, @FDSet, nil, nil, @TimeVal) > 0로 읽을게 있으면
TServerClientWinSocket.ReceiveBuf(FDSet, -1)를 읽어봐 읽어지면
Synchronize(DoRead)로 TServerWinSocket.ClinetEvent(seRead)를 호출하고
ClinetRead를 거처 FOnClinetRead에 등록된 event handler를 호출한다.
그리고 IF select(0, nil, @FDSet, nil, @TimeVal) > 0로 읽을게 있으면
TServerClientWinSocket.ReceiveBuf(FDSet, -1)를 읽어봐 쓸게있으면
Synchronize(DoWrite)로 TServerWinSocket.ClinetEvent(seWrite)를 호출하고
ClinetWrite를 거처 FOnClinetWrite에 등록된 event handler를 호출한다.
5. Window가 message를 보내오는 경우
TServerClientWinSocket.OnSocketEvent
TServerClientThread.HandleEvent(Sender, Socket, SocketEvent)
Event(SocketEvent)
TServerWinSocket.ClientEvent(Self, ClientSocket, SocketEvent)
ClientConnect(Socket)
OR ClientDisconnect(Socket)
OR ClientRead(Socket)
OR ClientWrite(Socket)
FOnClientConnect(Socket)
OR FOnClientDisconnect(Socket)
OR FOnClientRead(Socket)
OR FOnClientWrite(Socket)에 등록된 event handler를 호출한다.
TServerClientWinSocket.OnErrorEvent
TServerClientThread.HandleError(Sender,Socket,ErrorEvent,ErrorCode)
Error(ErrorEvent, ErrorCode)
TServerWinSocket.ClientError(Sender,Socket,ErrorEvent,ErrorCode)
ClinetErrorEvent(Socket, ErrorEvent, ErrorCode)
FOnClinetError(Socket, ErrorEvent, ErrorCode)
에 등록된 event handler를 호출한다.
6. 참고
indent는 호출되어지는 순서를 나타낸다.
TServerSocket.Create
FServerSocket := TServerWinSocket.Create;
TServerSocket.Active := true;
TServerSocket.SetActive(true);
FActive := true;
DoActivate(true);
TServerWinSocket.Listen(FHost, FAddress, FService, FPort, 5);
TCustomWinSocket.Listen(FHost, FAddress, FService, FPort, 5);
FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
SockAddrIn := InitSocket(FHost, FAddress, FService, FPort, False);
TSockAddrIn.sin_family := PF_INET;
TSockAddrIn.sin_addr := {LookupName(FHost)
OR inet_addr(PChar(FAddress))
OR INADDR_ANY
};
TSockAddrIn.sin_port := {htons(LookupService(FService))
OR htons(FPort)
};
DoSetASyncStyles;
IF AsyncStyles
WSAAsyncSelect(FSocket, CM_SOCKETMESSAGE, Handle,
Longint(Byte(FAsyncStyles)));
ELSE
WSAAsyncSelect(FSocket, 0, 0, Longint(Byte(FAsyncStyles)));
ioctlsocket(FSocket, FIONBIO, 0);
Event(Self, seListen);
FOnListen(Self, Socket);
FServerAcceptThread := TServerAcceptThread.Create(False, Self);
TThread.Create(False);
....
TServerAcceptThread.Execute;
TServerWinSocket.Accept(FServerSocket.SocketHandle);
getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
PChar(@OldOpenType),Len);
IF stThreadBlocking
setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
PChar(@SO_SYNCHRONOUS_NONALERT), Len);
ClientWinSocket := WinSock.accept(Socket, @Addr, @Len);
ClientSocket := GetClientSocket(ClientWinSocket);
{FOnGetSocket(Self, Socket, Result)
OR TServerClientWinSocket.Create(Socket, Self)
};
FOnSocketEvent(Self, ClientSocket, seAccept);
IF stThreadBlocking
ClientSocket.ASyncStyles := [];
GetServerThread(ClientSocket);
{SELECT TServerClientThread
FROM FActiveThreads
WHERE ClientSocket = nil
TServerClientThread.ReActivate(ClientSocket);
TServerClientWinSocket := ASocket;
TServerWinSocket := FClientSocket.ServerWinSocket;
TServerWinSocket.AddThread(Self);
FActiveThreads.Add(Self);
TServerClientWinSocket.OnSocketEvent := HandleEvent;
TServerClientWinSocket.OnErrorEvent := HandleError;
FEvent.SetEvent;
Windows.SetEvent(Handle);
OR FOnGetThread(Self, ClientSocket, Result)
OR DoCreateThread(ClientSocket)
TServerClientThread.Create(False, ClientSocket);
ReActivate(ASocket); <- 상동
};
setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
PChar(@OldOpenType), Len);
...
TServerClientThread.Execute;
TServerWinSocket.ThreadStart(Self);
FOnThreadStart(Self, AThread);
WHILE
IF StartConnect
WaitForSingleObject(Handle, INFINITE)
RETURN NOT terminated
ClientExecute;
FD_ZERO(FDSet);
FD_SET(TServerClientWinSocket.SocketHandle, FDSet);
TimeVal.tv_sec := 0;
TimeVal.tv_usec := 500;
IF select(0, @FDSet, nil, nil, @TimeVal) > 0
IF TServerClientWinSocket.ReceiveBuf(FDSet, -1) = 0
Break
ELSE
Synchronize(DoRead);
TServerWinSocket.ClinetEvent(seRead);
ClinetRead
FOnClinetRead
IF select(0, nil, @FDSet, nil, @TimeVal) > 0
Synchronize(DoWrite);
TServerWinSocket.ClinetEvent(seWrite);
ClinetWrite
FOnClinetWrite
IF EndConnect
RETURN terminated AND NOT KeepInCache
Break;
TServerWinSocket.ThreadEnd(Self);
FOnThreadEnd(Self, AThread);
...
TServerClientWinSocket.OnSocketEvent;
TServerClientThread.HandleEvent(Sender, Socket, SocketEvent);
Event(SocketEvent);
TServerWinSocket.ClientEvent(Self, ClientSocket, SocketEvent);
{ClientConnect(Socket)
OR ClientDisconnect(Socket)
OR ClientRead(Socket)
OR ClientWrite(Socket)
};
{FOnClientConnect(Socket)
OR FOnClientDisconnect(Socket)
OR FOnClientRead(Socket)
OR FOnClientWrite(Socket)
};
...
TServerClientWinSocket.OnErrorEvent;
TServerClientThread.HandleError(Sender,Socket,ErrorEvent,ErrorCode);
Error(ErrorEvent, ErrorCode);
TServerWinSocket.ClientError(Sender,Socket,ErrorEvent,ErrorCode);
ClinetErrorEvent(Socket, ErrorEvent, ErrorCode);
FOnClinetError(Socket, ErrorEvent, ErrorCode);
Posted by
~ 정윤식
Trackback | http://sshoking.tistory.com/trackback/969