티스토리 툴바

블로그 이미지
~ 정윤식

카테고리

~ (488)
Tip (41)
Study (106)
자료 (34)
TCP/IP (9)
ATmega128 강의 (0)
AVR 강의 (0)
Project (0)
CAM (12)
Char LCD (4)
ADC 컨버터 (3)
어셈블리 (9)
Zigbee (12)
API (4)
Carrot (9)
ATmega (13)
ARM (3)
RFID (10)
초음파센서 (2)
AX-12 일기 (4)
관절로봇 (3)
FINAL (7)
FINAL KEEP (1)
C++ (8)
MFC (4)
Java (2)
문서 (0)
etc (8)
TFT LCD (0)
Program (3)
Image (1)
Delphi 자료 (5)
Delphi 레퍼런스 (131)
Delphi 컴포넌트 (0)
Delphi 알고리즘 (3)
Delphi Database (3)
K Base Program (0)
Jung (0)
Lee (0)
Web Site (1)
CAN (6)
Hardware (5)
SQL (3)
DB (7)
Component 관련 (8)
Error (3)
MS Outlook (0)
iTools (0)
VB (1)
Total61,332
Today14
Yesterday115

달력

« » 2012.05
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

공지사항

태그목록

'Delphi 레퍼런스'에 해당되는 글 131건

  1. 2012/03/16 델파이 ICON(아이콘) 변경 비활성화
  2. 2011/09/23 Transparent
  3. 2011/09/08 파일 복사
  4. 2011/08/18 FillChar 관련
  5. 2011/07/04 두점 각도 계산 (1)
  6. 2011/03/11 포인터 배열을 인자로
  7. 2011/03/04 just-in-time debugger Error
  8. 2011/01/27 TServerSocket
  9. 2011/01/27 TClientSocket
  10. 2010/11/30 combobox의 item을 한번에 입력하는 방법
{$R *.res}

프로젝트 파일에 추가 
Posted by ~ 정윤식

Transparent

Delphi 레퍼런스 / 2011/09/23 17:19
Image의 속성에 보면 Transparent 속성이 있습니다.
이는 디폴트로 False인데, True인경우 이미지가 투명이 됩니다.
단 배경색으로 사용되는 단 하나의 색상을 정해야 하는데, 이는 이미지의 좌측하단 점의 색상으로 결정됩니다.
또한, 그림은 반드시 Bitmap으로 작성된 것이라야 합니다.
(jpge는 안됩니다. ^^)

그림판으로 흰색바탕에 빨간 동그라미와 파란 동그라미 두개를 그려서 테스트 해 보시면 될겁니다.

정지성 님이 올리신 글-----------------------
> 답변 정말 감사드립니다.
> 근데, 제가 알아보니깐 Transparent인가를 사용하는 방법이 있다고 하더라구요.
> Transparent를 이용해서 어떻게 해야할지... ㅠ.ㅠ

Posted by ~ 정윤식

파일 복사

Delphi 레퍼런스 / 2011/09/08 11:55

procedure ddd.click(sender...)
var f : file;
begin
    AssignFile(f,`c:\대상화일`);
    CopyFile(f,`c:\aaa\복사화일`);
Posted by ~ 정윤식
안녕하세요..
 
배열을 FillChar 를 이용하여 특정 문자로 꽉!!  채운 후..
 
다시 특정 위치에 값을 넣고자 하는데 Access violation error가 발생합니다..
 
var
    ac: array of byte;
begin
    // 배열 크기를 5로 정하고..
    SetLength(ac, 5);

    // 0으로 배열을 채운 뒤..
    FillChar(ac, sizeOf(ac), 0);

    // 배열의 [1] 번에 10을 넣고자 합니다..
    ac[1] := 10;

   // 여기 바로 윗 줄에서 오류가 납니다..
end;
 
 
 
무엇이 잘못된 것인지 모르겠습니다..  FillChar를 사용하지 않으면 정상적으로 되는데 FillChar를 진행하고 나면 오류가 나옵니다..
 
어떻게 진행해야 하는지요..?





와이군 (yypbd)   FillChar(ac, sizeOf(ac), 0);  를 
    FillChar(ac[0], Length(ac), 0);
으로 바꿔보세요.

위에 작성하신 코드의 오류는 ac배열의 포인터를 클리어 시킨겁니다.
그리고 array의 사이즈를 구하실때는 sizeof(ac) 로 하시면 4가 나옵니다. length(ac)로 하세요~   2008-01-14 오전 10:37:12
창훈 (changhoon)   아, 그렇군요..  감사합니다..  :)   2008-01-14 오전 10:53:37
가위손 (yhseok)   덧붙여서...

가변배열이 아닌 경우엔 sizeof를 사용하셔도 됩니다.
또 배열의 type에 따라 달라지기도 합니다.

ac: array[1..5] of word;  의 경우라면
length(ac) => 5가 되고
sizeof(ac) => 10이 됩니다. 초기화를 위해선

FillChar(ac, sizeof(ac), 0)
FillChar(ac, length(ac) * sizeof(word), 0)
를 사용하시면 됩니다. ac대신에 ac[1]을 사용하셔도 되구요

가변배열의 경우이고 데이터타입이 word일 때는
ac: array of word
FillChar(ac[0], Length(ac) * sizeof(word), 0);  처럼
데이터형 크기에 대해서도 고려해주셔야 합니다.
(FillChar가 인자값이 byte단위길이 이기 때문에)

   2008-01-14 오전 11:20:39
와이군 (yypbd)   설명을 너무 대충 썼나 싶었는데
가위손님이 잘 정리해주셨네요 ^^   2008-01-14 오후 1:28:42
창훈 (changhoon)   아닙니다..  답글 올려주신 두 분께 모두 감사말씀 드립니다..   




Posted by ~ 정윤식
function Angle(Pt1, Pt2: TPoint): Double; // 각도 계산
begin
Result := ArcTan((Pt1.Y - Pt2.Y) / (Pt2.X - Pt1.X));
end;
Posted by ~ 정윤식

구조체로 넘기는 방법
type
  taaa = array of ????;
var
  aaa : taaa;
test(var bbb : taaa) ....

포인터로 넘기는 방법 모두 될꺼에요
test(a : pointer);
....
test(@buf[0]);  
Posted by ~ 정윤식

> 기존에 델파이5를 사용하고 있었습니다.
> 그런데 델파이7 설치하고 잊어버리고 있다가 델파이5를 실행하니까 information 메세지박스가 뜨면서 just-in-time debugger 가 현재 델파이7로 설정되어 있는데 델파이5로 바꿀건지 묻는겁니다. 만약에 예를 선택하면 델파이7을 사용할때 문제가 있는건 아닌지 또는 바꾸지 않아도 되는건지 궁금합니다.
> 동시에 설치해서 사용하는분은 알려주세요.

어짜피 델파이로 개발시에는 IDE 에서 실행시키고 디버깅을 하기때문에 사용안해도 관계없습니다.

just-in-time debugger <- 이건 델파이에서 실행시키면서 디버깅하는것이 아닌
그냥 프로그램을 실행하다가 오류가 발생했을때 그 프로그램을 수정할때 사용하는것입니다.
Posted by ~ 정윤식

TServerSocket

Delphi 레퍼런스 / 2011/01/27 16:10
#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 ~ 정윤식

TClientSocket

Delphi 레퍼런스 / 2011/01/27 16:07
#1
주제는 ClientSocket에서 한큐에 접속부터 데이타 전송까지 입니다.

보통 질문 답난에 많이 보시면. OnConnect 이벤트에서 데이타를 날리라고 많이 합니다.

하지만 그렇게 하지 않고도 가능합니다.

아래는 간단한 소스입니다.

procedure TForm1.Bitbtn1Click(Sender:TObject);
begin
ClientSocket1.Open;
// 여기부터 아래 두줄이 중요.....
while (not ClientSocket1.Socket.Connected) do
Application.ProcessMessages;

if not ClientSocket1.Active then
begin
ShowMessage('접속 에러');
exit;
end;

// 여기서 데이타를 날려준다.
ClientSocket1.Socket.SendText('날라라~~~~~~~~~');
ClientSocket1.Close;
end;

위에 있는 ClientSocket1.Socket.Connected를 검사하시면 접속 상태를 알수 있습니다.
접속 후에 SendBuf나 SendText를 날려주시면 바로 데이타를 날릴수 있습니다.


#2
1탄 접속하고 데이타 날리기를 한번에 날리고 나고... 일찍 올릴려고 했는데
이런저런일때문에 못올렸습니다..
이번에는 기존방식과 다른방식으로 접속, 데이타 날리기, 그리고 받기까지 한번에 끝내는 방법입니다.

직접 소스 넣기때문에 오류가 있을지도.. ^^


function SendRecvData(var Data : String):Integer;
var
CS : TClientSocket;
CSWSS : TWinSocketStream;
Buff : PChar;
begin
Result := 0;
CS := TClientSocket.Create(nil);
try
CS.Address := SInfo.Address;
CS.Port := StrToInt(SInfo.Port);
CS.ClientType := ctBlocking;
try
// Blocking방식에서는 Connect하면은 접속 되고 난후에 넘어간다. NonBlocking하고 틀리다.
CS.Active := True;
except
// 접속에러
Result := -1;
Exit;
end;
// CSWSS를 통해서 데이타를 보내고 받을수 있다.
// 직접 SendText나 ReciveText(맞나?)를 통해서도 된다.
CSWSS := TWinSocketStream.Create(CS.Socket, 30000);
CSWSS.WriteBuffer(Pchar(Data)^, Length(Data));
// 30초동안 반응이 오는지 기달린다.
if not CSWSS.WaitForData(30000) then
begin
CS.Close;
CSWSS.Destroy;
// TimeOut 에러
Result := -2;
Exit;
end;

// 데이타를 읽어온다.
// 가져오는 길이는 우선 ReceiveLength를 읽었지만. 정확히 사용할려면
// 서버에서 길이를 받아서 사용하기를 바란다.
Buff := AllocMem(CS.Socket.ReceiveLength + 1);
FillChar(Buff^, CS.Socket.ReceiveLength + 1, 0);
CSWSS.ReadBuffer(Buff^, CS.Socket.ReceiveLength);

Data := Buff;
CSWSS.Destroy;
finally
CS.Destroy;
end;
end;

직접 넣고 있어서 일부 오류가 있을지 모르겠습니다....
전문 모양은 기존 강좌팁이 많으니 그것을 참조하시고. 이런방식으로 함수형식으로 만들수 있다는것을 보이기 위해서 적었습니다.



#3
Delphi에서 제공하는
ClientSocket, ServerSocket 루틴을 이용하여
프로그램을 작성시
유위하여야 할 것이 있습니다.

대부분 Error가 발생하면

ErrorCode := 0;
라는 처리만 해주는데
여기에 덧붙여 해 주어야 할 것이 있습니다.

바로

Socket.Close입니다.

Connect요청을 한 다음
10061(ServerSocket 이 Open하지 않았을 경우, 혹은 Port No가 다를 경우)
인 경우에는 더더욱 Socket.Close를 해 주어야 합니다.
일반적으로 Connect를 요청하고 안 될 경우 Timer를 이용하여 계속 시도하는데
Close를 하지 않으며 ㄴ 그 객체가 메모리에 계속 남게 되어서
결국에는 프로그램에 이상이 생깁니다.
Pentium 3, 128M 메모리에서 테스트해 봤는데
230번정도 Connect를 요청하면
프로그램이 다운됩니다.
그러므로 Onerror 발생시
Socket.Close를 반드시 불러 주시기 바랍니다.

정확한 자료는 아니나 저희 경헙상에서 얘기하는 것이므로
정확한 이상 발생 이유를 아시는 분은
답변을 해 주시기 바랍니다. 그럼...


Posted by ~ 정윤식

  ComboBox1.Items.Delimiter:=#13;
  ComboBox1.Items.DelimitedText:=`a`+#13+`b`+#13+`c`;
Posted by ~ 정윤식

최근에 달린 댓글

최근에 받은 트랙백

글 보관함