2014년 2월 27일 목요일

Android multicast packet send, receive

Android에서 multicast packet 전송, 수신을 다룰 때 특별한 권한 및 유의 사항이 있음.

[Multicast sending]

- DatagramSocket을 사용 (broadcast 설정, 올바른 port 설정)

channel = DatagramChannel.open();
channel.socket().setReuseAddress(true);
channel.socket().setBroadcast(true);
channel.socket().setSendBufferSize(UDP_BUFFER_SIZE);

- 올바른 주소로 DatagramPacket 전송
  : wifiap (hotspot)일 경우 broadcast 주소(interface info에 있는)로 전송
  : 그 외 multicast 주소로 전송

아래 [multicast sending, receiving mini sample] 참고


[Multicast receiving]

- WifiManager.MulticastLock 설정 및 CHANGE_WIFI_MULTICAST_STATE permission

아래 [multicast 수신 설정] 참고

- MulticastSocket 사용 및 올바른 그룹주소와 interface에 대해 joinGroup()

InetSocketAddress socketAddress = new InetSocketAddress(addr, port);
socket = new MulticastSocket(port);
socket.setReceiveBufferSize(bufsize);

Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();

while (ifs.hasMoreElements()) {
  NetworkInterface xface = ifs.nextElement();
  Enumeration<InetAddress> addrs = xface.getInetAddresses();
  String name = xface.getName();

  if (false == name.startsWith("wlan")) {
  continue;
}

Log.e(LOG_TAG, "Listener Adding " + name + " to our interface set");
socket.joinGroup(socketAddress, xface);



참고 링크들

[multicast 사용 시 주의 점 및 address 지정 내용]
: https://plus.google.com/+Chainfire/posts/9NMemrKYnCd

To receive multicasts you need to do the following:
- use a MulticastSocket instead of a DatagramSocket
- MulticastSocket::joinGroup() the correct group
- hold a WifiManager.MulticastLock
- have the CHANGE_WIFI_MULTICAST_STATE permission

- Cell data and Wi-Fi connected: 255.255.255.255 works
- Cell data and Wi-Fi in hotspot mode: 255.255.255.255 works
- only Wi-Fi in hotspot mode: 255.255.255.255 doesn't work, throws network unreachable exception. Using the correct broadcast address works fine.



[multicast 수신 설정]
: http://codeisland.org/2012/udp-multicast-on-android/

Acquiring a lock
Actually getting the required lock isn’t that much trouble at all. Given that you declared your application to use the following permissions:

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

The code-part is as short as:

WifiManager wifi = (WifiManager)getSystemService( Context.WIFI_SERVICE );
if(wifi != null){
    WifiManager.MulticastLock lock = wifi.createMulticastLock("Log_Tag");
    lock.acquire();
}

[WifiManager.MulticastLock]
: http://developer.android.com/reference/android/net/wifi/WifiManager.MulticastLock.html

Allows an application to receive Wifi Multicast packets. Normally the Wifi stack filters out packets not explicitly addressed to this device. Acquring a MulticastLock will cause the stack to receive packets addressed to multicast addresses. Processing these extra packets can cause a noticable battery drain and should be disabled when not needed.


[multicast sending, receiving mini sample]
: https://code.google.com/p/boxeeremote/wiki/AndroidUDP

Getting the Broadcast Address

You need to access the wifi manager to get the DHCP info and construct a broadcast address from that:
InetAddress getBroadcastAddress() throws IOException {
    WifiManager wifi = mContext.getSystemService(Context.WIFI_SERVICE);
    DhcpInfo dhcp = wifi.getDhcpInfo();
    // handle null somehow

    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
      quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
}


* 근데 하다 보니 DhcpInfo 의 값이 모두 0인 경우가 있어서
interface 정보에서 broadcast 주소 획득하여 사용.

List<InterfaceAddress> addrs = xface.getInterfaceAddresses();String name = xface.getName();

if (false == name.startsWith("wlan")) { continue;}

for(InterfaceAddress addr : addrs){ if(addr.getAddress() instanceof Inet6Address){ continue;} Log.d(LOG_TAG, name + " has addr " + addr.getBroadcast());}



Sending and Receiving UDP Broadcast Packets

Having constructed the broadcast address, things work as normal. The following code would send the string data over broadcast and then wait for a response:
DatagramSocket socket = new DatagramSocket(PORT);
socket.setBroadcast(true);
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
    getBroadcastAddress(), DISCOVERY_PORT);
socket.send(packet);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);

[broadcast 주소 획득]
: http://stackoverflow.com/questions/15524593/broadcasting-over-wi-fi-direct

Try getting you network connection this way:
InetAddress getBroadcastAddress() throws IOException {
    WifiManager wifi = mContext.getSystemService(Context.WIFI_SERVICE);
    DhcpInfo dhcp = wifi.getDhcpInfo();
    // handle null somehow

    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
      quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
}  
Then try sending a packet this way:
DatagramSocket socket = new DatagramSocket(PORT);
socket.setBroadcast(true);
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
    getBroadcastAddress(), PORT);
socket.send(packet);

// If you want to listen for a response ...
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
Edit: From same page to read try this ...
WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
MulticastLock lock = wifi.createMulticastLock("dk.aboaya.pingpong");
lock.acquire();
serverSocket = new DatagramSocket(19876);
serverSocket.setSoTimeout(15000); //15 sec wait for the client to connect
byte[] data = new byte[UDPBatPositionUpdater.secretWord.length()]; 
DatagramPacket packet = new DatagramPacket(data, data.length);
serverSocket.receive(packet);
lock.release();
String s = new String(packet.getData());
System.out.println(s);
Remember, that you need the following permission for it to work:
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
share|improve this answer

2014년 2월 24일 월요일

C++11 Feature 정리 - Lambda

Code Project의 "Ten C++11 Features Every C++ Developer Should Use"를 기반으로 
C++11 feature들에 대해서 대충 정리 함.

http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer

[Lambda Expression]

- Lambda expression은 보통 a lambda function과 lambda를 지칭한다. 
- anonymous function/function object를 정의 & 사용하기 위한 간략한 표기법
- algorithms이나 logic에서 operation을 argument로 전달할때 유용함. (callback과 같이)

Syntax


: http://en.cppreference.com/w/cpp/language/lambda


[ capture ] ( params ) mutable exception attribute -> ret { body }(1)
[ capture ] ( params ) -> ret { body }(2)
[ capture ] ( params ) { body }(3)
[ capture ] { body }(4)
1) Full declaration
2) Declaration of a const lambda: the objects captured by copy cannot be modified.
3) Omitted trailing-return-type: the return type of the closure's operator() is deduced according to the following rules:
  • if the body consists of the single return statement, the return type is the type of the returned expression (after rvalue-to-lvalue, array-to-pointer, or function-to-pointer implicit conversion)
  • otherwise, the return type is void
4) Omitted parameter list: function takes no arguments, as if the parameter list was ()

Explanation

mutable-allows body to modify the parameters captured by copy, and to call their non-const member functions
exception-provides the exception specification or the noexcept clause for operator() of the closure type
attribute-provides the attribute specification for operator() of the closure type
capture-specifies which symbols visible in the scope where the function is declared will be visible inside the function body.
A list of symbols can be passed as follows:
  • [a,&b] where a is captured by value and b is captured by reference.
  • [this] captures the this pointer by value
  • [&] captures all automatic variables mentioned in the body of the lambda by reference
  • [=] captures all automatic variables mentioned in the body of the lambda by value
  • [] captures nothing
params-The list of parameters, as in named functions
ret-Return type. If not present it's implied by the function return statements ( or void if it doesn't return any value)
body-Function body

[Example]
: http://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11

기존 
#include <algorithm>
#include <vector>

namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}

void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

Lambda 사용 시
void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

사실 한곳이나 한번만 사용하는 일회성 function object인 경우 굳이 별도로 정의할 필요가 없어
사용되는 algorithms의 arguments로서 간략히 정의 하고자 하는 것이 장점. 

trailing return type 형태로 return type 명시 예
void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) -> double {
                   if (d < 0.0001) {
                     return 0;
                   }
                   else {
                     return d;
                   }
                 });
}

[Capture]
 : lambda body내에서 사용할 argument 명시

- lambda는 inline 형태의 이름 없는 function/ function object로 어디서나 사용되어야 하므로 내부 변수를 참조 할 수 없다. 

void func5(std::vector<double>& v, const double& epsilon) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [epsilon](double d) -> double {
                   if (d < epsilon) {
                     return 0;
                   }
                   else {
                     return d;
                   }
                 });
}
You can capture by both reference and value, which you can specify using = and &:
  • [&epsilon] capture by reference
  • [&, epsilon] specify that the default way of capturing is by reference and what we want to capture
  • [=, &epsilon] capture by value by default, but for epsilon use reference instead

- [] 비어 있는 empty list
- [capture list] : explicit capture
- &, = 를 사용하여 capture by reference , capture by value 지정 가능
 . [&] : implicitly capture by reference
 . [=] : implicitly capture by value
 . [&, capture list]
   : implicitly capture by reference all local variables with names not mentioned in the list. 
   : capture list에 this는 포함 될 수 있음.
 . [=, capture list] : 
   : implicitly capture by value all local variables with names not mentioned in the list.
   : capture list에 this는 포함 될 수 없음.



ret operator()(params) const { body }
(the keyword mutable was not used)
ret operator()(params) { body }
(the keyword mutable was used)

- 기본적으로 Lambda의 body는 operator()() prototype을 가지며 기본이 const이다. 
 : 만약 내부에서 captured variables를 수정하고 싶다면 mutable 속성을 지정해 주어야 한다.

void algo(vector<int>& v)
{
  int count = v.size();
  std::generate(v.begin(), v.end(),
     [count]()mutable{ return --count; }
  };
}

[Lambda and Lifetime]

lambda가 caller보다 lifetime이 길 경우 lambda에서 참조하는 arguments들의 확인이 필요함.

void setup(Menu& m)
{
  Point p1, p2, p3;
  m.add("draw triangle", [&]{m.draw(p1,p2,p3); });  // probable disater
}
위 예제에는 p1, p2, p3가 reference로 참조 되는 상태에서 lambda가 나중에 사용될 목적으로 저장 되는 상황이라 문제 발생 가능함. 이때는 reference & 에서 copy = 로 사용하는 것이 맞다.

- Lambda에서 name space를 capture할 필요는 없음.

- Lambda의 argument로 this를 사용할 수 있음. this는 call by reference로 참조되어 class instance의 member variable을 참조할 수 있음. 단 multi-threaded program에서 race condition 문제를 유발 가능.

[Call and Return]
- lambda가 argument를 받지 않는다면 생략 가능, 가장 minimal lambda expression = []{}
- lambda가 return type을 명시하지 않는다면 body를 통해 판단함. return이 있다면 return value의 type, 없다면 void

void g(double y)
{
  [&]{ f(y); }  // void
  auto z1 = [=](int x){return x+y}  // double
  auto z2 = [=,y]{ if (y) return 1; else return 2;}  // error : body is too complicated for return type deduction
  auto z3 = [y]() { return 1: 2;} // int
  auto z4 = [=,y]()->int { if (y) return 1; else return 2; }  // explicit return type
}

- lambda는 template instantiation mechanism으로 인해 동일 type의 lambda가 동시에 사용될 수 없음.

* 일부 소스는 "The C++ Programming Language - Bjarne Stroustrup"에 포함된 예제 입니다.