앱테더링(App Tethering)?

앱 테더링은 2개의 개별적인 앱을 서로 연결(테더링)해서 데이터를 주고 받을 수 있는 기술입니다.

앱테더링 기술을 이용해 VCL어플리케이션에서 모바일로 데이터를 제공하면 모바일에서 뷰어만 빠르게 만들어 아주 빠르고 쉽게 모바일앱을 개발할 수 있습니다.

(모바일 앱의 동작을 다시 VCL 앱으로 전송해 사용자의 액션과 모바일 이미지등을 VCL 앱에서 사용하면 더 강력한 시스템으로 탈바꿈할 수 있습니다.)

❑ 앱테더링 컴포넌트

앱 테더링은 TTetheringManager, TTetheringAppProfile 두개의 컴포넌트를 통해 구현할 수 있습니다. 이번글에서는 각 컴포넌트의 자세한 속성과 이벤트에 대해서는 다루지 않습니다. 하지만 아래의 "앱테더링 데모를 통해 기능 살펴보기" 단계를 보시면 주요한 대부분의 속성과 이벤트를 다루고 있기 때문에 충분히 기능을 익힐 수 있습니다.

더 자세히 알고 싶은 기능(속성, 이벤트)는 엠바카데로 기술 도움말(영문)을 통해서 익혀보시기 바랍니다.

아래는 앱테더링 기술이 제공하는 대표적인 속성입니다.

앱테더링의 대표 기능

  • 동일한 서브넷에서 동반자앱 자동탐색 기능(P2P기반으로 별도의 서버 구성이 필요하지 않습니다.)
  • 지정한 IP / 지정한 서브넷(192.168.1.0: 192.168.1.1 ~ 192.168.1.255)에서 탐색 시도
  • 블루투스 기반 앱테더링
  • String / Stream 기반 데이터 전송 기능 제공(Stream이므로 사실상 모든 데이터 전송 가능)
  • 동반자앱에 공개된 Action(명령)을 원격에서 호출하는 Remote Action 기능제공
  • VCL / FMX 2개의 프레임워크 지원
    • VCL 앱을 모바일과 연결해 쉽게 모바일로 전환/확장 할 수 있는 가능성 제공
    • 모바일에서 센서를 연결하면 VCL앱을 IoT로 바로 확장 가능
아래 데모영상을 통해 여러분들의 가능성을 만들어 보시기 바랍니다.

앱테더링 데모를 통해 기능 살펴보기

아래 데모영상은 VCL 어플리케이션을 모바일 앱으로 확장하기 위해 앱테더링 기술의 가장 기본적인 부분을 설명합니다. 

데모영상을 보시고 기능을 익히신 후 앱테더링 기능을 여러분의 프로젝트에 도입하면 어떤 기능을 확장할 수 있을지 생각해 보시고 여러분의 제품에 적용해보시기 바랍니다.

  • 탐색과 접속
    • 1단계, 동일 서브넷에서 접속대상 탐색시도
    • 2단계, 탐색된 테더링 매니저 표시
    • 3단계, 선택 매니저와 연결(페어링)
    • 4단계, 탐색된 테더링 프로필 표시
    • 5단계, 선택 페어링과 연결(페어링)
  • 데이터 송수신
    • 6단계, 문자열 데이터 전송
    • 7단계, 데이터 수신하기(문자열, 스트림)
    • 8단계, 이미지(스트림) 데이터 전송
    • 9단계, 원격지에 정의된 액션(명령) 호출

1, 동일 서브넷에서 접속대상 탐색시도하기

테더링매니저의 DiscoverManagers 메소드를 통해 주변(동일 서브넷)의 동반자 앱을 탐색합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
procedure TForm2.btnDiscoverManagerClick(Sender: TObject);
begin
  InitControls;
 
  LogAndInfo('동일 서브넷에서 접속대상을 찾습니다.');
  // (#1) 주변에서 TetheringManager 발견명령
  TetheringManager.DiscoverManagers;
 
  // (#2) TetheringAppProfile의 Group 기반으로 자동 연결
//  TetheringManager.AutoConnect(3000);
 
  //  (#3) 대상을 지정해 접속할 수 있습니다.
//  TetheringManager.DiscoverManagers(3000, 127.0.0.1);
end;

AutoConnect(#2, TetheringAppProfile의 Group이 동일한 대상으로 자동 접속), 접속 대상을 지정(#3)등 다양한 접속 방식을 제공합니다.

2, 발견된 테더링 매니저 정보표시

탐색(DiscoverManagers)이 완료되면 TetheringManager의 OnEndManagersDiscovery 이벤트가 발생하고, ARemoteManagers 파라메터를 통해 탐색된 매니저정보를 제공합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
procedure TForm2.TetheringManagerEndManagersDiscovery(const Sender: TObject;
  const ARemoteManagers: TTetheringManagerInfoList);
var
  Info: TTetheringManagerInfo;
begin
  cbxRemoteManagers.Items.Clear;
  for Info in ARemoteManagers do
  begin
    Log(Format('[검색된 매니저]'#13#10#9'%s'#13#10#9'%s'#13#10#9'%s'#13#10#9'%s', [
      Info.ManagerIdentifier,
      Info.ManagerName,
      Info.ManagerText,
      Info.ConnectionString
    ]));
    cbxRemoteManagers.Items.Add(Format('%s(%s)', [Info.ManagerText, Info.ConnectionString]));
  end;
  if cbxRemoteManagers.Items.Count > 0 then
  begin
    cbxRemoteManagers.ItemIndex := 0;
    btnPairManager.Enabled := True;
  end;
  LogAndInfo(Format('접속 대상이 [%d]건 발견되었습니다.', [ARemoteManagers.Count]));
end;

3, 선택한 매니저와 연결(페어링)

접속할 대상 매니저를 선택 후 페어링(PairManager)을 시도합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 선택한 매니저와 연결
procedure TForm2.btnPairManagerClick(Sender: TObject);
var
  N: Integer;
begin
  if cbxRemoteManagers.ItemIndex < 0 then
    Exit;
 
  N := cbxRemoteManagers.ItemIndex;
  LogAndInfo(TetheringManager.RemoteManagers[N].ManagerText + ' - 매니저와 페어링 시도');
  TetheringManager.PairManager(TetheringManager.RemoteManagers[N]);
end;
 
// 매니저 비밀번호 설정
procedure TForm2.TetheringManagerRequestManagerPassword(const Sender: TObject;
  const ARemoteIdentifier: string; var Password: string);
begin
  Password := edtPassword.Text;
 
  TThread.Synchronize(TThread.CurrentThread,
    procedure begin
      LogAndInfo('[비밀번호 요청] - ' + edtPassword.Text);
    end);
end;

매니저간 페어링 시도 시 서로의 비밀번호를 통해 연결을 허용합니다. 만약 매니저간의 비밀번호가 맞지 않다면, TetheringManager 컴포넌트에서 인증오류(OnAuthErrorFromLocal, OnAuthErrorFromRemote) 이벤트가 발생하고 페어링은 실패합니다.

비밀번호는 TetheringManager.Password 속성을 통해서도 설정할 수 있습니다.

4, 발견된 테더링 프로필 정보표시

매니저간 연결(페어링)되면 TetheringManager의 OnEndProfilesDiscovery 이벤트가 발생되며 ARemoteprofiles 파라메터를 통해 탐색된 프로필 정보를 제공합니다.

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
// 프로필 발견완료 이벤트
procedure TForm2.TetheringManagerEndProfilesDiscovery(const Sender: TObject;
  const ARemoteProfiles: TTetheringProfileInfoList);
var
  Info: TTetheringProfileInfo;
begin
//  Log('TetheringManagerEndProfilesDiscovery');
  cbxRemoteProfiles.Items.Clear;
  for Info in ARemoteProfiles do
  begin
    Log(Format('[앱 프로필]'#13#10#9'%s'#13#10#9'%s'#13#10#9'%s'#13#10#9'%s'#13#10#9'%s', [
      Info.ManagerIdentifier,
      Info.ProfileIdentifier,
      info.ProfileText,
      Info.ProfileGroup,
      Info.ProfileType
 
    ]));
    cbxRemoteProfiles.Items.Add(Format('%s(%s)', [Info.ProfileText, Info.ProfileType]));
  end;
  if cbxRemoteProfiles.Items.Count > 0 then
  begin
    cbxRemoteProfiles.ItemIndex := 0;
    btnPairProfile.Enabled := True;
  end;
  LogAndInfo(Format('대상 프로필 [%d]건 발견되었습니다.', [ARemoteProfiles.Count]));
end;

5, 선택한 프로필과 연결

접속할 대상 프로필을 선택(TetheringAppProfile.Connect)하여 페어링 시도합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 선택한 프로필과 연결
procedure TForm2.btnPairProfileClick(Sender: TObject);
var
  N: Integer;
begin
  if cbxRemoteProfiles.ItemIndex < 0 then
    Exit;
 
  N := cbxRemoteProfiles.ItemIndex;
  LogAndInfo(TetheringManager.RemoteProfiles[N].ProfileText + ' - 프로필과 페어링 시도');
  if TetheringAppProfile.Connect(TetheringManager.RemoteProfiles[N]) then
  begin
    LogAndInfo('페어링 성공!!');
    InitControls;
  end;
end;

페어링이 완료되면 데이터 전송, 데이터 수신, 원격 액션 호출등의 작업이 가능해 집니다.

6, 문자열 전달

TetheringAppProfile.SendString 메소드를 통해 명령어, 데이터등의 문자열 데이터를 전달할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
procedure TForm2.btnSendStrHelloClick(Sender: TObject);
begin
  LogAndInfo('문자열 전달 - 동반자 모바일앱에 Hello 전달');
  TetheringAppProfile.SendString(
    TetheringAppProfile.ConnectedProfiles.First,
    'HELLO',
    '안녕? App Tethering!!!');
end;
 
procedure TForm2.btnSendStrRSClick(Sender: TObject);
begin
  LogAndInfo('문자열 전달 - 동반자 모바일앱에 ReverseString 전달');
  TetheringAppProfile.SendString(
    TetheringAppProfile.ConnectedProfiles.First,
    'ReverseString',
    '반갑습니다.');
end;

SendString 메소드는 3개의 파라메터를 전달합니다.

  1. 전송할 앱 프로필 객체
  2. 수신 시 리소스의 힌트로 받을 수 있는 설명(Description)
  3. 문자열 데이터 값
ReverseString 문자열 데이터는 상대방(모바일 앱)에서 수신 시 글자를 뒤집어(반갑습니다. > .다니습갑반) 다시 전송하도록 구현되었습니다.

7, 데이터 수신

상대방 동반자앱에서 데이터를 보내면 TetheringAppProfile의 OnResourceReceived 이벤트를 통해 데이터를 수신할 수 있습니다. 문자열 데이터, 이미지 데이터(Stream)등이 모두 OnResourceReceived 이벤트를 통해 수신합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<p>procedure TForm2.TetheringAppProfileResourceReceived(const Sender: TObject;
  const AResource: TRemoteResource);
begin
  case AResource.ResType of
  // 데이터(SendString)
  TRemoteResourceType.Data:
    begin
      // ReverseString을 받은 모바일에서 전송한 응답
      if AResource.Hint = 'EchoString' then
        LogAndInfo('EchoString : ' + AResource.Value.AsString)
    end;
  // 스트림(SendStream)
  TRemoteResourceType.Stream:
    begin
      // 모바일에서 전송한 이미지
      if AResource.Hint = 'FMXIMG' then
      begin
        LogAndInfo('이미지 수신');
        LoadImage(AResource.Value.AsStream);
      end;
    end;
  end;
end;</p>

AResource 파라메터 주요 속성

  • AResource.ResType
    • SendString으로 전송 시 TRemoteResourceType.Data로 SendStream으로 전송 시 TRemoteResourceType.Stream으로 수신
  • AResource.Hint
    • SendString과 SendStream의 Description 파라메터에 입력한 값을 담고 있습니다.
  • AResource.Value
    • SendString과 SendStream의 Value(AString, AStream)에 입력한 값을 담고 있습니다.

8, 데이터 전달 - 이미지전송

이미지, 파일 등의 데이터를 TStream으로 변환해 동반자 앱으로 전달 할 수있습니다.(TStream은 TMemoryStream, TFileStream, TStringStream 등의 부모클래스이므로  다양한 데이터를 전달할 수 있습니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
procedure TForm2.btnSendStreamImageClick(Sender: TObject);
var
  Stream: TMemoryStream;
begin
  LogAndInfo('데이터 전달 - 이미지 데이터를 TStream으로 동반자 모바일앱에 전달');
  Stream := TMemoryStream.Create;
  try
    Image1.Picture.Graphic.SaveToStream(Stream);
      TetheringAppProfile.SendStream(TetheringManager.RemoteProfiles.First,
      'VCLIMG',
      Stream
    );
  finally
    Stream.Free;
  end;
  PageControl1.TabIndex := 2;
//  TetheringAppProfile.SendStream()
end;

9, 원격지의 명령 호출(이미지 전송 요청) 

TetheringAppProfile.RunRemoteAction 메소드를 통해 원격지에 공개된 Action을 실행 할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
// 원격지의 액션 호출
procedure TForm2.btnRunRemoteActionClick(Sender: TObject);
begin
  LogAndInfo('원격 명령 호출 - 원격지(동방자 모바일 앱)의 ActionSendImg 액션 호출');
  TetheringAppProfile.RunRemoteAction(
    TetheringManager.RemoteProfiles.First,
    'ActionSendImg'
  );
  PageControl1.TabIndex := 3;
end;

모바일 앱의 TetheringAppProfile에는 Actions 속성에 아래와 같이 Action이 등록되어 있습니다.

TetheringAppProfile의 Actions에 등록된 Action(명령)은 외부로 공개(IsPublic)할 수 있어 외부에서 RunRemoteAction 메소드를 통해 호출할 수 있습니다.

❑ 위 데모 소스코드 받기

앱테더링 데모 더 살펴보기...

RAD Studio XE7의 샘플 프로젝트에는 아래의 다양한 앱테더링 데모가 있습니다.

  • 앱테더링 샘플 프로젝트 경로

    • C:\Users\Public\Documents\Embarcadero\Studio\15.0\Samples\Object Pascal\RTL\Tethering

❑ BDShoppingList

  • 앱테더링 기술을 이용해 데스크탑의 쇼핑 데이터를 모바일에 제공합니다.

  • 모바일 화면에서 쇼핑항목의 구매를 선택하면 구매명령을 데스크탑으로 전달해 구매처리를 완료합니다.

  • 데스크탑과 모바일은 변경된 데이터를 수시로 동기화합니다.

  • http://docwiki.embarcadero.com/CodeExamples/XE7/en/RTL.BDShoppingList_Sample

❑ DesktopCast

  • 앱테더링 기술을 활용해 데스크탑 화면을 모바일에 방송하는 샘플입니다.