'2012/04'에 해당되는 글 2건

  1. 2012.04.12 UDP서버-클라이언트 by 초코송송이
  2. 2012.04.02 [Delphi] String에 관하여.. by 초코송송이

1.UDP 프로토콜개요

TCP와 UDP의 공통점: 전송 계층 프로토콜이라는 점에서 다음과 같은 공통점이 있다.

- 포트번호를 이용해 주소를 지정한다.

- 데이터 오류를 체크한다. : TCP와 UDP는 IP의 패킷 전송 기능을 기반으로 동작한다. 전송 중 여러 원인으로 오류가 발생할 수 있는데, IP는 프로토콜 동작에 필수적인 IP 헤더에 대해서만 오류를 체크하고 데이터는 체크하지 않는다. 반면 TCP와 UDP는 헤더는 물론이고 데이터에 대한 오류도 체크한다.

TCP와 UDP의 차이점 

항목 

TCP 

UDP 

 1)

연결형(connection-oriented) 프로토콜

- 연결설정 후 통신 가능

비연결형(connectionless) 프로토콜

- 연결설정 없이 통신 가능 

 2)

신뢰성 있는 데이터 전송  신뢰성 없는 데이터 전송 
 3) 일대일 통신(unicast) 

일대일 통신(unicast), 일대다 통신(broadcast, multicast) 

 4)

데이터 경계 구분 안함

- 바이트 스트림(byte-stream) 서비스

데이터 경계 구분함

- 데이터그램(datagram) 서비스 

항목을 UDP 위주로 설명하면,

1) 연결 설정을 하지 않았으므로 connect() 함수를 사용하지 않는다. (몇가지 이유로 connect() 함수를 사용하는 경우가 있지만, UDP 프로토콜에는 연결 설정 개념이 없으므로 connect() 함수를 호출하더라도 특별한 패킷 교환이 일어나지 않는다.

2) 프로토콜 수준에서 신뢰성 있는 데이터 전송을 보장하지 않으므로, 필요하다면 응용 프로그램 수준에서 신뢰성 있는 데이터 전송 기능을 구현해야 한다.

3) 간단한 소켓 함수 호출 절차만 따르면 다자 간 통신을 쉽게 구현할 수 있다.

4) TCP와 달리 응용 프로그램이 데이터 경계 구분을 위한 작업을 별도로 할 필요가 없다.

 

※ UDP에 대한 오해

UDP는 신뢰성 없는 데이터 전송을 하므로 데이터 오류를 체크하지 않을 것이라고 생각하면 안된다. UDP는 TCP처럼 체크섬(checksum)을 이용해 데이터 오류를 체크한다. 그렇다면 TCP는 신뢰성 있는 데이터 전송을 하는데 왜 UDP는 그렇지 못 할까? 답은 UDP에서 데이터 재전송과 데이터 순서 유지 작업을 하지 않기 때문이다. UDP는 도착한 데이터에 오류가 있다고 판단하면 이 데이터를 응용 프로그램에 전달하지 않고 그대로 삭제해 버린다. 따라서 응용 프로그램은 데이터에 오류가 있어 버려졌다는 사실을 알지 못한다. 또한 TCP는 데이터 순서 유지를 위해 각 바이트마다 번호를 부여하지만 UDP는 비슷한 기능을 제공하지 않는다.

 

2. UDP 서버-클라이언트 동작 원리

UDP서버는 TCP 서버와 달리 멀티스레드 등의 프로그래밍 기법을 사용하지 않고도 한 소켓으로 여러 클라이언트를 처리할 수있다.

 (a) 서버는 소켓을 생성하고 클라이언트가 데이터를 보내기를 기다린다. 이 때 서버가 사용하는 소켓은 특정 포트 번호와 결합되어 이 포트번호로 도착한 데이터만 수신한다.

(b) 첫 번째 클라이언트는 연결 설정 없이 서버와 데이터를 바로 주고 받는다.

(c) 두 번째 클라이언트도 연결 설정 없이 서버와 데이터를 바로 주고 받는다. (TCP 서버와 달리 UDP서버는 소켓을 한개만 사용한다.)

(d) UDP 서버-클라이언트가 통신하는 일반적인 상황이다. 'UDP 클라이언트 #n' 처럼 한 클라이언트가 소켓을 두개이상 사용해 서버와 통신할 수 도 있다.

 

※ UDP서버-클라이언트모델(1)

 

주의 사항

- 블로킹 소켓을 사용할 경우, 송수신 함수의 호출 순서가 맞지 않으면 교착상태가 발생할 수 있다.

- 클라이언트는 데이터를 받은 후 송신자의 주소(IP주소,포트번호)를 확인해야 한다. recvfrom() 함수는 UDP 서버가 보낸 데이터는 물론, 전혀 다른 UDP 응용 프로그램이 보낸 데이터도 수신할 수 있기 때문이다.

 

※ UDP 서버-클라이언트모델(2)

=> UDP 클라이언트를 위와 같이 작성할 수도 있다. UDP 소켓에 대해 connect() 함수를 호출하면 통신할 상대의 주소 정보가 내부적으로 기억되므로 sendto()/recvfrom() 함수 대신 send()/recv() 함수를 사용해 특정 UDP 서버와 통신할 수있다.

장점

- sendto() 함수를 사용한 경우보다 효율적이다. connect() 함수로 서버주소를 한 번만 설정해 두면, send() 함수가 이 정보를 재사용하기 때문이다.

- 데이터를 받은 후 송신자의 주소(IP, 포트)를 확인하지 않아도 된다. recvfrom() 함수와 달리 recv() 함수는 connect() 함수로 설정한 대상을 제외한 다른 UDP 응용 프로그램이 보낸 데이터는 수신하지 않기 때문인다.

주의사항

- 블로킹 소켓을 사용할 경우, 송수신 함수의 호출 순서가 맞지 않으면 교착 상태가 발생할 수 있다.

 

※ UDP 데이터 전송 함수

1. sendto() 함수

1) 함수 원형

int sendto(

 SOCKET s, //통신에 사용할 소켓

 const char *buf, //보낼 데이터를 담고 있는 버퍼의 주소

 int len, // 보낼 데이터의 크기

 int flags, // sendto()함수의 옵션. 대부분 0을 사용. MSG_DONTROUTE(윈속에서는 사용하더라도 무시), MSG_OOB(UDP에서는 의미가 없음)가 있음

 const struct sockaddr *to, // 목적지 주소를 담고 있는 소켓 주소 구조체(UDP의 경우, 특정 호스트나 라우터 주소, 브로드캐스트나 멀티캐스트 주소를 사용할 수 있음)

 int tolen // 목적지 주소를 담고 있는 소켓 주소 구조체의 크기

);

2) sendto() 함수의 특징

sendto() 함수는 UDP 소켓은 물론, TCP 소켓에도 사용가능 하다. TCP 일때는 to와 tolen인자는 무시된다. 그리고 TCP 소켓에 사용할 때만, flags 인자에 MSG_OOB를 사용할 수 있다.

sendto() 함수로 보낸 데이터는 독립적인 UDP 데이터그램(패킷)으로 만들어져 전송되며, 수신측에서는 recvfrom() 함수 호출 한번으로 이 데이터를 읽을 수 있다. 따라서 UDP를 사용할 경우에는 TCP와 달리 응용 프로그램 수준에서 메시지 경계를 구분하는 작업을 할 필요가 없다.

UDP 소켓에 대해 sendto()함수를 호출할 경우, 한번에 보낼수 있는 데이터의 크기에 제한이 있다. 최소 0~최대 65507(65535-20(IP헤더크기)-8(UDP헤더크기)) 바이트이다. 실제로는 최대값보다 훨씬 작은 크기를 사용하는것이 바람직하다. 특히 UDP를 이용해 브로드캐스트 패킷을 보낼 경우, 512바이트보다 작은 크기를 사용할 것을 권고한다.(비주얼스트디오 설명서)

sendto()함수로 보낸 응용 프로그램 데이터는 커널(운영체제)영역에 복사되어 전송된 후 곧바로 버려진다. sendto() 함수가 리턴됐다고 실제 데이터 전송이 완료된 것은 아니며, 데이터 전송이 끝났어도 상대방이 받았는지 확인할 수 없다.

블로킹 소켓을 사용할 경우, 커널 영역에 데이터를 복사할 공간이 부족하면, sendto()함수는 호출 시 블록된다.

2. recvfrom() 함수

1) 함수 원형

int recvfrom(

 SOCKET s, // 통신에 사용할 소켓. sendto()함수에 사용하는 소켓과 달리, 이 소켓은 반드시 지역주소(IP, 포트)가 미리 결정되어 있어야 한다.

 char *buf, // 받은 데이터를 저장할 버퍼의 주소

 int len, // 버퍼의 크기. 도착한 UDP 패킷 데이터가 len보다 크면 len 만큼만 복사하고 나머지는 버린다. 이때 SOCKET_ERROR를 리턴한다.

 int flags, // 대부분 0을 사용. 사용가능한 값으로 MSG_PEEK, MSG_OOB(UDP에서는 의미가 없음)가 있다. recvfrom() 함수의 기본 동작은 수신버퍼의 데이터를 응용프로그램 버퍼에 복사한 후 해당 데이터를 수신버퍼에서 삭제하는 것인데, MSG_PEEK 옵션을 사용하면 수신 버퍼에 데이터가 계속 남는다.

 struct sockaddr *from, // 소켓 주소 구조체를 전달하면 송신자의 주소 정보로 채워진다.

 int *fromlen // 함수가 채워넣은 주소정보 구조체의 크기를 갖게된다.

);

2) recvfrom() 함수의 특징

recvfrom() 함수는 UDP 소켓은 물론이고, TCP 소켓에도 사용할 수 있으며, 이 경우, from과 fromlen 인자는 무시된다. TCP 소켓에 사용할 때만 flags 인자에 MSG_OOB를 사용할 수 있다.

sendto() 함수로 보낸 데이터는 독립적인 UDP 데이터그램(패킷)으로 만들어져 전송되며, 송신측에서는 recvfrom() 함수 호출 한번으로 이 데이터를 읽을 수 있다. 따라서 응용프로그램 수준에서 메시지 경계를 구분하지 않아도 된다.

UDP 소켓에 대해 recvfrom() 함수를 호출할 경우 리턴 값이 0이 될 수 있는데, 이는 상대방이 sendto() 함수 호출시 데이터 크기를 최소값 0으로 설정했다는 뜻이다. UDP 프로토콜에는 연결 설정과 종료 개념이 없으므로 recvfrom() 함수의 리턴 값이 0이라고 해서 특별한 의미가 있는 것은 아니다. 반면, TCP 소켓에서 recvfrom() 함수를 호출할 경우 리턴 값이 0이면, 정상종료를 의미한다.

블로킹 소켓을 사용할 경우, 소켓 수신 버퍼에 도착한 데이터가 없으면 recvfrom()함수는 호출 시 블록된다.

 

3. 브로드캐스팅

 

- 유니캐스팅(unicasting: one-to-one): 한 개체가 다른 한 개체에 데이터를 보내는 모델. IPv4, IPv6에서 지원

- 브로드캐스팅(broadcasting: one-to-many): 한 개체가 특정 네트워크(자신과 동일 네트워크도 가능)에 속한 모든 개체에 데이터를 보내는 모델. IPv4에서만 지원

- 멀티캐스팅(multicasting: ont-to-many): 동일 그룹에 가입한 모든 개체(물리적으로는 서로 다른 네트워크에 속할 수 있음)에 데이터를 보내는 모델. 개념적으로 브로드캐스팅은 멀티캐스팅의 특수한 경우로 볼수 있다. IPv4, IPv6에서 지원)

- 애니캐스팅(anycasting: one-to-one-of-many): 한 개체가 동일 그룹에 가입한 개체 중 가장 가까운 하나에만 데이터를 보내면, 데이터를 받은 개체가 그룹에 속한 나머지 개체에 데이터를 보내는 모델. IPv6에서만 지원)

 

※ 소켓응용 프로그램에서 브로드캐스트 데이터를 보내는 방법

① 브로드캐스팅을 활성화 한다.

BOOL bEnable = TRUE;

retval = setsocketopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&bEnable, sizeof(bEnable));

=> socket() 함수로 생성한 소켓은 기본적으로 유니캐스팅만 지원하지만, setsocketopt() 함수의 첫번째 인자로 해당 소켓, 두번째 SOL_SOCKET과 세번째 SO_BROADCAST 옵션, 네번째 TRUE 값을 사용하면 브로드캐스팅이 활성화된다.

② 브로드캐스트 주소를 목적지로 해서 데이터를 보낸다.

SOCKETADDR_IN remoteaddr;

ZeroMemory(&remoteaddr, sizeof(remoteaddr));

remoteaddr.sin_family = AF_INET;

remoteaddr.sin_addr.s_addr = inet_addr("255.255.255.255");

remoteaddr.sin_port = htons(9000);

char buf[BUFSIZE]; //송신용 버퍼를 선언하고 테이터를 넣는다.

...

retval = sendto(sock, buf, strlen(buf), 0, (SOCKET *) &remoteaddr, sizeof(remoteaddr));

if(retval == SOCKET_ERROR) 오류처리;

printf("%d바이트를 보냈습니다.\n", retval);

=>브로드캐스트 데이터를 보내는 코드는 IP 주소 부분을 제외하면 유니캐스트 데이터를 보내는것과 동일

 

※ 브로드캐스트 주소의 종류

IPv4 주소는 크게 네트워크 ID와 호스트 ID로 나눌수 있다. 서브넷을 사용하는 경우, 호스트 ID의 일부는 서브넷 ID로 사용된다. 브로드캐스트용으로 예약되어 있는 IPv4 주소는 아래와 같다. 

 

- 네트워크 브로드캐스트(net-directed broadcast)

호스트 ID 비트가 모두 1인 경우로 특정 네트워크에 대한 브로드캐스트를 의미한다. 브로드캐스트 데이터가 라우터를 거쳐야 하므로 라우터 설정에 따라 브로드캐스팅이 불가능할 수도 있다. 따라서 실용적으로 브로드캐스팅 주소로 사용하기는 어렵다.

 

- 서브넷 브로드캐스트(subnet-directed broadcast)

서브넷 ID를 제외한 호스트 ID비트가 모두 1인 경우로, 특정 서브넷에 대한 브로드캐스트를 의미한다. 서브넷 브로드캐스트도 라우터를 통과하지 못할 수 있으므로, 일반적으로 외부 서브넷에 대한 브로드캐스팅을 목적으로 사용하기는 어렵다. 하지만 송신자 자신이 속한 서브넷에 대한 브로드캐스팅은 항상 가능하다.

 

- 지역 브로드캐스트(local broadcast 또는 limited broadcast)

송신자 자신이 속한 네트워크에 대한 브로드캐스트를 의미한다. 항상 브로드캐스팅이 가능하며, 브로드캐스트 데이터가 라우터 경계를 넘어가지 않는다. 지역 브로드캐스트 주소는  ws2def.h파일에 INADDR_BROADCAST(0xffffffff; 255.255.255.255) 값으로 정의되어 있다.

 

"TCP/IP 윈도우 소켓 프로그래밍"책 에서 발췌

Posted by 초코송송이
l

Delphi String에 관하여..

 

Delphi 2007이하의 String Types 기준
 
3가지 타입이 있음.
  • Short strings.
  • Long strings.
  • Wide (Unicode) strings. 
 Type Maximum Length  Memory required  Used for 
 ShortString  255 characters  2 to 256 bytes  이전 버전과의 호환을 위해 존재함.
 AnsiString  ~ 2^31 characters   4 bytes to 2GB  8-bit (ANSI) characters, DBCS ANSI, MBCS ANSI, etc.
 WideString  ~ 2^30 characters  4 bytes to 2GB   유니코드. 다중사용자 서버나 다중언어 지원용 어플리케이션에서 사용.
(AnsiString: 때때로 long string 이라 불리며, 많은 목적으로 대부분 이걸 사용함.
WideString: .Net 플랫폼에서 주로 사용함.)  

Short String

델파이의 조상격인 Turbo-Pascal에서는 단지 한종류의 String 타입밖에 없었다. 바로 ShortString 타입이다. 터보파스칼 시절 ShortString은 디폴트 String 타입이었고, 우리가 단순하게 String으로 선언을 하면 기본적으로 ShortString이 선언이 되었다. ShortString은 기본적으로 힙에 생성이 되지 않고, 스택에 생성이 된다. 메모리 할당 관점에서 보면 ShortString은 다른 Integer, Boolean, Record, Enum타입등과 달리 정적할당 타입이다. 정적할당이다 보니 ShortString은 당연히 런타임시 사이즈가 변할수 없다. 그래서 ConCat(문자열 붙이는 함수), Insert(문자열에 추가하는 함수)등의 함수를 쓸 때 문제를 야기시켰다. 이 문제를 회피하기 위해, 터보파스칼(델파이)은 모든 String 인스턴스에 256바이트의 최대 사이즈를 미리 할당하였다. ShortString의 첫번째 요소는 String의 실제 길이를 나타내므로 ShortString은 최대 255 글자를 가질수 있다.

var S: ShortString;

 실제로 위와 같이 선언하면 스택에는 256 바이트가 사용된다. ShortString 변수가 지역변수이거나, 전역변수이거나 레코드나 클래스에 사용된다 해도 똑같이 256바이트가 사용된다. 

 ShortString의 메모리 구조는 아래와 같다. n은 문자가 문자열에 저장된 수이다.

 

원문: http://blog.naver.com/g2000240023?Redirect=Log&logNo=50021477287

http://www.codexterity.com/delphistrings.htm

 

Long Strings (AnsiString)

long string 이라고도 부르는 AnsiString은 동적으로 할당되는 문자열이다. 최대길이는 메모리가 허락하는 한도까지이다.
 
long string 변수는 4 바이트의 메모리 공간을 차지하는 포인터다.
문자열이 비어있을 때 (=길이가 0 일 때), 그 포인터는 nil 을 가리키며, 추가적으로 사용하는 공간은 없다. 문자열이 비어 있지 않을 때, 그 포인터는 문자열이 들어있는 동적할당된 메모리의 블럭을 가리킨다. 문자열 위치 이전의 8 바이트는 32-bit 길이의 지시자와 32-bit 길이의 레퍼런스 카운트로 이루어져 있다.
이 메모리는 힙 영역에 할당되지만, 이 메모리는 프로그래머가 관리하지 않고 전부 자동적으로 관리된다.
 
왜냐하면 long-string 변수들은 포인터이며, 둘 이상의 이 변수들은 추가적인 메모리의 소비없이 같은 값을 가리킬 수도 있기 때문이다.
컴파일러는 이 메모리를 이용해 자원을 절약하고, 실행속도를 향상시킨다.
long-string 변수가 해제되거나 생성될 때, 레퍼런스 카운트는 감소되거나 증가한다. string 을 가리키고 있던 레퍼런스 카운트가 0 이 되면 그 string 의 메모리는 해제된다.
이 프로세스를 레퍼런스 카운팅이라고 부른다.
인덱싱 과정에서 string 의 문자 하나를 바꿀 경우, 만약 그 레퍼런스 카운트가 1 보다 크다면 그 string 의 복사본이 생성된다.
이것을 가리켜 copy-on-write 라 한다.

원문: http://bloodguy.tistory.com/96 

아래는 델파당에서 검색한 내용을 옮겨보았습니다.

VCL의 String은 reference가 count됩니다. String은 변수는 그냥 포인터 4Byte입니다.
그런데 4Byte에 적힌 address위치에 가 보면 실제data가 있죠.

procedure TForm1.Button1Click(Sender: TObject);
var
    s: AnsiString;
begin

  s:='abcd';

  ShowMessage(IntToStr(sizeof(s)));      //'4'
  ShowMessage(IntToStr(sizeof(AnsiString))); //'4'
end;

위 코드에서 s의 size나  AnsiString의 크기는 4입니다.
즉 String변수는 포인터입니다.
실제 Data가 기록된 위치의 메모리 주소(Address)가 적힌 것입니다.

[AnsiStirng의 실제 기록 구조]

AllocSize =   ( StrRec+ 실제 Data ) 까지의 크기가 기록되어있습니다.
RefCount =  말그대로 reference-Count 로 AnsiString변수가 참조하고 있는 숫자입니다.
Length     =  실제Data의 길이입니다.

String의 길이를 구할때..
Delphi(VCL)의 AnsiString이   Lenth함수가  c에서 strlen보다 속도가 빠른데..
AnsiStirng의 Length를 구하면 위에 StrRec구조체에서  Length값을 그냥 return하지만
c에서 strlen은   $0 즉  NULL문자가 나타날때까지 처음부터 계속 search합니다.
그래서 AnsiString의 Length가 c의 strlen보다 속도를 비교할수 없을 정도로 빠르죠
참조  http://cafe.naver.com/bcbmaster/850

var
  s1,12: Stirng;
begin                                             // 1)

  s1:='abcd';                                  // 2)
  s2:=s1;                                       // 3)

  s2[1]='k';                                   // 4) 

end;                                             // 5)
  
위 소스에서 1) ~ 4) 과정을 설명해드리면...

1) AnsiString을 선언하면 스택에 4Byte 씩 2개 변수가 할당됩니다.
    문론 내용은 nil 아무것도 없습니다.

2) heap(힙)에   strrec(12Byte)포함하여 실제 data 4Byte까지 총 16Byte가 할당됩니다.

      AllocSize = 16
      RefCnt    =  1
      Length     = 4
      data = 'abcd'

      위와같이 메모리에 기록되고 stack에 s1위치에는 Data위치의 address가 기록됩니다. 

3)  s2:=s1 하면
      RefCnt = 2 로 바뀌고
      S2변수가 있는 stack위치에 Data가 s1과 똑같은 값 Data위치의 addr이 기록됩니다.

4)  s2의 값의 일부를 변경하면
       *  s1의 가리키던   StrRec의 RefCnt =1 로 바뀝니다.
       *  그리고 다시 또다른  힙에 14+8=22Byte의 메모리가 확보되고 다음과 같이 내용이 기록됩니다.
      AllocSize = 22
      RefCnt    =  1
      Length     = 4
      data = 'kbcd'            //실데이타  5Byte인데..  4의 배수로  할당되어서 8Byte가 할당됨
       * 그리고 s2의 stack위치값은  새로할당된 data의 힙address가 기록됩니다.

    음.. 그리고 AllocSize가 왜 12+data_length가 아니라 14+data_length인지는 저도 정확한 이유를 잘 모르겠네요
   
...
5) 함수를 빠져나가면 stack에   할당된 s1 , s2 가 삭제되겠죠

    string변수가 삭제 될때는 RefCnt가 하나씩 줄어듭니다.
    만약 RefCnt가 0이 될때면 메모리에 할당된 StrRec부터 Data까지 모두 free됩니다.


만약 전역변수에 문자열을 할당하면 전역변수의 내용을 바꾸지 않은한 RefCnt는 프로그램이 종료할때까지 항상  1보다 크겠죠

원문: http://www.delmadang.com/community/bbs_view.asp?bbsNo=17&bbsCat=41&indx=417354

 

WideString

WideString 타입은 동적으로 할당되는 16-bit 유니코드 문자로 이루어진 문자열을 의미하며, Win32 에서, WideString 은 COM BSTR 타입과 호환된다.
 Note: Win32 에서, WideString 은 레퍼런스 카운트를 이용하지 않는다.

 Win32 플랫폼은 싱글바이트, 멀티바이트뿐만 아니라 유니코드를 지원한다.

SBCS(single-byte character set) 에서 각 바이트는 문자열의 한 character이며,                     MBCS(multibyte character set)에서는 일부 character 는 1바이트 또는 그 이상의 바이트를 통해 표현된다.
MBCS에서 가장 첫번째 바이트는 lead byte 라고 불리며,
일반적으로, MBCS에서 하위 128 characters 들은 7-bit ASCII characters 이며, 127 이상의 character 를 결정하는 건 MBCS의 lead byte 이다.
null 값은(#0) 언제나 싱글바이트 문자이다.
 
유니코드 character set 에서 각 문자는 2바이트를 의미하며 유니코드 문자열은 1바이트의 나열이 아니라 2바이트의 나열이다.
Unicode characters 와 strings 는 또한 wide characters 와 wide character strings 로 불린다.
Unicode characters map 의 처음 245은 ANSI character set 이다.
윈도우즈 OS는 유니코드를 지원한다. (UCS-2) 
 
델파이는 Char, PChar, AnsiChar, PAnsiChar, AnsiString 타입을 통해 싱글바이트와 멀티바이트 문자(열)을 지원한다.
(멀티바이트 함수들은 보통 Ansi- 로 시작하는 이름을 가진다. 예를 들어, StrPos 의 멀티바이트 버전 함수는 AnsiStrPos 임)
멀티바이트 문자의 지원은 로케일 별 OS 에 의존적이며,  델파이는 WideChar, PWideChar, WideString 을 통해 유니코드를 지원한다.

원문: http://bloodguy.tistory.com/96 

 

Delphi 2009 이상

 상당히 오랫동안 Delphi에는 문자를 나타내기 위해 다음의 두 가지 개별적인 문자 타입들을 가지고 있었습니다.
AnsiChar: 8비트 표현을 사용 (256개의 기호들을 표시), 코드 페이지에 따라 다르게 해석됨
WideChar: 16비트 표현을 사용 (64K의 기호 표시)
 이 측면에서 Delphi 2009에 변화된 점은 없습니다. 달라진 점은, AnsiChar의 별칭이었던 Char 타입이 이제 WideChar의 별칭이 되었다는 것입니다. 컴파일러가 코드에서 Char를 발견할 때마다 WideChar로 해석합니다. 이런 새 컴파일러 디폴트를 변경할 수 있는 방법은 없습니다.이는 많은 소스 코드에 영향을 미치고 많은 결과를 양산하는 상당히 큰 변화입니다. 예를 들어 PChar 포인터는 이제 이전의 PAnsiChar가 아닌 PWideChar의 별칭입니다.

문자열과 UNICODESTRING

 Char 타입 정의에서의 변경 사항은 문자열 타입 정의에서의 변경 사항과 밀접한 관련이 있기 때문에 매우 중요합니다. 문자와는 다르게 문자열은 이전에는 없었던 새로운 데이터 타입(UnicodeString이라고 함)에 매핑됩니다. 내부 구조도 AnsiString 타입의 표현과는 상당히 다릅니다.
WideChar 타입에 기반한 문자열을 나타내는 WideString 타입이 언어에 이미 있는데, 왜 힘들게 새 데이터 타입을 정의해야 할까요? WideString은 참조가 카운트되지 않으며 성능과 유연성의 측면에서 매우 취약합니다. 예를 들어 WideString은 네이티브 FastMM4가 아닌 Windows 글로벌 메모리 할당자(allocator)를 사용합니다.
AnsiString과 마찬가지로 UnicodeString은 참조가 카운트되며 copy-on-write 의미를 사용하고 성능이 우수합니다. AnsiString과 달리 UnicodeString은 문자별로 2바이트를 사용하며 UTF-16을 기반으로 합니다. 사실상 UTF-16은 변수 길이 인코딩이며, 때때로 UnicodeString은 두 개의 WideChar 서로게이트 요소(즉, 4바이트)를 사용하여 단일 유니코드 코드 포인트를 나타냅니다.
문자열 타입은 이제 Char 타입에서와 마찬가지로 하드 코딩된 방식으로 UnicodeString에 매핑됩니다. 이를 변경할 수 있는 컴파일러 지시어나 다른 방법은 없습니다. 계속해서 AnsiString 문자열 타입을 사용해야 하는 코드가 있으면 해당 코드를 AnsiString 타입의 명확한 선언으로 교체해야 합니다.

문자열의 내부 구조

새 UnicodeString 타입과 관련된 주요 변경 사항 중 하나는 내부 구조입니다. 새로운 구조는 참조 카운트되는 문자열 타입 모두(UnicodeString 및 AnsiString)에 해당되며, 참조 카운트되지 않는 문자열 타입들(ShortString 및 WideString)에는 해당되지 않습니다.
기존의 AnsiString 타입의 구조는 다음과 같았습니다.

-8 -4  문자열 참조 주소 
 참조 카운트  길이 문자열의 첫 번째 문자 

첫 번째 요소(문자열 자체의 시작 부분부터 역으로 카운트)는 Pascal 문자열 길이이며 두 번째 요소는 참조 카운트입니다.

Delphi 2009에서는, 참조 카운트된 문자열의 구조는 다음과 같습니다.

-12

-10

 -8

-4

문자열 참조 주소

코드 페이지

요소 크기

참조 카운트

길이

문자열의 첫 번째 문자


길이와 참조 카운트 외에, 새로운 필드는 요소 크기와 코드 페이지를 나타냅니다. 요소 크기는 AnsiString과 UnicodeString 사이에 식별하는 데 사용되며, 코드 페이지의 경우는 UnicodeString 타입이 고정 코드 페이지 1200을 가지므로 AnsiString 타입에서만 의미를 갖습니다.

해당 지원 데이터 구조는 다음과 같은 System 유닛의 임플먼테이션 섹션에서 선언되어 있습니다.
type
    PStrRec = ^StrRec;
    StrRec = packed record
        codePage: Word;
        elemSize: Word;
        refCnt: Longint;
        length: Longint;
    end;
UnicodeString에 의해 리턴되는 코드 페이지는 1200이며, 이것은 글로벌 변수인 DefaultUnicodeCodePage에 저장되어 있는 숫자 값을 리턴한 것입니다. 위의 코드와 결과에서 Length는 문자 수를 리턴하므로 문자열 길이를 바이트 단위로 확인할 수 있는 직접적인 호출은 없습니다.
물론 다음 표현식을 사용하여, 길이 값에 각 문자의 바이트 크기를 곱하여 문자열 길이의 바이트 값을 알 수 있습니다.
Length(str1) * StringElementSize(str1)

(자세한 사항은 문서 참조)

참조문서: http://www.google.co.kr/url?sa=t&rct=j&q=delphi%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%EA%B5%AC%EC%A1%B0&source=web&cd=1&ved=0CC8QFjAA&url=http%3A%2F%2Fwww.devgear.co.kr%2Fpdf%2Fmarco_cantu_delphi_and_unicode_kor.pdf&ei=Bc17T-6_I8ORiQfGnaCiCQ&usg=AFQjCNE0oq_js4cy2BaiEJ0aVFNAe4BUPg&cad=rjt 

 

Posted by 초코송송이
l