UDP 브로드캐스팅을 통한 근거리 데이터 교환 앱을 짜고 있습니다.

소켓 부분은 서비스 클래스를 만들어서 따로 스레드를 구현하여 돌리고 있고,

메인 액티비티에서는 스레드 시작과 종료, UI 부분만 담당하고 있습니다.


두 장치 이상이 서로가 브로드캐스팅하면서 데이터를 주고 받는 것이 정상인데,

이게 화면이 켜져 있을 땐(Sleep 상태가 아닐 때) 정상적으로 데이터를 교환하는데,

화면이 꺼지면(Sleep 상태면) send만 되고 receive는 되지 않는 현상이 발생합니다.


온갖 실험과 반복과 질문을 통해서도 해결이 안되기에 안드로이드 API 자체의 오류가 아닌가 의심이 될 정도입니다.


아래는 메인 액티비티 코드입니다.

코드는 오류증상을 확인할 수 있을 최소한으로 줄였고, 이해가 쉽도록 주석을 붙였습니다.


public class MainActivity extends Activity {

static TextView internal;

static TextView external;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

LinearLayout layout = new LinearLayout(this);

// 0과 0만 있는 텍스트뷰 2개만 화면에 띄움

layout.setOrientation(1);

internal = new TextView(this);

internal.setText("0");

external = new TextView(this);

external.setText("0");

layout.addView(internal);

layout.addView(external);

setContentView(layout);


// 서비스 시작

        startService(new Intent(this, LocalService.class));

}

// 서비스에서 데이터를 수신하면 UI 변경을 위해 이 메소드를 호출함

static public void recive(int message) {

// 패킷의 호스트 주소를 받아서 해당 주소가 나의 주소와 같으면 위쪽 숫자가 1씩 오름

// 해당 주소가 나의 주소와 다르면 아래쪽 텍스트뷰 숫자가 1씩 오름

if(message==getLocalHostAddress())

internal.setText(Integer.parseInt(internal.getText().toString())+1+"");

else

external.setText(Integer.parseInt(external.getText().toString())+1+"");

}

@Override

public void onDestroy() {

super.onDestroy();

// 서비스 종료

        stopService(new Intent(this, LocalService.class));

System.gc();

}

//자신의 아이피 호스트 주소 가져오는 메소드

private static int getLocalHostAddress() {

try {

for (Enumeration<NetworkInterface> en = NetworkInterface

.getNetworkInterfaces(); en.hasMoreElements();) {

NetworkInterface intf = en.nextElement();

for (Enumeration<InetAddress> enumIpAddr = intf

.getInetAddresses(); enumIpAddr

.hasMoreElements();) {

InetAddress inetAddress = enumIpAddr.nextElement();

if (!inetAddress.isLoopbackAddress()) {

if(!inetAddress.getHostAddress().toString().contains(":")){

String[] addr = inetAddress.getHostAddress().toString().split("\\.");

return Integer.parseInt(addr[3]);

}

}

}

}

} catch (SocketException ex) {

ex.printStackTrace();

}

return -1;

}

}


아래는 서비스 클래스 코드 입니다.

지면상의 이유로 임포트 부분은 제외하였습니다.


public class LocalService extends Service {


WifiManager.WifiLock wifiLock = null;

PowerManager.WakeLock wakeLock = null;

static DatagramSocket socket;

MessageReciver messageReceiver;

MessageSender messageSender;

private volatile boolean keepRunning = true;


public static String SERVER_IP = "255.255.255.255";

public static int SERVER_PORT = 15464;


    @Override

    public IBinder onBind(Intent intent) {

        return null;

    }


    @Override

    public void onCreate() {

    try {

if (wifiLock == null) { // 와이파이락 부분

   WifiManager wifiManager = (WifiManager) this.getSystemService(this.WIFI_SERVICE);

   wifiLock = wifiManager.createWifiLock("wifilock");

   MulticastLock lock = wifiManager.createMulticastLock("multicast");

   lock.acquire();

   wifiLock.setReferenceCounted(true);

   wifiLock.acquire();

}

if (wakeLock == null) { // 웨이크락 부분

   PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);

   wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "wakelock");

   wakeLock.acquire();

}

// UDP 소켓 생성, 브로드캐스트 모드

socket = new DatagramSocket(SERVER_PORT);

socket.setBroadcast(true);

// 스레드 객체 생성, UDP 패킷 수신과 송신

messageReceiver = new MessageReciver();

messageSender = new MessageSender();

    }

catch (Exception e) {

       Log.e("SOCKET", e.getMessage()); 

e.printStackTrace();

}

    }


    @Override

    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.i("LocalService", "Received start id " + startId + ": " + intent);

        // 스레드 시작

messageReceiver.start();

messageSender.start();

        return START_NOT_STICKY;

    }


    @Override

    public void onDestroy() {

    // 스레드 중단

keepRunning = false;

messageReceiver.interrupt();

messageSender.interrupt();


// 와이파이락, 웨이크락 종료

if (wifiLock != null) {

   wifiLock.release();

   wifiLock = null;

}

if (wakeLock != null) {

   wakeLock.release();

   wakeLock = null;

}

messageReceiver = null;

messageSender = null;


// 소켓 닫기

socket.close();

socket = null;

System.gc();

    }


    // 메인 액티비티와 연결하기 위핸 수신 핸들러

Handler handler = new Handler() {

public void handleMessage(Message message) {

super.handleMessage(message);

// 받은 패킷의 아이피의 마지막 주소를 메인 액티비티에 전송

String[] addr = ((String)message.obj).split("\\.");

MainActivity.recive(Integer.parseInt(addr[3]));

}

};


// 패킷 수신 스레드

public class MessageReciver extends Thread {

public void run() {

try {

while (keepRunning) {

// 패킷 수신

byte[] buffer = new byte[1000];

DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

socket.receive(packet); // ** 스레드가 블락되는 메소드 **

//이 메소드 도중에 프로그램이 종료되면 예상치않은 오류 메시지가 뜸 **

// 패킷의 데이터는 버리고, 아이피주소만 핸들러로 전송

Message message = handler.obtainMessage(1, packet.getAddress().getHostAddress());

handler.sendMessage(message);

}

} catch (IOException e) {

       Log.e("recvTHREAD", e.getMessage()); 

e.printStackTrace();

}

}

}

// 패킷 송신 스레드, 1초마다 반복 송신

public class MessageSender extends Thread {

public void run() {

try {

while (keepRunning) {

// 더미 패킷을 1초마다 브로드캐스트 송신

String text = new String("signal");

DatagramPacket packet = new DatagramPacket(text.getBytes(), text.getBytes().length,

InetAddress.getByName(SERVER_IP), SERVER_PORT);

socket.send(packet);

Thread.sleep(1000);

}

} catch (InterruptedException e) {

       Log.e("sendTHREAD", e.getMessage()); 

e.printStackTrace();

} catch (IOException e) {

       Log.e("sendTHREAD", e.getMessage()); 

}

}

}


}


이 프로그램을 실행하면

0

0

으로 시작해서, 자신것이 켜져 있는 상태에서는 위에 텍스트 숫자 0이 1씩 증가합니다.

이 상태에서 다른 안드로이드폰을 가져와서 같은 프로그램을 또 실행하면 아래쪽 숫자도 1씩 증가하게 됩니다.

하지만 잠시 슬립상태로 화면을 껏다가 다시 켜면, 위쪽 숫자만 증가되어 있고, 아래쪽 숫자는 멈춰있다가

다시 켜면 그때서야 올라가는 것을 확인할 수 있습니다.

홈버튼을 누르거나 다른 앱을 사용중일때도 정상적으로 둘다 카운트가 됩니다.

즉, 슬립상태에서는 자신이 보낸 패킷만 읽을 수 있고, 외부 수신은 전혀 되지 않는다는 걸 알 수 있습니다.

와이파이락과 웨이크락도 걸려있는 상태도인데도 말이죠...


매니페스트 파일입니다.


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

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

    package="com.example.androidtest"

    android:versionCode="1"

    android:versionName="1.0" >


    <uses-sdk

        android:minSdkVersion="9"

        android:targetSdkVersion="15" />

    

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

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

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

 

    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name="com.example.androidtest.MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

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


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

            </intent-filter>

        </activity>

        

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

    </application>

</manifest>


너무 어이가 없고 해결이 안되서 안펍 고수님들께 여쭤보고자 염치없이 코드를 들고왔습니다.