2015년 5월 28일 목요일

와이파이 다이렉트 - Android Wifi Direct(wifi peer-2-peer)

wifi direct 서비스 개발하기

샘플 프로젝트 : https://github.com/sunsoopark/Android_Wifi_direct_filetransfer

1. 개요

 와이파이 다이렉트(Wi-Fi Direct)는 wifi기기간 접속을 지원하는 AP를 거치지 않고 기기간 직접접으로 통신하는 것을 말한다.
안드로이드에서는 4.0(api 14) 이후부터 지원하고 있다. 관련 api를 이용할 경우 기기간 연결만 해줄뿐 데이터의 전송등의 작업은 소켓을 이용한 네트워크 통신프로그램을 구현 해야함


2. 주요 사용 패키지

android.net.wifi.p2p.WifiP2pManager

3. 필요 퍼미션

 Wifi p2p 기능을 사용하기 위한 기본적인 퍼미션
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

소켓 통신 위한 퍼미션
<uses-permission android:name="android.permission.INTERNET"/>


4. 연결 단계















위와같은 단계로 피어간에 연결이 이루어 지며 검색 방법에는 서비스로 검색하는 것과 단말을 검색하는 것 2가지로 나눌 수 있다. 서비스로 검색하는 것은 api 16이상부터 가능하다.
검색시 두 단말 모두 discover 해야 상호간에 확인 가능 하다.

5. 개발 단계별 설명
 가. p2p 서비스 객체 초기화
  다음과 같이 wifip2p 서비스 객체를 생성하고 채널 객체를 초기화 한다.  

 WifiP2pManager mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
WifiP2PManager.Channel p2pChannel = mWifiP2pManager.initialize(mContext, mContext.getMainLooper(), null);
나. 피어 검색
  검색관련 메소드의 콜백 결과값은 단순히 안드로이드 프레임워크에 해당 요청을 전송했다는 의미임, 해당 요청에 대한 결과를 알고 싶다면 브로드캐스트 리시버를 등록해야 함
해당 브로드 캐스트에 대한 액션은 다음과 같음
 [Wi-Fi P2P 활성화시]
  (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
 [ 연결가능한 피어 목록이 변경시(requestPeers() 구현)] 
  WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
 [ Wi-Fi P2P 연결상태(1:N연결을 위해 여기서 requestConnectionInfo() 호출 하도록 구현)]
  WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
 [ 현재 단말 상태정보]
  (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
1)단말 검색
WifiP2pManager 클래스의 discoverPeers 메소드 호출

mWifiP2pManager.discoverPeers(p2pChannel, new WifiP2pManager.ActionListener() {
    @Override    public void onSuccess() {
        Log.i(TAG, "discover success");
    }

    @Override    public void onFailure(int reason) {
        Log.i(TAG, "discover fail >>" + reason);    }
});
이후 피어가 검색되면 등록한 브로드캐스트 리시버로 인텐트 전달됨


BroadcastReceiver wifiP2PReceiver = new BroadcastReceiver() {
    @Override    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();       
        if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);          
            if (state == -1) return;           
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                Log.i(TAG, "WIFI_P2P_STATE_ENABLED");               
               requestPeers();            } else {
                Log.i(TAG, "WIFI_P2P_STATE_DISABLED");            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            Log.i(TAG, "WIFI_P2P_PEERS_CHANGED_ACTION");            //do request peer            
            requestPeers();            
            NetworkInfo netInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);            if (netInfo != null && netInfo.isConnected()) {
                Log.i(TAG, "device connected:: " + netInfo.isConnected());                
            mWifiP2pManager.requestConnectionInfo(p2pChannel, new WifiP2pManager.ConnectionInfoListener() {
                    @Override                   
                    public void onConnectionInfoAvailable(WifiP2pInfo info) {
                        mWifiP2pInfo = info;                    }
                });            }
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            Log.i(TAG, "WIFI_P2P_CONNECTION_CHANGED_ACTION");       
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            Log.i(TAG, "WIFI_P2P_THIS_DEVICE_CHANGED_ACTION");        }
    }
};

2) 서비스 검색
 가. 서비스 등록
Map record = new HashMap<String, String>();
record.put("listenport", Constants.FILE_SERVICE_PORT);
record.put("buddyname", "John Doe" + (int) (Math.random() * 1000));record.put("available", "visible");

WifiP2pServiceInfo info = WifiP2pDnsSdServiceInfo.newInstance("_tester", "ip_tcp", record);
mWifiP2pManager.addLocalService(p2pChannel, info, new WifiP2pManager.ActionListener() {
    @Override   
     public void onSuccess() {
        mCallback.onSucess("addLocalService success");        Log.v(TAG, "success");    }

    @Override    
     public void onFailure(int reason) {
        mCallback.onFailure(reason);        Log.v(TAG, "fail ::" + reason);    }
});
  나. 서비스 요청

WifiP2pManager.DnsSdTxtRecordListener txtListener = new WifiP2pManager.DnsSdTxtRecordListener() {
    @Override/* Callback includes: * fullDomain: full domain name: e.g "printer._ipp._tcp.local." * record: TXT record dta as a map of key/value pairs. * device: The device running the advertised service. */
    public void onDnsSdTxtRecordAvailable(
            String fullDomain, Map record, WifiP2pDevice device) {
        Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());        
        buddies.put(device.deviceAddress, (String) record.get("buddyname"));    }
};
WifiP2pManager.DnsSdServiceResponseListener serviceResponseListener = 
                                                                new WifiP2pManager.DnsSdServiceResponseListener() {
    @Override    
     public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice srcDevice) {
        Log.d(TAG, "service res available [" + instanceName + "/type:" + registrationType);    }
};

mWifiP2pManager.setDnsSdResponseListeners(p2pChannel, serviceResponseListener, txtListener);
          WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();//서비스 검색 요청 추가
mWifiP2pManager.addServiceRequest(p2pChannel,        serviceRequest,        new WifiP2pManager.ActionListener() {
            @Override            
             public void onSuccess() {
                Log.v(TAG, "sevice success");            }

            @Override            
            public void onFailure(int code) {
                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY               
              Log.v(TAG, "sevice fail :: " + code);
            }
        });//서비스 검색
  mWifiP2pManager.discoverServices(p2pChannel, new WifiP2pManager.ActionListener() {
    @Override    public void onSuccess() {
    }

    @Override    public void onFailure(int reason) {
    }
});

3. 피어간 연결
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
mWifiP2pManager.connect(p2pChannel, config, new WifiP2pManager.ActionListener() {
    @Override    public void onSuccess() {
        Log.v(TAG, "connect success");
        requestPeers();    }

    @Override    public void onFailure(int reason) {
        Log.v(TAG, "connect fail >>" + reason);    }
});

 이후의 동작은 서버/클라이언트소켓 연결하여 구현 하면 됨
1. 서버는 원하는 포트를 열고 클라이언트의 접속을 기다림

welcomeSocket = new ServerSocket(8988);
while (true) {

    socket = welcomeSocket.accept();    if(socket.isConnected()){
        break;    }
}
2. 클라이언트는 WifiP2pInfo객체에서 group owner의 ip주소를 가져와서 연결,
Wifip2pinfo 객체는 wifip2pManager 객체의 requestConnectionInfo() 호출하여 얻음

WifiP2pInfo wifiInfo = (WifiP2pInfo) intent.getParcelableExtra("host");InetAddress targetIP = null;
if (!wifiInfo.isGroupOwner) {
    targetIP = wifiInfo.groupOwnerAddress;}
try {
    socket.bind(null);    socket.connect(new InetSocketAddress(targetIP, 8988), 5000);
} 

2015년 1월 28일 수요일

Ibatis with ORA-1006 error, "bind variable doesn't exists"

Error : ORA-1006 error, "bind variable doesn't exists"

문제가 된 sql무니

<select id="id" parameterClass="hashmap">

Select something

From somewhere

Where case1

<dynamics>

<notnull prepend="and" property=value> columname like '%#value#%'

</dynamics>

원인 : 동적쿼리 작성하면서 변수를 #value#으로 선언

해결: $value$로 선언


##은 파라미터에 값을 대입

$$는 값을 쿼리문에 직접 입력