본문 바로가기
Delphi Tip/통신

소켓 프로그래밍 기법의 활용 3편

by MonoSoft 2021. 12. 21.
728x90
반응형

소켓 프로그래밍 기법의 활용 3편

 

블로킹 연결 (Blocking connections)

 

클라이언트 소켓에서는 ClientType 프로퍼티를 ctBlocking으로

설정하면 블로킹 연결이 생성된다. 

클라이언트 어플리케이션에 따라서는 읽고 쓰는 데에 

새로운 쓰레드를 생성하기를 원할 수도 있는데, 

이렇게 하면 어플리케이션은 연결이 완료되어 데이터를 읽고, 

쓸 때까지 다른 쓰레드를 실행할 수 있다.

 

서버 소켓에서는 ServerType 프로퍼티를

stThreadBlocking으로 설정하면 블로킹 연결이 생성된다. 

 

블로킹 연결은 연결에 의한 데이터 교환이 될 때까지 실행이 되지 않으므로, 

다른 클라이언트 연결에 대해서 항상 새로운 쓰레드가 생성된다.

 

1)블로킹 연결과 쓰레드의 이용

 

클라이언트 소켓은 블로킹 연결이 사용될 때 새로운 쓰레드가 자동으로 생성되지 않는다. 

만약, 클라이언트 어플리케이션이 데이터를 읽고, 

쓰는 것 이외에 다른 작업이 없다면 이러한 방식도 큰 상관이 없겠지만, 

연결이 이루어지지 않은 경우에도 사용자 인터페이스에서 

다른 작업을 할 수 있게 하려면 새로운 쓰레드를 생성해야 한다.

 

그에 비해 서버 소켓은 블로킹 연결이 생성될 때마다 

각 클라이언트 연결에 대해 새로운 쓰레드가 생성된다. 

그렇게 때문에 연결을 통해 클라이언트가 데이터를 읽고

쓰는 작업을 할 때 다른 클라이언트가 

이를 기다리지 않아도 된다. 

 

서버 소켓은 TServerClientThread 객체를 사용해서 

각 연결에 대한 쓰레드를 구현한다. 

 

일단 서버 클라이언트 쓰레드가 실행되면, 

연결되어 있는 클라이언트 측에서 연결을 통해 데이터를 쓰고 있는지 

검사하여 그렇다면 OnClientRead 이벤트를 발생시키며, 

그렇지 않으면 OnClientWrite 이벤트를 발생시키고

서버가 데이터를 쓰기 시작한다.

 

2) TWinSocketStream 클래스의 활용

 

논-블로킹 연결과 블로킹 서버 연결 모두 연결에 의해 데이터를 읽고, 

쓸 수 있게 되면 특정 이벤트를 받게 된다. 

이때 이벤트 핸들러에서는 실제로 데이터를 읽고, 

쓰는 작업을 윈도우 소켓 객체의 메소드를 이용해서 실행한다.

 

블로킹 클라이언트 연결에서는 반드시

자기 자신이 반대편의 연결이 데이터를 읽고, 

쓸 준비가 되었는지를 파악해야 한다. 

 

이때 TWinSocketStream 객체를 이용해서 연결을 통한 

데이터 읽기, 쓰기를 실행하면, 

적절한 시간 간격 등을 조율할 수 있는 메소드를 제공받을 수 있다. 

예를 들어, WaitForData 메소드를 호출하면 서버 소켓이 준비될 때까지

기다리게 할 수 있다.

 

3) 클라이언트 쓰레드의 작성

 

클라이언트 접속을 위한 쓰레드를 작성할 때에는

New Thread Object 대화 상자를 이용하여 

새로운 쓰레드 객체를 정의할 수 있다. 

 

새로운 쓰레드 객체의 Execute 메소드는 실제 쓰레드 연결을

통한 데이터 읽기와 쓰레드를 관리한다. 

 

다음의 코드가 전형적인 클라이언트 쓰레드의 예이다.

 

procedure TMyClientThread.Execute;

var

  TheStream: TWinSocketStream;

  buffer: string;

begin

  TheStream := TWinSocketStream.Create(ClientSocket1.Socket, 60000);

  //TWinSocketStream을 생성한다.

  try

    while (not Terminated) and (ClientSocket1.Active) do

    begin

    try

      GetNextRequest(buffer);

      TheStream.Write(buffer, Length(buffer) + 1); //버퍼의 내용을 서버에 기록한다.

      ...

    except

    if not(ExceptObject is EAbort) then

      Synchronize(HandleThreadException);

    end;

  end;

  finally

    TheStream.Free;

  end;

end;

 

쓰레드를 이용하려면 OnConnect 이벤트 핸들러를 작성하면 된다.

 

4) 서버 쓰레드의 작성

 

서버 접속에 대한 쓰레드는 TServerClientThread에서 상속받는다.

 

서버 쓰레드를 구현하기 위해서는 Execute 메소드가 아닌, 

ClientExecute 메소드를 오버라이드해야 한다. 

 

클라이언트 쓰레드와 비슷하게 구현하지만, 

클라이언트 소켓 컴포넌트 대신에 TServerClientWinSocket 객체를 

사용한다는 점이 가장 큰 차이점이다. 

 

이 객체는 ClientSocket 프로퍼티를 통해 접근이 가능하다. 

그리고, 예외 처리는 HandleException 메소드를 호출하면 된다.

 

procedure TMyServerThread.ClientExecute;

var

  Stream: TWinSocketStream;

  Buffer: array[0 .. 9] of Char;

begin

  while (not Terminated) and ClientSocket.Connected do

  begin

    try

      Stream := TWinSocketStream.Create(ClientSocket, 60000);

      try

        FillChar(Buffer, 10, 0); //버퍼를 초기화 한다.

        if Stream.WaitForData(60000) then

        begin

          if Stream.Read(Buffer, 10) = 0 then ClientSocket.Close;

          //1분까지 기다린다.

          ...

        end

        else

          ClientSocket.Close;

      finally

        Stream.Free;

      end;

  except

    HandleException;

  end;

  end;

end;

 

이 쓰레드를 사용하려면 OnGetThread 이벤트 핸들러에서 쓰레드를 생성하면 된다.

 

논-블로킹 연결 (Non-Blocking connections)

 

클라이언트 소켓에서 ClientType 프로퍼티를 ctNonBlocking으로 설정하면 

논-블로킹 연결이 이루어 진다. 

 

이 경우 반대 편의 서버가 데이터를 읽거나 쓰게 되면 클라이언트 소켓이 이를 알 수 있으며, 

이때 OnRead 또는 OnWrite 이벤트 핸들러에 의해서 반응하게 된다.

 

서버 소켓에서는 ServerType 프로퍼티를 stNonBlocking으로 설정하면 

논-블로킹 연결이 생성되는데, 

 

논-블로킹 클라이언트 연결처럼 클라이언트에서 데이터를 읽고 쓰게 되면 

OnClientRead 또는 OnClientWrite 이벤트 핸들러에 의해서 반응하게 된다.

 

이러한 이벤트에서 소켓 연결과 관련한 윈도우 소켓 객체가 파라미터로 전달되며, 

이들 객체를 이용하면 여러가지 메소드를 활용할 수 있다. 

 

소켓 연결에서 데이터를 읽을 때에는 ReceiveBuf, ReceiveText 메소드를 사용할 수 있다. 

ReceiveBuf 메소드는 사용하기 전에 ReceiveLength 메소드를 사용해서 

반대 편 연결에서 전송하려는 데이터의 바이트 수를 결정한 후에 사용한다.

 

SendBuf, SendStream, SendText 메소드 등을 이용하면 데이터를 쓸 수 있다. 

 

또한, 데이터를 쓰고 나서 더 이상 소켓 연결을 유지할 필요가 없을 때에는 

SendStreamThenDrop 메소드를 사용해서 스트림에서

읽은 데이터를 모두 전송하고 연결을 닫도록 할 수 있다.

 

참고로 SendStream, SendStreamThenDrop 메소드를 사용하면 

소켓이 연결이 종료된 후 스트림을 자동으로 메모리에서 해제하므로, 

스트림 객체를 직접 해제할 필요가 없다.

 

클라이언트 소켓의 이용

 

어플리케이션을 TCP/IP 클라이언트로 사용할 때에는 

클라이언트 소켓 컴포넌트(TClientSocket)를 폼이나 데이터 모듈에 추가한다. 

클라이언트 소켓에는 연결하고자 하는 서버 소켓과 서버에서

제공받을 서비스를 지정하게 된다.

 

각각의 클라이언트 소켓 컴포넌트는 연결에서의 클라이언트측 

종료점을 나타내게 되는 클라이언트

윈도우 소켓 객체(TClientWinSocket)을 사용한다.

 

1) 서버의 지정

 

클라이언트 소켓 컴포넌트는 

서버 시스템과 연결하고자 하는 포트를 지정할 때 사용할 수 있는 프로퍼티가 있다. 

 

서버 시스템은 Host 프로퍼티에서 호스트의 이름을 이용해서 지정하거나, 

Address 프로퍼티에 직접 IP 주소를 적어넣을 수 있다. 

 

만약 둘 다 지정한 경우에는 호스트 이름을 사용한다.

 

포트 역시 Port와 Service 프로퍼티를 이용해서 지정할 수 있는데, 

Port 프로퍼티에는 포트의 번호를 직접 지정하는 것이고 

Service 프로퍼티에는 포트 번호와 연관된 표준 서비스의 

이름을 지정할 경우 간접적으로 포트가 지정되는 것이다. 

 

둘 다 지정된 경우에는 서비스 이름을 사용한다.

 

2) 연결의 생성

 

연결하고자 하는 서버에 대한 정보를 클라이언트 소켓 컴포넌트에 설정하고 나면, 

런타임에서 Open 메소드를 호출하여 연결을 시도하게 된다. 

디자인 시에 Active 프로퍼티를 True로 설정하면 어플리케이션이

시작할 때 연결을 시도한다.

 

3) 연결에 대한 정보 얻기

 

서버 소켓과의 연결이 완료되면 클라이언트 

윈도우 소켓 객체를 사용해서 연결에 대한 여러가지 정보를 얻어올 수 있게 된다. 

이때 이 객체를 얻어오기 위해 Socket 프로퍼티를 사용한다. 

윈도우 소켓 객체에는 클라이언트와 서버 소켓이 사용하는 

포트 번호와 주소를 결정할 때 사용하는 프로퍼티가 있으며, 

 

SocketHandle 프로퍼티를 이용해서 윈도우 소켓 API를 호출할 때 

사용할 소켓 연결에 대한 핸들을 얻을 수도 있다. 

 

또한, Handle 프로퍼티를 이용해서 소켓 연결에서의 메시지를 받는

윈도우에 접근할 수 있으며 

ASyncStyles 프로퍼티를 이용해서 윈도우 핸들이 받게 되는 

메시지의 종류를 결정할 수도 있다.

4) 연결 종료

 

서버 어플리케이션과의 소켓 연결을 통한 통신이 끝나면,

Close 메소드를 이용해서 연결을 종료할 수 있다. 

이때 서버 측에서 연결을 종료하면 OnDisconnect 이벤트가 발생하므로, 

적절한 처리를 해줄 수 있다.

 

 

4편 계속....

 

728x90
반응형

댓글