소스는 아래 링크에서 받으시면 됩니다.


사용의 예제는 SuperSocket\Delphi\Samples\P2P UDP 폴더에 서버와 클라이언트를 참고하시면 됩니다.


제가 진행했었던 1:1 온라인 강의 시스템에 사용했던 소스 일부를 새로 정리해 본 것입니다.  예전 소스를 거의 무시한채 새로 만들었기 때문에 버그가 많을 수 있습니다.  이슈에 남겨주시면 틈틈히 고쳐 나가겠습니다. 


기본적으로 UDP를 이용해서 P2P로 패킷을 전송하지만, P2P로 보낼 수 없는 상황이되면 TCP를 이용해서 서버를 통해 릴레이를 하게 되어 있습니다.


원래는 1:1 서비스에서 사용했지만 n:m 상황에서도 사용할 수 있도록 수정했습니다.  기본적인 로그인 처리 및 사용자 처리 등의 패킷이 정의 되어 있습니다.

  • 서버
    • UDP 홀펀칭을 위해서 외부 주소 및 포트를 전달하기 위한 게이트웨이 역활
    • UDP 홀펀칭이 실패하면 서버가 TCP로 릴레이로 전달하는 기능
  • 클라이언트
    • 접속, 로그인, 패킷 전송


자세한 사용법은 기회가 될 때 온라인이나 오프라인 강의로 진행하도록 하겠습니다.


예제를 컴파일 하시면 SuperSocket\Bin 폴더가 생기면서 실행 파일 두 개가 생성됩니다.  우선 P2P_Server.exe를 실행하고, Client.exe를 두 번 실행합니다.


Client.exe 실행 화면


  • 상단 왼쪽에 서버 주소를 입력합니다.  
    • 127.0.0.1이 기본 설정되어 있습니다.
  • Room-01에 방 이름을 입력합니다.  
    • 동시에 여러 강의실(대화실)을 개설하기 위해서 사용했습니다.  
    • 저는 실적용에서 랜덤한 긴 문자열을 사용했습니다.
  • User-A에 사용자 아이디를 입력합니다.
    • 암호 처리는 서버 소스를 참고해서 추가하셔야 합니다.
    • 같은 방에 같은 아이디로 접속하면 기존의 아이디가 로그아웃 됩니다.  이를 클라이언트에서 신호를 받아 접속을 끊어야 하는데, 지금보니 해당 처리가 빠져있네요.  다음 커밋에서 추가하도록 하겠습니다.
  • Connect 버턴을 클릭하고, Login 버턴을 클릭하면 됩니다.
  • 가장 긴 빈 에디터에서 메시지를 입력하고 리턴하면 접속 된 사용자에게 모두 메시지를 보냅니다.
  • Start 버턴을 누르면 연속되는 숫자를 계속 보냅니다.  혹시 하나라도 누락이 되면 현재 숫자가 표시됩니다.  
    • UDP로 원격지에서 오는 메시지가 얼마나 사라지는 지 확인하기 위해서 테스트로 만들었습니다.
  • 사용자 입장 및 퇴장 그리고 목록 보이기 등의 기능이 구현되어 있지만, 예제에 완전히 표현되지 않았습니다.  다음 커밋에 추가하도록 하겠습니다.



저작자 표시 비영리 변경 금지
신고

Posted by 류종택



자주 쓰는 형태인데, 매번 스래드 안에서 PostMessage를 날려서 사용하다보니 불편해서 유닛으로 만들었습니다.


사용법은 아래와 같습니다.

procedure TfmMain.FormCreate(Sender: TObject);
begin
  AsyncTask(
    // This will execute task asynchronously by new thread without blocking.
    procedure (AUserData:pointer)
    begin
      Sleep(1000 * 3);
    end,

    // This will execute by main thread after above code completed.
    procedure (AUserData:pointer) 
    begin
      fmMain.Caption := 'Done';
    end
  );
end;

AsyncTask() 함수는 두 개의 익명함수가 필요합니다.  첫 번 째 함수는 별도의 스레드에 의해서 비동기적으로 실행됩니다.  그리고, 실행이 끝나고 난 뒤에는 두 번 째 익명함수가 실행됩니다.  두 번 째 익명함수는 메인 스래드에서 실행되기 때문에 UI 접근을 해도 문제가 되지 않습니다.


AUserData는 AsyncTask(익명함수, 익명함수, UserData) 와 같이 마지막 인자로 넘겨준 포인터 값입니다.  생략하면 nil이 됩니다.


소스는 아래의 링크를 참고하시기 바랍니다.






저작자 표시 비영리 변경 금지
신고

Posted by 류종택

바쁜 와중에 주말에 소스 분석 프로그램을 만들어 봤습니다.  기능은 별로 없는 데 시간은 많이 잡아 먹네요 ㅠ.ㅠ

  • 클래스의 메소드의 begin 뒤에 디버깅 코드를 삽입하는 프로그램입니다.
  • 소스를 분석 할 때, 단일 이벤트가 어떤 경로를 거치는 지 확인하고 싶을 때 사용합니다.
  • 디버깅 모드로 소스를 차근 차근 들어가다보면, 객체의 실행 코드가 아닌 추상화 래벨의 코드로 넘어가서 분석을 귀찮게 할 때도 사용합니다.
  • Config.pas에서 삽입 할 디버깅 코드와 유닛을 지정 할 수 있습니다.

저장소 위치: https://github.com/ryujt/ryulib4delphi/tree/master/Projects/InsertDebugCode


[실행 화면]


  • Execute 버턴을 클릭합니다.
  • 소스가 있는 폴더를 선택합니다.
  • 확인 버턴을 클릭합니다.
  • 선택 된 폴더 및 하위 폴더를 검색하여 모든 *.pas 파일에 디버깅 코드를 삽입합니다.
  • 테스트 케이스로 사용 된 코드가 있는 TestFile 이라는 폴더가 디폴트로 선택 됩니다.  확인 버턴을 클릭하면, 해당 소스가 어떻게 바뀌는 지 확인 하실 수가 있습니다.


주의!!

  • 복구 기능이 없습니다.  복사본을 이용하시거나 소스 버전 관리 프로그램을 이용하세요.
  • 코드 삽입에 대한 필터링은 Config.pas의 DebugCodeMethodBegin 함수에서 AMethodName을 참고하여 Result := '' 처럼 하시면 됩니다.
  • 일반 함수나, 인라인 함수 등은 무시합니다.
  • 컴파일러 지시자를 처리하지 않고 있기 때문에 컴파일러 지시자에 따라 파싱 에러가 발생 될 수 있습니다.  현재는 메소드 마지막에는 디버깅 코드를 삽입하지 않도록 코멘트 처리하였습니다.


저작자 표시 비영리 변경 금지
신고

Posted by 류종택




권한 상승 중인 프로그램에서 권한이 낮은 상태로 프로그램을 실행시키는 방법입니다.  구글링을 통해서 주어 온 소스가 제대로 동작하지 않아서, MSDN을 오가며 약간의 삽질을 하였습니다.  저처럼 불필요한 삽질을 하시게 될 분이 있을 지 몰라서 올려봅니다.


소스는 아래 링크를 참고하시면 됩니다.


사용법은 단순합니다.  위의 유닛에 있는 아래의 함수를 실행하면 됩니다.

  • procedure CreateLowProc(ACommandLine:WideString);
    • ACommandLine 에는 실행시킬 프로그램 파일명을 입력하시면 됩니다.  파라메터가 필요한 경우에는 붙여서 사용하시면 됩니다.
    • 예: CreateLowProc( 'notepad.exe Test.txt' );


저작자 표시 비영리 변경 금지
신고

Posted by 류종택

하마티(http://www.himytv.com/) 안드로이드 버전을 만들 때, 델파이 코드 중에 열거형을 자바로 바꾸기 위해서 만든 프로그램입니다.


소스는 아래 링크를 참고하시면 됩니다.  상당히 단순한 프로그램입니다.


프로그램 동작은

  • 델파이 열거형의 소스의 일부분을 붙여 넣고, Convert 버턴을 클릭합니다.
  • 열거형의 원소들은 자바의 static으로 변경됩니다.
  • toString() 이라는 static 메소드가 추가 됩니다.  열거형을 문자로 표현하고 싶을 때 사용합니다.  제가 델파이에서 디버깅 등을 이유로 자주 사용하는 것이라 넣어 봤습니다.
  • 델파이 소스에 있는 코멘트는 모두 제거 됩니다.
  • 열거형 원소에 인덱스를 강제로 지정하는 등의 처리는 지원하지 않습니다.

[그림 1] 변경 전의 델파이 소스



[그림 2] 변경 후의 자바 소스




저작자 표시 비영리 변경 금지
신고

Posted by 류종택

프로그래밍 소스 코드를 스캐닝하고 싶을 때 사용 할 수 있는 유닛입니다.  스캐닝 후 공백 등의 화이트 스페이스와 코멘트는 무시하고, 특수문자(ttSpecialChar), 문자열(ttString), 숫자(ttNumber), 식별자(ttIdentifier)를 찾아 줍니다.  제가 예전에 계산기 및 프로그래밍 언어를 만들 때 사용하던 것입니다.


소스보기: http://goo.gl/Ucev3A



사용법

procedure ....;
var
  Scanner : TScanner;
  Token : TToken;
begin
  Scanner := TScanner.Create;
  Scanner.SetText( '소스코드...' );

  repeat
    Token := Scanner.GetNextToken;

    if Token.TokenType = ttString then
      WriteLn(
        Format('* %4d:%3d - ''%s''', [Token.Line, Token.Col, Token.Text])
      );
  until Scanner.IsEOF;
  ...
end;

7: SetText 메소드를 이용해서 스캐닝하고자 하는 소스코드 내용을 입력합니다.

9-16: 소스 코드의 끝까지 검색하면서 반복합니다.

10: GetNextToken 메소드는 현재 위치 이후의 토큰을 찾아 줍니다.

12-15: 찾아낸 토큰(TToken)에는 소스 내의 라인 위치 및 토큰의 타입 등을 알 수가 있습니다.



기타

  • property PascalStyle : boolean (read / write)
    • 파스칼 코드를 스캐닝 할 때는 true로 설정되어 있어야 합니다.  false라면 C 언어 스타일이 됩니다.
  • property UseStringEscape :boolean (read)
    • C 언어 스타일이 되면, 문자열 내에서 Escape 문자 처리가 됩니다.






저작자 표시 비영리 변경 금지
신고

Posted by 류종택

우선 전체 소스는 http://goo.gl/0dSTvu 에 있습니다.  RyuLib for Delphi 저장소에 있는 라이브러리가 필요하니 전부 받으시는 것을 좋을 듯 합니다.  (https://code.google.com/p/ryulib4delphi/)


[그림 1] 실행 화면


웬지 이런 프로그램이 있을 거 같지만, 검색도 잘 못하는 컴맹인지라 그냥 만들어서 쓰기로 했습니다 ㅡ.ㅡa



사용법은 간단합니다.


C:\> ExtractStrings.exe  [Path] [File Ext]  -r

  • Path: 검색 할 경로, 생략하면 뒤에 오는 [File Ext]도 생략해야 합니다.  마음에 안드시면 소스를 수정하세요 ㅋㅋ  생략하면 현재 경로를 검색합니다.
  • File Ext: 검색 할 확장자.  *.pas 처럼 하시면 됩니다.
  • -r 또는 -recursive: 하위 폴더들을 모두 검색하게 됩니다.
소스는 파스칼 언어 스타일에 맞도록 되어 있습니다.  소스 중에 문자열이 있으면 모두 화면에 표시해 줍니다.


빌드하시기 싫으신 분들을 위한 실행파일 서비스 ^^*


주요 소스 설명
  • SearchDir.pas
  • Scanner.pas  (http://ryulib.tistory.com/344)
    • 프로그래밍 언어에 대한 스캐너.
    • 디폴트로 파스칼 언어에 맞춰져 있습니다.  코멘트 및 문자열 처리가 다릅니다.
    • property PascalStyle 를 false로 세팅하면 C 언어 스타일이 됩니다.
    • 소스보기: http://goo.gl/Ucev3A





저작자 표시 비영리 변경 금지
신고

Posted by 류종택

간단한 유닛이지만 만들어 놓고 상당히 잘 활용했던 코드입니다.  이제는 델파이에서 자체적으로 지원해주기 시작하지만, 아직 XE7을 사용하지 않는 분들에게는 도움 될 듯 합니다.


병렬처리 유닛https://code.google.com/p/ryulib4delphi/source/browse/trunk/XE2/SimpleMP.pas


사용 예제https://code.google.com/p/ryulib4delphi/source/browse/trunk/XE2/Samples/SimpleMP


예제에는 

  • 1부터 100까지 더하기
  • CRC32 구하기를 단순 반복과 병렬처리를 놓고 속도를 비교하기
가 포함되어 있습니다.



[그림 1] 예제의 실행 결과


예제의 실행 결과를 보면 속도가 3.21 배 향상 된 것으로 나오는데, 처리 할 단위 작업이 시간이 많이 걸릴 수록 효율이 높아집니다.  또한, 기껏 병렬처리하면서 각 단위 작업이 임계영역을 사용하거나 하면 시간이 더 많이 걸리게 됩니다.


델파이 내부 코드 중에는 디버그 모드에서 병렬처리가 오히려 더 느린 경우도 있습니다.  배포하실 때는 릴리즈 모드를 사용하세요.


type
  {*
    스레드 마다 한 번만 호출한다.
    @param Context Execute 메소드의 AContext 인자가 그대로 전달 됩니다.
    @param ThreadNo 현재 TSimpleProcedureSingle 함수를 실행하는 스레드의 순번이 전달 됩니다.
           이를 통해서 스레드를 구별 할 수 있습니다.
    @param ThreadCount 현재 사용 중인 전체 스레드의 숫자를 알려줍니다.
           Execute 함수를 호출 할 때 지정한 AThreadCount와 동일한 값을 가집니다.
  }
  TSimpleProcedureSingle = reference to procedure(Context:pointer; ThreadNo,ThreadCount:integer);

  {*
    스레드 내부에서 익명메소드를 필요한 횟수만큼 반복한다.  (전체 Task를 나눠서 해당 스레드가 꼭 필요한 횟수 만큼)
    @param Context Execute 메소드의 AContext 인자가 그대로 전달 됩니다.
    @param ThreadNo 현재 TSimpleProcedureSingle 함수를 실행하는 스레드의 순번이 전달 됩니다.
           이를 통해서 스레드를 구별 할 수 있습니다.
    @param Index Execute 메소드의 ATaskCount 횟 수만큼 반복해서 TSimpleProcedureRepeat를 실행할 때, 현재 반복하고 있는 순서입니다.
           병렬처리이기 때문에 Index는 순서대로 진행된다는 보장은 없습니다.
  }
  TSimpleProcedureRepeat = reference to procedure(Context:pointer; ThreadNo,Index:integer);

  TSimpleMP = class
  private
  public
    {*
       AThreadCount 숫자만큼 스레드를 만들어서, 각각의 스레드가 ASimpleProcedure를 병렬로 실행합니다.
       스레드와 ASimpleProcedure가 1:1로 매칭 됩니다.

       AContext는 ASimpleProcedure 내부에서 사용하고 싶은 객체(데이터)의 참조를 넘겨주고 싶을 때 사용합니다.
    }
    class procedure Execute(AContext:pointer; AThreadCount:integer; ASimpleProcedure:TSimpleProcedureSingle); overload;

    {*
      반복 횟수(ATaskCount)가 정해져 있는 경우 사용합니다.

      만약 "for i:= 1 to 10 do ..."를 병렬처리한다면, 아래처럼 사용하게 됩니다.
      AThreadCount는 사용하고 싶은 스레드 개수입니다.
        TSimpleMP.Execute( nil, 10, AThreadCount,
          procedure(Context:pointer; ThreadNo,Index:integer)
          begin
            ...
          end
        );
    }
    class procedure Execute(AContext:pointer; ATaskCount,AThreadCount:integer; ASimpleProcedure:TSimpleProcedureRepeat); overload;
  end;

SimpleMP.pas 인터페이스 부분만 올려보았습니다.  간단한 사용법은 유닛 안에 위와 같이 주석처리 되어 있습니다.


가장 단순한 형태는 아래와 같습니다.  1부터 100까지 더하기를 32개의 스레드를 이용해서 구하는 예제입니다.

Trace는 OutputDebugString 함수입니다.  계산되는 과정을 DebugView를 통해서 확인 할 수가 있습니다.

  TSimpleMP.Execute( nil, 100, 32,
    procedure (Context:pointer; ThreadNo,Index:integer) begin
      iTemp := InterlockedExchangeAdd( iSum, Index + 1 );
      Trace( Format('ThreadNo(%d) - %d: %d', [ThreadNo, Index + 1, iTemp]) );
    end
  );


저작자 표시 비영리 변경 금지
신고

Posted by 류종택

Visual C++로 작성 된 http://ryulib.tistory.com/287 소스에 대한 델파이 버전입니다.  공유기가 UPnP를 지원 할 경우, 코딩으로 포트 포워딩을 해주는 유닛입니다.


[그림 1] 실행 결과




유닛 소스


http://goo.gl/PVIWTs




필요한 DLL


libPortForward.7z




DLL에 대한 소스  (VC++)


libPortForward.7z




사용법 (예제)

unit _fmMain;

interface

uses
  PortForward,
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TfmMain = class(TForm)
    Panel1: TPanel;
    moMsg: TMemo;
    btOpen: TButton;
    btOpenUsingThread: TButton;
    procedure btOpenClick(Sender: TObject);
    procedure btOpenUsingThreadClick(Sender: TObject);
  private
    procedure do_WM_SetPortForwarding(var AMsg:TMessage); message WM_SetPortForwarding;
  public
  end;

var
  fmMain: TfmMain;

implementation

{$R *.dfm}

procedure TfmMain.btOpenClick(Sender: TObject);
var
  iErrorCode, ExternalPort : integer;
begin
  ExternalPort := 658;

  iErrorCode := SetPortForwarding( '192.168.10.13', 'Ryu', 658, ExternalPort );

  moMsg.Lines.Add( Format('iErrorCode=%d, , ExternalPort=%d', [iErrorCode, ExternalPort]) );
end;

procedure TfmMain.btOpenUsingThreadClick(Sender: TObject);
begin
  SetPortForwarding( Handle, '192.168.10.13', 'Ryu', 658, 658 );
end;

procedure TfmMain.do_WM_SetPortForwarding(var AMsg: TMessage);
begin
  moMsg.Lines.Add( Format('iErrorCode=%d, , ExternalPort=%d', [AMsg.WParam, AMsg.LParam]) );
end;

end.

6: 라인과 같이 PortForward 유닛을 추가합니다.


19: 라인은 비동기 식으로 처리 결과를 받고자 할 때 필요합니다.  함수를 호출하고 바로 결과를 얻고자 할 때는 필요 없습니다.  다만, 함수를 호출하고 바로 결과를 얻고자 할 때는 결과를 오랫 동안 기다려야 하는 경우도 발생합니다.


36: 라인에서는 SetPortForwarding 함수를 이용해서 '192.168.10.13' 주소의 내부 포트 658을 공유기의 외부 포트 658로 포워딩 해줍니다.  iErrorCode가 0 이면 성공이고 이외의 숫자는 모두 실패를 뜻 합니다.


43: 라인에서는 유사항 동작을 스레드를 이용해서 비동기식으로 결과를 알아옵니다.  결과는 메시지를 통해서 Handle 을 가지고 있는 객체에게 전달 됩니다.


48: SetPortForwarding의 결과가 메시지로 전달 되었습니다.  WParam에는 에러 코드가, LParam에는 실제 오픈 된 외부 포트가 전달 됩니다.


공유기의 외부 포트 658을 요청했다고 해도 반드시 658일 오픈되지는 않습니다.  이미 다른 곳에서 사용 중이라면 다른 포트가 오픈됩니다.



예제 전체 소스:   http://goo.gl/djcJtu





저작자 표시 비영리 변경 금지
신고

Posted by 류종택


ClassToDLL.zip


Source: http://goo.gl/oiYUPx


가끔 C++ 이나 C# 등 다른 언어를 사용하는 개발자들과 협업 할 때 델파이 코드를 Dll로 옮겨서 쓰고는 했었는데, 그 때마다 Dll 코드 작성하는 것이 귀찮아서 "To do list" 에 올려 놓기만 하고 손을 못 대던 넘을 오늘 해치웠습니다 ^^;


좀 더 복잡한 기능을 생각하고 있었는데, 그냥 필요한 최소한의 기능만 뽑아서 만들었습니다.  진작 만들었으면 그 동안 허비한 시간을 ㅠ.ㅠ


  • 실행하면 [그림 1]과 같은 화면이 나타납니다.
  • 맨 위에 있는 메모 창에는 클래스 선언부의 코드를 입력합니다.  Interface도 상관없습니다.  실행하면 예제 겸해서 기본으로 코드가 들어가 있습니다.
  • 버턴을 클릭하면 
    • 중간 창에는 해당 클래스를 Dll 프로젝트 파일로 만들어 줍니다.
    • 가장 아래 있는 창에는 만들어진 Dll을 이용해서 원래 클래스와 같은 인터페이스를 가진 클래스에 대한 유닛이 만들어 집니다.
  • Dll 프로젝트에는 빌드에 필요한 유닛들을 스스로 첨가해주어야 합니다.
  • 주석은 모두 제거 됩니다.
  • 클래스 선언 부분만 입력하셔야 합니다.
  • 기타 수동으로 처리해줘야 할 것들이 생길 것입니다. 


[그림 1]


저작자 표시 비영리 변경 금지
신고

Posted by 류종택


티스토리 툴바