WIFIDirectStart.zip


"기적을 부르는 안드로이드 통신 프로그래밍" 으로 공부한 내용 정리입니다.


1. 와이파이 다이렉트란 ? 

2. 와이파이 개발 일반 

  2-1) 와이파이 스캔 

  2-2) 와이파이 환경 설정

  2-3) 와이파이 네트워크 연결

 

3. 와이파이 다이렉트 개발 

  3-1) 일반

  3-2) 서비스 검색 

  3-3) 와이파이 락 과 멀티캐스트 설정 




와이파이 작업단계


1. 단말기 검색 

  단말기끼리 서로 연결하기 전에 먼저 서로 단말기를 검색하고 연결하고자 하는 단말기를 서로 인식해야한다. 

  이때 데이터 링크 계층의 "프로브" 라는 프레임을 사용한다. 

  예를들어 단말기 설정내 "WI-FI Direct" 를 누르면, 단말기는 프로브라는 요청 프레임으로 주변에 뿌려 와이파이 다이렉트와 관련된 정보를 

  요청한다.

  요청 프레임과 응답 프레임 모두 기본 서비스 집합(SSID) 를 "DIRECT" 란 이름을 사용한다. 단말기들은 요청 프레임과 응답 프레임으로 

  단말기 이름이나 와이파이 다이렉트 모드등의 기본적인 정보를 교환한다.


2. 그룹 생성과 오너 결정

  네트워크 그룹을 형성한 단말기들 가운데 하나의 단말기를 선정하여 소프트웨어 무선 접속 장치(soft-AP) 로 만든다.
  이 무선 접속 장치가 다른 단말기들과 연결하고 통신의 시작과 종료의 역할을 수행하는 그룹 오너가 된다. 

  그룹오너는 무선 접속 장치의 기능을 수행하며, 네트웤 그룹을 통제하는 역할을 한다.

  따라서 와이파이 다이렉트의 기능을 만들려면, 반드시 다른 와이파이 다이렉트 장치와 협상하여 그룹 오너를 형성하는 기능을 

  제공해야한다. 이러한 기능을 영어로 "Group Owner Negotiation procedure' 이라 한다.

  이 외에도 검색 및 전원 관리 매커니즘을 반드시 지원해야한다.

  다음 두가지 방법으로 단말기들간의 그룹을 형성하고 오너를 선정한다.

  

  첫번째. 시스템에 위임 

  두번째. 직접 그룹 생성


3. 사용자 인증  

  일단 단말기는 네트워크 그룹에 가입하려면, 무선 접속 장치에 연결하는 방법과 동일하게 그룹 오너에 연결 요청을 수행한다. 

  그리고 그에 대해 그룹 오너는 대부분 별도 사용자 인증 작업을 위한 패스워드를 요구하기 보다 승인 버튼을 사용하여 인증한다.

  이러한 기능을 PBC(Push Button Configuration) 이라 표현한다.


4. IP 주소 배정 

  와이파이는 블루투스와 달리 IP 주소를 사용하여 , 클라이언트와 통신한다. 따와이파이 다이렉트에서 그룹 오너가 결정되면 

  일반 무선 접속 장치처럼 자신에 접속한 클라이언트에 IP 주소를 배포한다. 

  그룹 오너는 클라이언트와 일대 다수로 연결되기 때문에 대부분 자신이 서버소켓을 생성한다.


아래 코드에 대한 시나리오는 다음과 같다.

  

 1) WIFIP2pManager 클래스의 인스턴스 생성

 2) 와이파이 다이렉트 초기화

 3) 와이파이 다이렉트 디바이스의 검색

 4) 브로드캐스트 리시버 등록

 5) 검색한 와이파이 다이렉트 디바이스 얻기

 6) 와이파이 다이렉트 연결 요청 

 7) 서버소켓이나 소켓 객체 생성 

 8) 비지니스 로직 

 9) 와이파이 다이렉트 연결 종료 






전반적인 작동 순서에 대한 모식도를 그려보았다.




<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 

    package="com.example.android.wifidirect"

    android:versionCode="1" android:versionName="1.0">


    <uses-sdk android:minSdkVersion="14" />

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

    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

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

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

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


    <!-- Google Play filtering --> 

    <uses-feature android:name="android.hardware.wifi.direct" android:required="true"/>


    <application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@android:style/Theme.Holo">

        <activity

            android:name=".WiFiDirectActivity"

            android:label="@string/app_name" android:launchMode="singleTask">

            <intent-filter>

                <action

                    android:name="android.intent.action.MAIN" />

                <category

                    android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>


        <!-- Used for transferring files  after a successful connection -->

        <service android:enabled="true" android:name=".FileTransferService" />


    </application>

</manifest>



WiFiDirectActivity 

import com.example.android.wifidirect.DeviceListFragment.DeviceActionListener;

public class WiFiDirectActivity extends Activity implements ChannelListener, DeviceActionListener {


    public static final String TAG = "wifidirectdemo";

    private WifiP2pManager manager;

    private boolean isWifiP2pEnabled = false;

    private boolean retryChannel = false;


    private final IntentFilter intentFilter = new IntentFilter();

    private Channel channel;

    private BroadcastReceiver receiver = null;


    /**

     * @param isWifiP2pEnabled the isWifiP2pEnabled to set

     */

    public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) {

        this.isWifiP2pEnabled = isWifiP2pEnabled;

    }


    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);


        // add necessary intent values to be matched.

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);


        manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);

        channel = manager.initialize(this, getMainLooper(), null);

    }


    /** register the BroadcastReceiver with the intent values to be matched */

    @Override

    public void onResume() {

        super.onResume();

        receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);

        registerReceiver(receiver, intentFilter);

    }


    @Override

    public void onPause() {

        super.onPause();

        unregisterReceiver(receiver);

    }


    /**

     * Remove all peers and clear all fields. This is called on

     * BroadcastReceiver receiving a state change event.

     */

    public void resetData() {

        DeviceListFragment fragmentList = (DeviceListFragment) getFragmentManager()

                .findFragmentById(R.id.frag_list);

        DeviceDetailFragment fragmentDetails = (DeviceDetailFragment) getFragmentManager()

                .findFragmentById(R.id.frag_detail);

        if (fragmentList != null) {

            fragmentList.clearPeers();

        }

        if (fragmentDetails != null) {

            fragmentDetails.resetViews();

        }

    }


    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        MenuInflater inflater = getMenuInflater();

        inflater.inflate(R.menu.action_items, menu);

        return true;

    }


    /*

     * (non-Javadoc)

     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)

     */

    @Override

    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {

            case R.id.atn_direct_enable:

                if (manager != null && channel != null) {

                    // Since this is the system wireless settings activity, it's

                    // not going to send us a result. We will be notified by

                    // WiFiDeviceBroadcastReceiver instead.

                    startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));

                } else {

                    Log.e(TAG, "channel or manager is null");

                }

                return true;


            case R.id.atn_direct_discover:

                if (!isWifiP2pEnabled) {

                    Toast.makeText(WiFiDirectActivity.this, R.string.p2p_off_warning,

                            Toast.LENGTH_SHORT).show();

                    return true;

                }

                final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()

                        .findFragmentById(R.id.frag_list);

                fragment.onInitiateDiscovery();

                manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {


                    @Override

                    public void onSuccess() {

                        Toast.makeText(WiFiDirectActivity.this, "Discovery Initiated",

                                Toast.LENGTH_SHORT).show();

                    }


                    @Override

                    public void onFailure(int reasonCode) {

                        Toast.makeText(WiFiDirectActivity.this, "Discovery Failed : " + reasonCode,

                                Toast.LENGTH_SHORT).show();

                    }

                });

                return true;

            default:

                return super.onOptionsItemSelected(item);

        }

    }


    @Override

    public void showDetails(WifiP2pDevice device) {

        DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()

                .findFragmentById(R.id.frag_detail);

        fragment.showDetails(device);


    }


    @Override

    public void connect(WifiP2pConfig config) {

        manager.connect(channel, config, new ActionListener() {

            @Override

            public void onSuccess() {

                // WiFiDirectBroadcastReceiver will notify us. Ignore for now.

            }


            @Override

            public void onFailure(int reason) {

                Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",

                        Toast.LENGTH_SHORT).show();

            }

        });

    }


    @Override

    public void disconnect() {

        final DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()

                .findFragmentById(R.id.frag_detail);

        fragment.resetViews();

        manager.removeGroup(channel, new ActionListener() {


            @Override

            public void onFailure(int reasonCode) {

                Log.d(TAG, "Disconnect failed. Reason :" + reasonCode);

            }


            @Override

            public void onSuccess() {

                fragment.getView().setVisibility(View.GONE);

            }

        });

    }


    @Override

    public void onChannelDisconnected() {

        // we will try once more

        if (manager != null && !retryChannel) {

            Toast.makeText(this, "Channel lost. Trying again", Toast.LENGTH_LONG).show();

            resetData();

            retryChannel = true;

            manager.initialize(this, getMainLooper(), this);

        } else {

            Toast.makeText(this,

                    "Severe! Channel is probably lost premanently. Try Disable/Re-Enable P2P.",

                    Toast.LENGTH_LONG).show();

        }

    }


    @Override

    public void cancelDisconnect() {


        if (manager != null) {

            final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()

                    .findFragmentById(R.id.frag_list);

            if (fragment.getDevice() == null

                    || fragment.getDevice().status == WifiP2pDevice.CONNECTED) {

                disconnect();

            } else if (fragment.getDevice().status == WifiP2pDevice.AVAILABLE

                    || fragment.getDevice().status == WifiP2pDevice.INVITED) {


                manager.cancelConnect(channel, new ActionListener() {


                    @Override

                    public void onSuccess() {

                        Toast.makeText(WiFiDirectActivity.this, "Aborting connection",

                                Toast.LENGTH_SHORT).show();

                    }


                    @Override

                    public void onFailure(int reasonCode) {

                        Toast.makeText(WiFiDirectActivity.this,

                                "Connect abort request failed. Reason Code: " + reasonCode,

                                Toast.LENGTH_SHORT).show();

                    }

                });

            }

        }


    }

}



WiFiDirectBroadcastReceiver 



public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

    private WifiP2pManager manager;
    private Channel channel;
    private WiFiDirectActivity activity;

  
    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
            WiFiDirectActivity activity) {
        super();
        this.manager = manager;
        this.channel = channel;
        this.activity = activity;
    }


    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {

            // UI update to indicate wifi p2p status.
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                // Wifi Direct mode is enabled
                activity.setIsWifiP2pEnabled(true);
            } else {
                activity.setIsWifiP2pEnabled(false);
                activity.resetData();

            }
            Log.d(WiFiDirectActivity.TAG, "P2P state changed - " + state);
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

            // request available peers from the wifi p2p manager. This is an
            // asynchronous call and the calling activity is notified with a
            // callback on PeerListListener.onPeersAvailable()
            if (manager != null) {
                manager.requestPeers(channel, (PeerListListener) activity.getFragmentManager()
                        .findFragmentById(R.id.frag_list));
            }
            Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

            if (manager == null) {
                return;
            }

            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);

            if (networkInfo.isConnected()) {

                // we are connected with the other device, request connection
                // info to find group owner IP

                DeviceDetailFragment fragment = (DeviceDetailFragment) activity
                        .getFragmentManager().findFragmentById(R.id.frag_detail);
                manager.requestConnectionInfo(channel, fragment);
            } else {
                // It's a disconnect
                activity.resetData();
            }
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
                    .findFragmentById(R.id.frag_list);
            fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
                    WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));

        }
    }
}


DeviceListFragment 


public class DeviceListFragment extends ListFragment implements PeerListListener {


    private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();

    ProgressDialog progressDialog = null;

    View mContentView = null;

    private WifiP2pDevice device;


    @Override

    public void onActivityCreated(Bundle savedInstanceState) {

        super.onActivityCreated(savedInstanceState);

        this.setListAdapter(new WiFiPeerListAdapter(getActivity(), R.layout.row_devices, peers));

    }


    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        mContentView = inflater.inflate(R.layout.device_list, null);

        return mContentView;

    }

    public WifiP2pDevice getDevice() {

        return device;

    }


    private static String getDeviceStatus(int deviceStatus) {

        Log.d(WiFiDirectActivity.TAG, "Peer status :" + deviceStatus);

        switch (deviceStatus) {

            case WifiP2pDevice.AVAILABLE:

                return "Available";

            case WifiP2pDevice.INVITED:

                return "Invited";

            case WifiP2pDevice.CONNECTED:

                return "Connected";

            case WifiP2pDevice.FAILED:

                return "Failed";

            case WifiP2pDevice.UNAVAILABLE:

                return "Unavailable";

            default:

                return "Unknown";


        }

    }


    @Override

    public void onListItemClick(ListView l, View v, int position, long id) {

        WifiP2pDevice device = (WifiP2pDevice) getListAdapter().getItem(position);

        ((DeviceActionListener) getActivity()).showDetails(device);

    }


    private class WiFiPeerListAdapter extends ArrayAdapter<WifiP2pDevice> {


        private List<WifiP2pDevice> items;

        public WiFiPeerListAdapter(Context context, int textViewResourceId,

                List<WifiP2pDevice> objects) {

            super(context, textViewResourceId, objects);

            items = objects;


        }


        @Override

        public View getView(int position, View convertView, ViewGroup parent) {

            View v = convertView;

            if (v == null) {

                LayoutInflater vi = (LayoutInflater) getActivity().getSystemService(

                        Context.LAYOUT_INFLATER_SERVICE);

                v = vi.inflate(R.layout.row_devices, null);

            }

            WifiP2pDevice device = items.get(position);

            if (device != null) {

                TextView top = (TextView) v.findViewById(R.id.device_name);

                TextView bottom = (TextView) v.findViewById(R.id.device_details);

                if (top != null) {

                    top.setText(device.deviceName);

                }

                if (bottom != null) {

                    bottom.setText(getDeviceStatus(device.status));

                }

            }


            return v;


        }

    }


    public void updateThisDevice(WifiP2pDevice device) {

        this.device = device;

        TextView view = (TextView) mContentView.findViewById(R.id.my_name);

        view.setText(device.deviceName);

        view = (TextView) mContentView.findViewById(R.id.my_status);

        view.setText(getDeviceStatus(device.status));

    }


    @Override

    public void onPeersAvailable(WifiP2pDeviceList peerList) {

        if (progressDialog != null && progressDialog.isShowing()) {

            progressDialog.dismiss();

        }

        peers.clear();

        peers.addAll(peerList.getDeviceList());

        ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();

        if (peers.size() == 0) {

            Log.d(WiFiDirectActivity.TAG, "No devices found");

            return;

        }


    }


    public void clearPeers() {

        peers.clear();

        ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();

    }


    public void onInitiateDiscovery() {

        if (progressDialog != null && progressDialog.isShowing()) {

            progressDialog.dismiss();

        }

        progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel", "finding peers", true,

                true, new DialogInterface.OnCancelListener() {


                    @Override

                    public void onCancel(DialogInterface dialog) {

                        

                    }

                });

    }

    public interface DeviceActionListener {


        void showDetails(WifiP2pDevice device);


        void cancelDisconnect();


        void connect(WifiP2pConfig config);


        void disconnect();

    }


}



DeviceDetailFragment


public class DeviceDetailFragment extends Fragment implements ConnectionInfoListener {


    protected static final int CHOOSE_FILE_RESULT_CODE = 20;

    private View mContentView = null;

    private WifiP2pDevice device;

    private WifiP2pInfo info;

    ProgressDialog progressDialog = null;


    @Override

    public void onActivityCreated(Bundle savedInstanceState) {

        super.onActivityCreated(savedInstanceState);

    }


    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        mContentView = inflater.inflate(R.layout.device_detail, null);

        Button b = (Button) mContentView.findViewById(R.id.btn_connect);

        b.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                WifiP2pConfig config = new WifiP2pConfig();

                config.deviceAddress = device.deviceAddress;

                config.wps.setup = WpsInfo.PBC;

                if (progressDialog != null && progressDialog.isShowing()) {

                    progressDialog.dismiss();

                }

                progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel",

                        "Connecting to :" + device.deviceAddress, true, true

//                        new DialogInterface.OnCancelListener() {

//

//                            @Override

//                            public void onCancel(DialogInterface dialog) {

//                                ((DeviceActionListener) getActivity()).cancelDisconnect();

//                            }

//                        }

                        );

                ((DeviceActionListener) getActivity()).connect(config);


            }

        });


        mContentView.findViewById(R.id.btn_disconnect).setOnClickListener(

                new View.OnClickListener() {

                    @Override

                    public void onClick(View v) {

                        ((DeviceActionListener) getActivity()).disconnect();

                    }

                });


        mContentView.findViewById(R.id.btn_start_client).setOnClickListener(

                new View.OnClickListener() {

                    @Override

                    public void onClick(View v) {

                        // Allow user to pick an image from Gallery or other

                        // registered apps

                     ACTION_GET_CONTENT 설명:   http://androidhuman.tistory.com/269

                        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

                        intent.setType("image/*");

                        startActivityForResult(intent, CHOOSE_FILE_RESULT_CODE);

                    }

                });


        return mContentView;

    }


   onActivityResult 설명 :   http://blog.naver.com/hisukdory/50088038280

    @Override

    public void onActivityResult(int requestCode, int resultCode, Intent data) {

        Uri uri = data.getData();

        TextView statusText = (TextView) mContentView.findViewById(R.id.status_text);

        statusText.setText("Sending: " + uri);

        Log.d(WiFiDirectActivity.TAG, "Intent----------- " + uri);

        Intent serviceIntent = new Intent(getActivity(), FileTransferService.class);

        serviceIntent.setAction(FileTransferService.ACTION_SEND_FILE);

        serviceIntent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString());

        serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS,

                info.groupOwnerAddress.getHostAddress());

        serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8988);

        getActivity().startService(serviceIntent);

    }


    @Override

    public void onConnectionInfoAvailable(final WifiP2pInfo info) {

        if (progressDialog != null && progressDialog.isShowing()) {

            progressDialog.dismiss();

        }

        this.info = info;

        this.getView().setVisibility(View.VISIBLE);


        // The owner IP is now known.

        TextView view = (TextView) mContentView.findViewById(R.id.group_owner);

        view.setText(getResources().getString(R.string.group_owner_text)

                + ((info.isGroupOwner == true) ? getResources().getString(R.string.yes)

                        : getResources().getString(R.string.no)));


        // InetAddress from WifiP2pInfo struct.

        view = (TextView) mContentView.findViewById(R.id.device_info);

        view.setText("Group Owner IP - " + info.groupOwnerAddress.getHostAddress());


        // After the group negotiation, we assign the group owner as the file

        // server. The file server is single threaded, single connection server

        // socket.

      TextView v = (TextView) mContentView.findViewById(R.id.status_text);

        if (info.groupFormed && info.isGroupOwner) {

            new FileServerAsyncTask(getActivity(), v).execute();

        } else if (info.groupFormed) {

            // The other device acts as the client. In this case, we enable the get file button.

            mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);

            v.setText(getResources().getString(R.string.client_text));

        }


        // hide the connect button

        mContentView.findViewById(R.id.btn_connect).setVisibility(View.GONE);

    }


    public void showDetails(WifiP2pDevice device) {

        this.device = device;

        this.getView().setVisibility(View.VISIBLE);

        TextView view = (TextView) mContentView.findViewById(R.id.device_address);

        view.setText(device.deviceAddress);

        view = (TextView) mContentView.findViewById(R.id.device_info);

        view.setText(device.toString());


    }


    public void resetViews() {

        mContentView.findViewById(R.id.btn_connect).setVisibility(View.VISIBLE);

        TextView view = (TextView) mContentView.findViewById(R.id.device_address);

        view.setText(R.string.empty);

        view = (TextView) mContentView.findViewById(R.id.device_info);

        view.setText(R.string.empty);

        view = (TextView) mContentView.findViewById(R.id.group_owner);

        view.setText(R.string.empty);

        view = (TextView) mContentView.findViewById(R.id.status_text);

        view.setText(R.string.empty);

        mContentView.findViewById(R.id.btn_start_client).setVisibility(View.GONE);

        this.getView().setVisibility(View.GONE);

    }


AsyncTask

 1. 비동기 처리를 하기 위해서는 별도의 Thread 를 생성하여 public void run()  메소드를 구현하면 되지만 안드로이드에서는 

 직접 Thread 를 생성하기 보다는 AsyncTask 를 사용하길 권장합니다.

 AsyncTask 내부에는 자체 ThreadPool 이 있어 Thread 가 무한정 늘어나 메모리에 부담을 주지 않도록 관리 하고 있기 때문에

 따로 ThreadPool 을 관리하지 않는다면 AsyncTask 를 사용하는게 무난할 것 같습니다.

 doInBackground 메소드는 기존의 Thread 에서의 run() 메소드라고 보시면 됩니다.

 

 2.안드로이드는 UI Thread 에서 일정 시간동안 유저의 반응에 응답하지 못하면 ANR(Application Not Responding) 오류를

 발생시킨다. 하지만 네트워크를 통한 데이터 전송 등 계속적인 작업이 실행되어야 할 경우 AsyncTask를 상속받은 

 클래스를 통해서 작업을 수행하는 것으로 ANR 오류를 방지할 수 있다.

    

     public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> {


        private final Context context;

        private final TextView statusText;


        public FileServerAsyncTask(Context context, View statusText) {

            this.context = context;

            this.statusText = (TextView) statusText;

        }


        @Override

        protected String doInBackground(Void... params) {

            try {

                ServerSocket serverSocket = new ServerSocket(8988);

                Log.d(WiFiDirectActivity.TAG, "Server: Socket opened");

                Socket client = serverSocket.accept();

                Log.d(WiFiDirectActivity.TAG, "Server: connection done");

                final File f = new File(Environment.getExternalStorageDirectory() + "/"

                        + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis()

                        + ".jpg");


                File dirs = new File(f.getParent());

                if (!dirs.exists())

                    dirs.mkdirs();

                f.createNewFile();


                Log.d(WiFiDirectActivity.TAG, "server: copying files " + f.toString());

                InputStream inputstream = client.getInputStream();

                copyFile(inputstream, new FileOutputStream(f));

                serverSocket.close();

                return f.getAbsolutePath();

            } catch (IOException e) {

                Log.e(WiFiDirectActivity.TAG, e.getMessage());

                return null;

            }

        }


        @Override

        protected void onPostExecute(String result) {

            if (result != null) {

                statusText.setText("File copied - " + result);

                Intent intent = new Intent();

                intent.setAction(android.content.Intent.ACTION_VIEW);

                intent.setDataAndType(Uri.parse("file://" + result), "image/*");

                context.startActivity(intent);

            }


        }


        @Override

        protected void onPreExecute() {

            statusText.setText("Opening a server socket");

        }


    }


    public static boolean copyFile(InputStream inputStream, OutputStream out) {

        byte buf[] = new byte[1024];

        int len;

        try {

            while ((len = inputStream.read(buf)) != -1) {

                out.write(buf, 0, len);


            }

            out.close();

            inputStream.close();

        } catch (IOException e) {

            Log.d(WiFiDirectActivity.TAG, e.toString());

            return false;

        }

        return true;

    }


}



FileTransferService


IntentService

이 클래스는 Service 클래스의 서브 클래스이며, 하나의 워커 쓰레드를 만들어서 요청들을 한번에 하나씩 처리하도록 합니다. 만약 여러개의 요청을 동시에 처리해야만 하는 상황이 아니라면, 이 클래스를 사용하는 것이 가장 좋은 선택일 것입니다. 이 클래스를 상속받아 확장할 때는 onHandleIntent()만 구현하면 되는데, 이 콜백 메소드는 워커 쓰레드에서 실행되기 때문에, 매개변수로 인텐트를 받아서 그에 해당하는 요청을 처리하는 것만 구현해 주면 됩니다. 

IntentService는 아래와 같은 일을 합니다:

워커 쓰레드를 만든 후, onStartCommand()를 통해 전달받은 인텐트들을 워커 쓰레드에서 차근차근 실행합니다.

작업큐를 만들어서 전달받은 인텐트들을 넣어놨다가 한번에 하나씩 onHandleIntent()에게 넘겨주기 때문에, 멀티쓰레드 구현에 

대한 고민을 하지 않아도 됩니다.

모든 요청에 대한 처리가 끝나면 종료되도록 이미 구현되어 있기 때문에, 확장한 클래스에서 stopSelf()를 호출할 필요가 없습니다.

onBind() 콜백 메소드는 기본적으로 null을 리턴하도록 되어 있습니다.

onStartCommand() 콜백 메소드는 기본적으로 전달 받은 인텐트를 작업큐에 넣는 일을 합니다.

이러한 사실들에 비춰봤을때, 확장 클래스에서는 onHandleIntent()만 구현하면 됩니다.

http://arabiannight.tistory.com/247


public class FileTransferService extends IntentService {

    private static final int SOCKET_TIMEOUT = 5000;

    public static final String ACTION_SEND_FILE = "com.example.android.wifidirect.SEND_FILE";

    public static final String EXTRAS_FILE_PATH = "file_url";

    public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";

    public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";


    public FileTransferService(String name) {

        super(name);

    }


    public FileTransferService() {

        super("FileTransferService");

    }


    @Override

    protected void onHandleIntent(Intent intent) {


        Context context = getApplicationContext();

        if (intent.getAction().equals(ACTION_SEND_FILE)) {

            String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH);

            String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);

            Socket socket = new Socket();

            int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);


            try {

                Log.d(WiFiDirectActivity.TAG, "Opening client socket - ");

                socket.bind(null);

                socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);


                Log.d(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected());

                OutputStream stream = socket.getOutputStream();

                ContentResolver cr = context.getContentResolver();

                InputStream is = null;

                try {

                    is = cr.openInputStream(Uri.parse(fileUri));

                } catch (FileNotFoundException e) {

                    Log.d(WiFiDirectActivity.TAG, e.toString());

                }

                DeviceDetailFragment.copyFile(is, stream);

                Log.d(WiFiDirectActivity.TAG, "Client: Data written");

            } catch (IOException e) {

                Log.e(WiFiDirectActivity.TAG, e.getMessage());

            } finally {

                if (socket != null) {

                    if (socket.isConnected()) {

                        try {

                            socket.close();

                        } catch (IOException e) {

                            // Give up

                            e.printStackTrace();

                        }

                    }

                }

            }


        }

    }

}




1. WI-FI ?  와이파이 다이렉트란 ? 

2. 와이파이 개발 일반 

  2-1) 와이파이 스캔 

  2-2) 와이파이 환경 설정

  2-3) 와이파이 네트워크 연결

 

3. 와이파이 다이렉트 개발 

  3-1) 일반

  3-2) 서비스 검색 

  3-3) 와이파이 락과 멀티캐스트 설정 



WI-FI


전파거리 : 직선거리 200미터 /  벽으로 막힌곳에서는 대략 3~4미터 (1층~2층) 

전송속도 : 최대 300Mbps ( 802.11n) 

단점     :  블루투스에 비해서 많은 전력소모 /  전자레인지등과 같은 대역대라서 전파간섭의 가능성 

다른 무선 통신 방법들 : 지그비 or Z-Wave 등 


보통 가정에서 무선공유기를 주축으로 와이파이가 연결되어져서 사용된다. 무선공유기같은 AP 없이 

기기들끼리 서로 제어하고 데이터를 전송할수있는 기술을 와이파이 다이렉트라고 한다. 


와이파이 다이렉트 기술은 와이파이가 무선 접속 장치를 거치지 않고 기기간에 직접 접속하여 데이터를 주고받을수 있다는 

점에서 초기에는 WI-FI Peer-to-Peer 라고 불리워 왔다. 



무선랜이란 ? "전통적이니 와이파이 망을 무선랜이라고 부른다" 


와이파이 규격 

- IEEE 802.11n

 802.11n 은 최근 상용화된 전송 규격으로 2.4Ghz 대역과 5Ghz 대역을 사용할 수 있으며 이론적으로 최대 300Mbps 까지의 

 속도를 지원할 수있다. 우리나라에서는 기술 규격 내 주파수 점유 대역폭의 문제로 인하여 135~144Mbps 로 속도가 

 제한되었으나 최근에 전파연구소의 기술기준고시로 300Mbps 까지 사용할수있게 되었다.


http://blog.skbroadband.com/543 의 와이파이 다이렉트 소개 

와이파이 다이렉트는 와이파이 기술의 새 버전으로, AP(Access Point)를 통한 인터넷 망과의 연결 없이도 무선으로 여러 기기를 와이파이로 연결, 데이터를 빠르고 신속하게 전송할 수 있는 기술을 뜻합니다. Wi-Fi의 장점과 블루투스의 장점을 합친 기술로써, 블루투스보다 속도가 빠른 Wi-Fi의 통신방식을 활용하면서 블루투스의 기능을 띄고 있습니다. 

보통의 와이파이는 무선으로 유선망을 사용하기 때문에 유선망과 연결된 AP가 하나는 존재해야만 사용이 가능한 반면, 와이파이 다이렉트는 해당 기능을 지원하는 기기만 2개가 있다면 블루투스처럼 쉽게 상호 통신이 가능하므로 별도의 AP는 필요 없게 됩니다. 와이파이 다이렉트는 블루투스처럼 기기들간의 통신을 위한 기술이라는 점. 이해가 가시겠죠?^^

                                                          유무선 공유기 & 와이파이 를 이용한 인터넷 공유 

                                     무선 접속 장치는 자신과 연결된 단말기 모두를 같은 네트워크 대역으로 연결시킨다.



                                                                            WI-FI Direct 모식도 



안드로이드에서 와이파이 다이렉트 

와이파이와 와이파이 다이렉트는 안드로이드 프레임워크가 독자적으로 젝오하는 기능이 아니다. 리눅스의 wpa_supplicant 에 의해 제공.


WiFi  와   TCP/IP 의 차이점은 ?



           

위에서 눈여겨 볼것은 TCP/IP 는 OSI 계층의 3,4 단계에 위치해있는 소프트웨어 레벨 프로토콜이라는 점~

WI-FI 는 이더넷과 같은 1,2 계층에 위치한 물리적 계층이라는 점~~

WiFi is one way to implment OSI Layers 1 and 2 .It does that using radio technology. Ethernet is another way to implment Layers 1 and 2 (using wires or fibre). Once you have a Layer 1 and 2 infrastructure, you then have a local network (LAN) that can interconnect a small number of hosts (usually a few to a few hundred) that are typically managed by a single entity. You can then run any Level 3 internetworking protocol, such as IP, IPX, AppleTalk, etc. over the LAN, which allows those hosts to talk not only to each other but also to hosts anywhere else on the internet (provided you have an appropriate router with a physical connection to an ISP). If your Level 3 protocol is IP, then you can run any of several Level 4 protocols on top of that, such as TCP or UDP. The Level 4 protocols transfer data between specified "ports" (basically, just numerical addresses) on the hosts at the two ends, and they can do it very reliably, though with high overhead (TCP), not reliably, but with low overhead (UDP), or in various other ways. ## Bluetooth networks are kind of like LANs, but are not normally thought of in this model, because they are only used for point-to-point communication between devices close together (10 m). Bluetooth is really just a wireless replacement for a serial cable.



zigbee 와 z-wave 프로토콜 스택 

           


지그비와 Z-Wave 는 어플리케이션레벨 개발에 대한 라이브러리가 지원되고있다. 칩에 핸들링 코드를 C 등으로 코딩하여 

호스트-타켓간에 통신할수있겠다. Z-WAY(http://www.z-wave.me/index.php?id=22)  같은 기술을 이용하여 손쉽게 개발할수도 있다. 


2 편 부터는  코드와 함께  와이파이 다이렉트 개발 에 대해서 알아보도록 하자.



+ Recent posts