'Network'에 해당되는 글 8건

  1. 2013.03.03 [Platinum UPnP] 101 :: (6) Device에서 Event 받기
  2. 2013.03.02 [Platinum UPnP] 101 :: (5) Device 연결하고 Action 호출하기
  3. 2013.03.02 [Platinum UPnP] 101 :: (4) 간단한 Control-Point 만들기
  4. 2013.03.01 [Platinum UPnP] 101 :: (3) Service에 Action 추가하기
  5. 2013.03.01 [Platinum UPnP] 101 :: (2) Device에 Service 올리기 + StateVariable & Event
  6. 2013.03.01 [Platinum UPnP] 101 :: (1) 간단한 Device 만들기
  7. 2013.03.01 [Platinum UPnP] 101 :: (0) 소개
  8. 2013.01.14 #20130114-01 :: Ubuntu 12.04 LTS :: Atheros AR8161/8165 드라이버 설치

[Platinum UPnP] 101 :: (6) Device에서 Event 받기




파일: tutorial06.cpp


이제 Platinum UPnP 101 Tutorial도 막바지에 왔습니다. 이번에는 끝으로 Control-Point에서 Device의 Event를 받는 기능을 구현하도록 하겠습니다.

    하지만, 기능 구현에 앞서 약간의 리팩토링을 하고 진행하도록 하겠습니다.

    이전 단계에서 우리는 Action을 호출하기 위하여 TutorialListener 객체에서 Device 정보를 받아 PLT_CtrlPoint 객체에서 이 정보를 이용해 Action을 호출하였습니다. Event에서도 이와 비슷한 형태가 필요합니다. 그래서 이렇게 서로 다른 객체사이에 정보를 주고 받기 위한 복잡한 인터페이스를 줄이기 위헤, TutorialListener 클래스가 PLT_CtrlPoint 클래스와 PLT_CtrlPointListener 클래스를 다중 상속 받도록 변경하겠습니다. 그리고 TutorialListener 클래스의 이름을 TutorialCP로 변경합니다. 변경된 코드는 아래와 같습니다.


class TutorialCP : public PLT_CtrlPoint, public PLT_CtrlPointListener
{
public:
	TutorialCP(void)
	{
		AddListener(this);
	}
	~TutorialCP(void)
	{
	}

	// ...

	void Print(const char* str)
	{
		if (!device_.IsNull()) {
			PLT_Service* service;
			device_->FindServiceById("urn:upnp-org:serviceId:Tutorial.001", service);
			PLT_ActionDesc* desc = service->FindActionDesc("Print");
			PLT_ActionReference action(new PLT_Action(*desc));
			action->SetArgumentValue("String", str);
			InvokeAction(action);
		}
	}

	// ...
};


    클래스의 형태를 변경하면서 Listener 객체(여기에서는 자기 자신)를 생성자에서 바로 등록하도록 하였습니다. 그리고 device() 함수를 제거하고, main() 함수에서 콘솔의 입력을 받아 "Print" Action을 실행하던 부분을 Print() 함수로 묶었습니다.

    클래스 형태가 변하였으므로 main() 함수에서 TutorialCP(구 TutorialListener) 객체를 사용하는 코드에도 변경이 일어났습니다. 우선 Control-Point를 만들고 등록하는 부분은 아래와 같습니다.


TutorialCP* ctrlpoint = new TutorialCP;
PLT_CtrlPointReference ref(ctrlpoint);
upnp.AddCtrlPoint(ref);


    콘솔의 입력을 받아 "Print" Action을 호출하는 부분은 아래와 같이 변경되었습니다.


ctrlpoint->Print(input.c_str());


    빌드 후 실행하면 이전 단계와 동일하게 동작합니다. 이제 여기에서부터 Event에 대한 구현을 다시 시작합니다.

    Control-Point에서 Event를 받기 위해서는 Device Spy 프로그램에서 "Subscribe to Events"를 했듯이, PLT_CtrlPoint 객체서 Event를 받기 원하는 Service에 대해 Subscribe() 함수를 호출해야 합니다. 이 동작은 OnDeviceAdded() 함수에서 수행합니다. Service는 Action을 호출할 때 Print() 함수에서도 쓰이므로 멤버변수로 저장해 놓습니다. TutorialCP 클래스에서 변경된 코드들은 아래와 같습니다.


class TutorialCP : public PLT_CtrlPoint, public PLT_CtrlPointListener
{
public:
	// ...

	virtual NPT_Result OnDeviceAdded(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] added\n");
			device->FindServiceById("urn:upnp-org:serviceId:Tutorial.001", service_);
			Subscribe(service_);
			device_ = device;
		}
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnDeviceRemoved(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] removed\n");
			service_ = 0;
			device_ = 0;
		}
		return NPT_SUCCESS;
	}

	// ...

	void Print(const char* str)
	{
		if (!device_.IsNull()) {
			PLT_ActionDesc* desc = service_->FindActionDesc("Print");
			// ...
		}
	}

private:
	// ...
	PLT_Service* service_;
};


    마지막으로 OnEventNotify() 함수에서 Event를 받아서 변경된 "PrintBuffer"의 값을 콘솔에 출력합니다. 코드는 아래와 같습니다.


virtual NPT_Result OnEventNotify(PLT_Service* service, NPT_List<plt_statevariable*>* vars)
{
	const PLT_DeviceData* device = service->GetDevice();
	if (device->GetFriendlyName() == "Tutorial Device") {
		NPT_List<plt_statevariable*>::Iterator itr = vars->GetFirstItem();
		for (bool stop = false ; !stop ; ++itr) {
			const PLT_StateVariable* state_variable = *itr;
			if (state_variable->GetName() == "PrintBuffer") {
				const NPT_String& value = state_variable->GetValue();
				printf("[PrintBuffer] changed: %s\n", value);
				break;
			}
			stop = (itr == vars->GetLastItem());
		}
	}
	return NPT_SUCCESS;
}


    빌드 후 실행시킨 후 Device의 콘솔에서 직접 값을 변경하거나 Control-Point의 Action 호출을 통해 값을 변경하였을 때, Event에 의해 Control-Point의 콘솔에 변경된 값이 출력되는 것을 볼 수 있습니다.


    이것으로 Platinum UPnP를 이용하여 UPnP의 기본 구성 요소인 Device와 Control-Point를 간단하게 구현해 보았습니다. Platinum UPnP는 매우 잘 설계된 강력한 라이브러리 이므로, 라이선스 문제가 있기 때문에 무조건 쓸 수 없겠지만, 사용할 수 있는 조건이 된다면 UPnP/DLNA 서비스를 만드는데 적극적으로 사용해 보기를 권장합니다.

[Platinum UPnP] 101 :: (5) Device 연결하고 Action 호출하기




파일: tutorial05.cpp


이번에는 전에 만들어 두었던 "Tutorial Device"를 찾아내어 "Print" Action을 호출하는 기능을 추가합니다. 호출하는 방법은 Control-Point의 콘솔에 문자열을 입력하면 입력된 문자열을 인자로 하여 Action을 호출하는 것으로 해보겠습니다.

    미리 추가해 두었던 TutorialListener 클래스에는 OnDeviceAdded와 OnDeviceRemoved라는 한 쌍의 콜백 함수가 있습니다. OnDeviceAdded 함수는 Control-Point를 실행 했을 때 Network에 이미 존재하는 UPnP Device들 또는 Control-Point가 실행된 후 Network에 새롭게 등장한 Device들을 알려줍니다. 반면에 OnDeviceRemoved 함수는 Network에서 기존의 Device들이 사라지게 되면 알려줍니다.

    이 두 함수에 "Tutorial Device"를 찾으면 멤버변수로 저장해 두는 기능을 추가해 보겠습니다. 코드는 아래와 같습니다.


class TutorialListener : public PLT_CtrlPointListener
{
public:
	virtual NPT_Result OnDeviceAdded(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] added\n");
			device_ = device;
		}
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnDeviceRemoved(PLT_DeviceDataReference& device)
	{
		if (device->GetFriendlyName() == "Tutorial Device") {
			printf("[Tutorial Device] removed\n");
			device_ = 0;
		}
		return NPT_SUCCESS;
	}

	// ...

private:
	PLT_DeviceDataReference device_;
};


    먼저 Device의 정보가 담겨있는 PLT_DeviceData 객체를 저장할 멤버변수를 추가합니다. OnDeviceAdded() 함수에서는 추가된 Device가 "Tutorial Device"인지 확인하고, 맞다면 멤버변수에 저장합니다. OnDeviceRemoved() 함수에서는 사라진 Device가 "Tutorial Device"라면 멤버변수를 NULL 값으로 초기화 합니다.

    이제 콘솔의 입력을 받아 Device의 Action을 호출하는 기능을 구현하려고 합니다. 이에 앞서 콘솔의 입력을 받고 Control-Point의 조작은 main 함수에서 일어나는데 반해 Device의 정보는 TutorialListener 객체가 갖고 있습니다. 따라서 TutorialListener 객체로부터 Device 정보를 얻어오는 함수를 아래와 같이 TutorialListener 클래스에 추가합니다.


PLT_DeviceData* device(void) const
{
	return device_.AsPointer();
}


    마지막으로 main() 함수에 아래와 같이 콘솔에서 입력된 값으로 Action을 호출하는 코드를 작성합니다.


PLT_DeviceData* device = listener.device();
if (device != 0) {
	PLT_Service* service;
	device->FindServiceById("urn:upnp-org:serviceId:Tutorial.001", service);
	PLT_ActionDesc* desc = service->FindActionDesc("Print");
	PLT_ActionReference action(new PLT_Action(*desc));
	action->SetArgumentValue("String", input.c_str());
	ctrlpoint->InvokeAction(action);
}


    지금까지 작성한 코드를 빌드한 후 실행시켜 봅니다. 앞서 만든 "Tutorial Device"가 이미 실행되고 있거나, 지금 만든 Control-Point를 실행시킨 후 실행한다면, 콘솔에 "[Tutorial Device] Added"가 출력되는 것을 볼 수 있습니다. 이제 콘솔에 문자열을 입력하면, Device의 콘솔 창에 Contorol-Point에서 입력한 문자열을 입력 값으로 한 Action이 호출되었다는 정보가 출력될 것입니다.

[Platinum UPnP] 101 :: (4) 간단한 Control-Point 만들기




파일: tutorial04.cpp


앞서 우리는 간단하지만 기본적인 기능을 모두 갖춘 하나의 Device를 만들어 보았습니다. 이번에는 UPnP의 또 다른 구성 요소인 Control-Point를 만들어 보겠습니다.

    이에 앞서 Control-Point에 대해 간략히 설명하자면, Device를 검증하는데 사용했던 Device Spy가 대표적인 Control-Point라고 할 수 있습니다. 즉, Device를 찾고 Device의 정보를 보고 올바른 Action을 날리고 Event를 받는 등의 일을 하는 것이 Control-Point의 역할입니다.

    이제 가장 간단한 Control-Point 프로그램을 구성해 보겠습니다. 코드는 아래와 같습니다.


#include <string>
#include <iostream>
#include "Platinum.h"

int main()
{
	PLT_UPnP upnp;

	PLT_CtrlPointReference ctrlpoint(new PLT_CtrlPoint);
	upnp.AddCtrlPoint(ctrlpoint);
	upnp.Start();

	bool stop = false;
	do {
		std::string input;
		std::cin >> input;
		if (input == "q" || input == "Q") {
			stop = true;
		} else {
			// nothing
		}
	} while(!stop);

	upnp.Stop();
	return 0;
}


    Control-Point의 기본적인 기능을 담당하는 PLT_CtrlPoint 클래스는 PLT_DeviceHost 클래스가 가상 클래스였던 것과는 다르게 독립적으로 객체를 생성할 수 있습니다. 다만, Device에 Service를 올리기 전까지 아무것도 할 수 없었던 것처럼 지금도 할 수 있는 것이 아무것도 없다는 것이 문제 입니다.

    Device의 경우에는 PLT_DeviceHost 클래스를 상속 받아 객체 생성이 가능한 자식 클래스를 구성했습니다. 그러나 Control-Point는 Listener 객체를 등록받아 Listener 객체들에게 콜백을 호출합니다. 따라서 이번에는 PLT_CtrlPoint 클래스를 상속받는 것이 아니라 PLT_CtrlPointListener 클래스라는 순수 가상함수를 상속 받아 PLT_CtrlPoint 객체에게 등록해 줍니다.

    PLT_CtrlPointListener 클래스를 상속 받은 TutorialListener 클래스의 기본 구현 코드는 아래와 같습니다.


class TutorialListener : public PLT_CtrlPointListener
{
public:
	virtual NPT_Result OnDeviceAdded(PLT_DeviceDataReference& device)
	{
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnDeviceRemoved(PLT_DeviceDataReference& device)
	{
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnActionResponse(NPT_Result res, PLT_ActionReference& action, void* userdata)
	{
		return NPT_SUCCESS;
	}

	virtual NPT_Result OnEventNotify(PLT_Service* service, NPT_List<PLT_StateVariable*>* vars)
	{
		return NPT_SUCCESS;
	}
};


    이제 PLT_CtrlPoint 객체에 TutorialListener 객체를 등록을 해줍니다. main() 함수의 코드가 아래와 같이 변경 됩니다.


TutorialListener listener;
PLT_CtrlPointReference ctrlpoint(new PLT_CtrlPoint);
ctrlpoint->AddListener(&listener);
upnp.AddCtrlPoint(ctrlpoint);


    이것으로 Control-Point의 기능을 만들기 위한 기본 코드가 완료되었습니다.

[Platinum UPnP] 101 :: (3) Service에 Action 추가하기




※ 주의: 앞으로 나오는 이상한 문자열이나 XML에 대해서 친절하게 설명하지 않습니다. 눈치것 의미를 알아차리시거나, 스펙문서를 통해 의미를 파악해 주시기 바랍니다. 이 문서의 목적은 Platinum UPnP를 이용해 잘 동작하는 Tutorial 프로그램을 작성하는 것이니까요.


파일: tutorial03.cpp


우리는 이제까지 Device를 만들어서 Service를 등록하고 Event가 발생하도록 StateVariable을 조작하는 것까지 해보았습니다.  이번에는 Device에 Action을 추가해 보겠습니다. Action의 기능은 입력 값을 문자열로 받아서 앞서 사용했던 "PrintBuffer" StateVariable의 값을 변경하고 콘솔에 변경된 값을 출력하는 것으로 하겠습니다. 이렇게 하면 Action이 정상적으로 동작 했는지에 대해 콘솔과 Event 양쪽에서 모두 확인할 수 있을 것입니다.


    우선 Service Description XML에 Action에 대한 내용을 추가합니다. 수정된 내용은 아래와 같습니다.


const char* SCPDXML_TUTORIAL =
	"<?xml version=\"1.0\" ?>"
	"  <scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
	"    <specVersion>"
	"      <major>1</major>"
	"	   <minor>0</minor>"
	"    </specVersion>"
	"    <actionList>"
	"      <action>"
	"        <name>Print</name>"
	"        <argumentList>"
	"          <argument>"
	"            <name>String</name>"
	"            <direction>in</direction>"
	"            <relatedStateVariable>PrintBuffer</relatedStateVariable>"
	"          </argument>"
	"        </argumentList>"
	"      </action>"
	"    </actionList>"
	"    <serviceStateTable>"
	"      <stateVariable sendEvents=\"yes\">"
	"        <name>PrintBuffer</name>"
	"        <dataType>string</dataType>"
	"        <defaultValue></defaultValue>"
	"      </stateVariable>"
	"    </serviceStateTable>"
	"  </scpd>";


    변경 내용을 간략히 살펴보자면, "ActionList" Tag가 추가되고 그 안에 "Print"라는 이름의 Action이 하나 있습니다. 이 Action은 "String"이라는 입력값을 갖으며, 값의 형태는 "PrintBuffer" StateVariable의 정의를 따라갑니다. 스펙을 보면 아시겠지만, UPnP에서 Action의 입출력 값들의 타입은 무조건 StateVariable 중 하나와 연결되어야 하는 것으로 보입니다.

    이 상태로 컴파일 후 실행하면, Device Spy에서 Action이 추가된 것을 확인할 수 있습니다. 하지만, 현재로써는 실제로 Action이 동작하지 않습니다. 그래서 이제 Action의 구현 코드를 넣어야 합니다.

    PLT_DeviceHost 클래스에는 OnAction()이라는 virtual 함수가 있습니다. 이 함수의 내용을 오버라이딩(Overriding)하여 원하는 Action이 동작하도록 하여야 합니다. Tutorial Device 클래스에 추가된 코드는 다음과 같습니다.


virtual NPT_Result OnAction(PLT_ActionReference& action, const PLT_HttpRequestContext& context)
{
	PLT_ActionDesc& desc = action->GetActionDesc();
	const NPT_String name = desc.GetName();
	if (name == "Print") {
		NPT_String value;
		action->GetArgumentValue("String", value);
		PLT_Service* service = desc.GetService();
		service->SetStateVariable("PrintBuffer", value);
		printf("[Action:%s] (String, %s)\n", name, value);
		return NPT_SUCCESS;
	}
	return PLT_DeviceHost::OnAction(action, context);
}


    위 코드의 내용은 다음과 같습니다. OnAction() 함수가 호출되면 Action의 이름이 "Print"가 맞는지 확인합니다. 맞지 않다면 부모 클래스의 기본 OnAction() 함수를 수행합니다. 맞다면 다음과 같이 "Print" Action을 수행합니다. 호출된 Action에서 "String"의 값을 읽어 옵니다. 그리고 읽어온 값을 Service의 "PrintBuffer" StateVariable에 적용합니다. 마지막으로 수행한 Action과 입력값을 콘솔에 출력합니다.


    이제 UPnP에서 정의하는 기본적인 동작을 모두 갖춘 하나의 Device가 완성되었습니다. 다음에는 간단한 Control-Point를 하나 만들어 보겠습니다.

[Platinum UPnP] 101 :: (2) Device에 Service 올리기 + StateVariable & Event




※ 주의: 앞으로 나오는 이상한 문자열이나 XML에 대해서 친절하게 설명하지 않습니다. 눈치것 의미를 알아차리시거나, 스펙문서를 통해 의미를 파악해 주시기 바랍니다. 이 문서의 목적은 Platinum UPnP를 이용해 잘 동작하는 Tutorial 프로그램을 작성하는 것이니까요.



앞에서 우리는 간단한 Device를 만들어서 확인해 보았습니다. 하지만, UPnP에서 Device만으로는 아무런 소용이 없습니다. UPnP는 Device가 소유하고 있는 Service를 통해서만 Device를 조작할 수 있습니다. 이러한 동작을 위해 Service는 다시 Action과 StateVariable, Event라는 개념을 갖고 있습니다. Action은 외부에서의 명령, StateVariable은 말 그대로 상태값(음량, 음소거 상태 등등)이며, Event는 StateVariable의 변경이 일어났을 때, 만약 그것을 외부에 알리도록 설정되어 있다면 변경을 공지합니다.


아마 Windows의 COM/DCOM/ActiveX 개념을 알고 있다면, 위의 내용을 이해하기 훨씬 쉬울 겁니다. 임의의 바이너리를 범용적으로 연결하여 동작하기 위한 기본 원리는 완벽하게 동일하다고 말할 수 있겠네요. 뭐, 모른다면 할 수 없구요 ㅋ


    이번 작업은 앞서 했던 파일(tutorial01.cpp)을 바탕으로 수정하도록 하겠습니다. 

    먼저, 성의가 없었던 Device의 생성자를 살짝 수정합니다. 수정된 코드는 아래와 같습니다.


TutorialDevice()
	: PLT_DeviceHost("/", "", "urn:schemas-upnp-org:device:Tutorial:1", "Tutorial Device")
{
}


    이제 Service를 생성해야 합니다. Service는 XML 형식의 Service Description에 의해 그 형태가 결정 됩니다. Service Description에는 그 Service가 갖는 Action과 StateVariable, Event에 대해 기술되어 있습니다.

    일단 복잡함을 피하기 위하여, StateVariable과 Event에 대한 동작을 먼저 만들어 보겠습니다. 지금 구현할 동작을 명세하자면, 콘솔 입력을 통해 문자열을 입력 받은 후 그것을 StateVariable에 저장하여 Event가 발생하는지를 확인합니다.

    우선 Service Description XML을 준비해야 합니다. 그래서 아래와 같은 변수를 정의합니다.


const char* SCPDXML_TUTORIAL =
	"<?xml version=\"1.0\" ?>"
	"  <scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
	"    <specversion>"
	"       <major>1</major>"
	"	    <minor>0</minor>"
	"	 </specversion>"
	"    <servicestatetable>"
	"      <statevariable sendevents=\"yes\">"
	"        <name>PrintBuffer</name>"
	"        <datatype>string</datatype>"
	"        <defaultvalue></defaultvalue>"
	"      </statevariable>"
	"    </servicestatetable>"
	"  </scpd>";


    위의 XML에서는 현재 Service 안에 문자열(string)로 취급되는 "PrintBuffer"라는 이름의 StateVariable이 하나 있으며, 변경시 Event가 발생한다는 것을 알려주고 있습니다. 이제 Device에 이 Service를 등록해야 합니다.

    전 단계에서 PLT_DeviceHost 클래스의 자식 클래스를 만들기 위해 정의했던 함수가 있습니다. 바로 SetupServices() 함수 였습니다. 기억하시는지요? Device에 Service를 등록하기 위해서는 이 함수 안에서 등록해 주어야 합니다. 그래서 아래와 같이 SetupServices() 함수에 내용물을 넣습니다.


virtual NPT_Result SetupServices(void)
{
	PLT_Service* service = new PLT_Service(
		this,
		"urn:schemas-upnp-org:service:Tutorial:1", 
		"urn:upnp-org:serviceId:Tutorial.001",
		"Tutorial");
	service->SetSCPDXML(SCPDXML_TUTORIAL);
	AddService(service);

	service->SetStateVariable("Status", "True");

	return NPT_SUCCESS;
}


    이렇게 함으로써 "urn:upnp-org:serviceId:Tutorial.001"이라는 아이디와 "Tutorial"이라는 이름을 갖는 Service가 Device에 등록 되었습니다.

    StateVariable과 Event가 정상 동작하는지 판별하기 위해서는 수동으로 값을 변경해 주어야 할 필요가 있습니다. 그래서 아래와 같이 외부에서 "PrintBuffer"를 조작하기 위한 함수를 하나 만들겠습니다.


void SetPrintBuffer(const char* str)
{
	PLT_Service* service;
	if (FindServiceByName("Tutorial", service) == NPT_SUCCESS) {
		service->SetStateVariable("PrintBuffer", str);
	}
}


    FindServiceByName() 함수는 PLT_DeviceHost의 부모 클래스인 PLT_DeviceData의 멤버함수 입니다. 위 함수 이외에도 다양한 방법으로 Device가 소유한 Service를 찾을 수 있습니다. Service를 찾고나면 원하는 StateVariable에 입력값을 저장합니다.

    마지막으로 콘솔에서 값을 입력 받아 위에서 정의한 SetPrintBuffer() 함수를 호출하도록 main() 함수의 while loop 구문을 아래와 같이 수정합니다.


bool stop = false;
do {
	std::string input;
	std::cin >> input;
	if (input == "q" || input == "Q") {
		stop = true;
	} else {
		tutorial_device->SetPrintBuffer(input.c_str());
	}
} while(!stop);


    이제 모든 작업이 완료 되었습니다. 코드를 빌드하여 실행시킨 후 StateVariable을 변경시켜 Event가 발생하는지 Device Spy로 확인할 차례입니다. 여기서 주의할 점은 Device Spy의 해당 서비스에서 마우스 오른쪽 버튼으로 컨텍스트 메뉴를 연 뒤 "Subscribe to Events" 항목을 클릭해야 해당 Service의 Event가 Device Spy에 나타나게 됩니다.

    확인해 본 결과 저는 아주 잘 동작하고 있습니다. 여러분은 어떠신가요?


[Platinum UPnP] 101 :: (1) 간단한 Device 만들기




파일: tutorial01.cpp


UPnP는 크게 Device와 Control-Point라는 개념으로 나뉘어 집니다. 우선 간단한 Device를 만들어 보겠습니다. Device를 먼저하는 이유는 Device Spy 프로그램으로 동작을 간단히 확인해 볼 수 있기 때문입니다. Control-Point의 기본적인 동작은 Device를 찾아내어 그 정보들을 확인하고 그에 맞게 Device를 조작하는 등의 복잡한 작업을 하기 때문에 Device를 먼저 알아보도록 하겠습니다.


    우선 main() 함수를 구성합니다. Platinum은 Network 통신을 위한 PLT_UPnP 객체를 제공합니다. 이 객체에 AddDevice() 함수를 통해 Device를 등록하면, UPnP 통신에 대한 것은 PLT_UPnP 객체가 담당해주고, Device는 자신의 임무만 열심히 하면 됩니다.


int main()
{
	PLT_UPnP upnp;
	upnp.Start();

	bool stop = false;
	do {
		const char c = fgetc(stdin);
		stop = (c == 'q' || c == 'Q');
	} while(!stop);

	upnp.Stop();
	return 0;
}


    위 코드를 통해 프로그램은 시작하면서 Network 통신을 준비하고, q 또는 Q 문자가 입력될 때까지 대기합니다. Platinum은 기본적으로 멀티 쓰레드로 동작하기 때문에, main() 함수에서 아무 동작을 하지 않아도 내부에서는 UPnP 통신을 위한 많은 동작을 하고 있습니다.


    이제 UPnP 통신은 준비를 했지만, 사용할 Device가 없습니다. 그래서 Device를 만들어 봅니다. Device는 PLT_DeviceHost 클래스를 상속을 받아서 구현을 합니다. 이 때, 주의할 점은 PLT_DeviceHost 클래스는 SetupServices() 라는 순수 가상함수를 포함하고 있습니다. 따라서 상속만 받아서는 객체가 생성되지 않습니다. SetupServices() 함수도 함께 구현을 해주어야 합니다. 코드는 아래와 같습니다.


class TutorialDevice :public PLT_DeviceHost
{
protected:
	virtual NPT_Result SetupServices(void)
	{
		return NPT_SUCCESS;
	}
};


    현재 위의 코드에서는 아무 일도 하지 않습니다. 왜냐하면 우리는 Service가 존재하지 않는 가장 단순한 Device를 만들기로 했기 때문 입니다. Service가 무엇인지는 다음 단계에서 알아보고 구현하겠습니다.

    Device가 만들어 졌으면 Device가 Network 상에서 동작할 수 있도록 PLT_UPnP 객체에 등록을 합니다. 등록 코드는 아래와 같습니다.


	...

	PLT_UPnP upnp;
	PLT_DeviceHostReference device(new TutorialDevice());
	upnp.AddDevice(device);
	upnp.Start();

	...


    이제 실행을 시켜 봅니다. 실행을 시키면 Device Spy 에 새로운 디바이스가 추가되는 것을 볼 수 있습니다. 하지만, 이름이 없는 Device 입니다. 너무 궁색해 보이니 TutorialDevice 클래스의 생성자에서 이름을 추가합니다.


	...

	TutorialDevice()
		: PLT_DeviceHost()
	{
		m_FriendlyName = "Tutorial Device";
	}

	...


    프로그램을 다시 실행 시킨 뒤에 Device Spy 프로그램의 Device 목록을 보면 "Tutorial Device"라는 이름의 새로운 Device를 볼 수 있습니다.

[Platinum UPnP] 101 :: (0) 소개




지난 1주일 간 Platinum UPnP라는 오픈소스를 만지작 거리고 있습니다. 가장 큰 이유는 회사 업무와 관련이 있기 때문입니다. 하지만, 그 이유만이 아니더라도 UPnP/DLNA 서비스는 더욱 확대 될 것으로 보이기 때문에 좀 더 제대로 알아두면 좋을 것 같습니다.

    다만, Platinum UPnP는 듀얼라이센스로 운영됩니다. 무료로 사용하시고자 하면 GPL이 적용되어 작업한 결과물의 소스를 모두 공개해야 합니다. GPL을 적용받기 싫다면, 유료 라이센스를 구매해서 사용하시면 됩니다. 언듯 듣기로 수천만원 한다고 하는 것 같던데, 잘은 모르겠네요.


    Platinum UPnP는 UPnP/DLNA 서비스를 만들기 위한 기반 코드를 제공합니다. 크로스 플랫폼 컴파일이 가능하며, 개인적으로 확인한 플랫폼은 Windows 7, Linux, Mac OS X 입니다. 소스를 다운받으면, Platinum UPnP의 라이브러리 빌드 환경과 몇몇 샘플들이 함께 포함되어 있습니다. 다만, 문서는 Doxygen 이외에는 보이지 않아서 약간 불친절 합니다. 하긴, 유료 라이센스에는 원래 기술지원 비용도 포함되니까 너무 친절한 문서를 제공해도 장사에 문제가 되려나요?


    일단 여기서는 UPnP/DLNA에 대한 자세한 내용은 다루지 않습니다. 저도 아직은 스펙을 보고 있는 중이기도 하고, 공식 홈페이지나 웹서핑을 통해 양질의 정보를 얻으실 수 있을거라 생각합니다. 


    이곳에서는 UPnP를 테스트 할 수 있는 툴이 제공됩니다. 무료입니다. 제공되는 프로그램들 중 Device Spy 라는 프로그램이 UPnP의 특징을 가장 잘 보여준다고 할 수 있겠네요. UPnP의 스펙문서들은 이곳에서 받아보실 수 있습니다.

#20130114-01 :: Ubuntu 12.04 LTS :: Atheros AR8161/8165 드라이버 설치

현재 회사에서 사용하는 리눅스 노트북에서 우분투를 설치하면,

유선 네트워크 드라이버가 자동으로 설치되지 않습니다.

네트워크 디바이스는 Atheros AR8161/8165 PCI-E Gigabit Ethernet Controller 입니다.

그래서 터미널 창에서 아래와 같이 명령을 입력하여 설치하였습니다.


$ sudo apt-get install linux-backports-modules-cw-3.4-precise-generic

$ sudo modprobe alx


[출처]