★ The Tsuchinoko News 2 (つちのこ通信2) ★

重要な話から、どうでもいいことまで。ほとんど役に立たないことを書き連ねています。

【PC】Android で UDPマルチキャスト

Androidで UDPのマルチキャスト(ブロードキャスト)を行うというキーワードで検索すると、当ブログが検索結果として出てくるようで、たぶんサンプルソースコードを期待して来訪される方が大変多い。たしかにサンプルコードを探してみると、ものすごく少ないので掲載しておくことにする。

実は、はてなダイアリーからはてなブログになって半月ほど経過するが、HTMLを使えるとはいえ、はてな記法なんかも魅力的だったりして、その意味では未だに、はてなブログは非力だ。もちろんCSSを駆使しHTMLを書けばいいのだろうが、毎日毎日書くブログがそんなことでは不便で仕方がない。さらなる、はてなブログの進化を待ちながら、ソースコード掲載の練習も兼ねてたりして・・・

package ***.***.****;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.TimeUnit;

import android.net.DhcpInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;

public class UDPMulitiCast extends Activity {

	//=============================================
	//
	//  UDPポート(マルチキャスト送信用)と
	//  TCPポート(受信用)
	//
	public final static int UDPPORT = 7777;
	public final static int TCPPORT = 8888;

	//=============================================
	//
	//  スレッド管理用
	//
	private static Thread udpmuliticastThread = null;
	private static Thread tcpreceiveThread = null;
    
	//=============================================
	//
	//  TCP受信用
	//
	private ServerSocket serverSocket;
	private Socket socket;

	//=============================================
	//
	//  サーバーのIPアドレス( UDPマルチキャストを受けたら
	//                   TCPで返信があるという前提)
	//
	private static String serverIP = null;
    
	//=============================================
	//
	//  コンストラクタ
	//
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_udpmuliti_cast);

		//=============================================
		//
		//  UDPマルチキャストを投げつけるスレッドを開始する
		//
		if ( udpmuliticastThread == null){
			udpmuliticastThread =new Thread( 
					
                                       new Runnable() {
					   public void run() {
										                                                          sendUDP();
					   }
				      }
			);
			udpmuliticastThread.start();
		}

		//=============================================
		//
		//  TCP受信用のスレッドを開始する
		//
		if ( tcpreceiveThread == null){
			tcpreceiveThread =new Thread(
			
         		              new Runnable() {
	 					public void run() {
				 							                                                      receiveTcp();
				 	
                                                }
				 	}
			);
			tcpreceiveThread.start();
		}

	}

	//=============================================
	//
	//  UDPマルチキャストを投げつけるスレッド本体
	//
	private void sendUDP(){
	
		Thread thread = Thread.currentThread();
		//
		// serverIP が確定するまで繰り返す
		//
		while( serverIP == null 
                        && udpmuliticastThread == thread ){

			
			String msgStr = getMyAddress( this );
			DatagramSocket udpSocket;
			try {
				udpSocket = new DatagramSocket( UDPPORT );
				udpSocket.setBroadcast(true);
				
				DatagramPacket packet;
				packet = new DatagramPacket(msgStr.getBytes(), 
                                         msgStr.length(),
                                         getBroadcastAddress( this ),
                                         UDPPORT);
				
				udpSocket.send(packet);
				udpSocket.close();
			} catch (SocketException e1) {
			} catch (IOException e1) {
			}

			//5秒待つ
			try {
				TimeUnit.MILLISECONDS.sleep(5000);
			} catch (InterruptedException e) {
			}
			// サーバーからTCPでの返答があり
			// サーバーIPアドレスが判明するまで続ける
		}
    }

	//=============================================
	//
	//  自機の(Wifiの) IPアドレスを取得
	//
	public String getMyAddress( Context context ){
		String strIp = "0.0.0.0";
		try {
			WifiManager wifiManager = 
             (WifiManager)context.getSystemService(Context.WIFI_SERVICE);

			WifiInfo wifiInfo = wifiManager.getConnectionInfo();
			int ip = wifiInfo.getIpAddress();

			strIp = 	((ip >> 0) & 0xFF)+"."+
						((ip >> 8) & 0xFF)+"."+
						((ip >> 16) & 0xFF)+"."+
						((ip >> 24) & 0xFF); 

		} catch (Exception e) {
		}
		return strIp;
	}
	//=============================================
	//
	//  Muliticast用のアドレスを取得
	//
	public InetAddress getBroadcastAddress( Context context )
        throws IOException {
		WifiManager wifiManager = 
        (WifiManager)context.getSystemService(Context.WIFI_SERVICE);

		DhcpInfo dhcp = wifiManager.getDhcpInfo();

		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);
	}

    
	//=============================================
	//
	//  TCP受信用スレッド本体
	//
	private void receiveTcp() {
		
		Thread thread = Thread.currentThread();
		try {
			serverSocket = new ServerSocket( TCPPORT );
			while( thread == tcpreceiveThread ){

				socket = serverSocket.accept();

				BufferedReader br =
		 		new BufferedReader(
				new InputStreamReader(socket.getInputStream()));
     
				String str;
				while( (str = br.readLine()) != null 
                                && thread == tcpreceiveThread ){

					serverIP = str;
				}
 
				if( socket != null){
					socket.close();
					socket = null;
				}
            	
			}

		} catch (IOException e) {
		} finally {
			try {
				// 接続終了処理
				tcpreceiveThread = null;
				if( serverSocket!=null){
					serverSocket.close();
				}
				if( in!=null ){
					in.close();
				}
				if( socket!=null ){
					socket.close();
				}
			} catch (IOException e) {
			}
		}
	}

}

ハイライト表示になってくれるのはありがたいけれど、なんか思ったように表示してくれないなぁ。そんなこんなで、時間がなくなったのでソースの解説は省略させていただきます。。。
UDP でマルチキャストして
ほかの機械がそれを拾って、IPアドレスをTCPで送ってくる。
それを受け取る、というもの。
もう少しさわると、エコーサーバーとか、ミニチャットとか作れる元になるのがこれ。
実用には、 onStop 時の処理を考えないと、再度この Activity を利用すると、Socket がつくれなくなる(ポートが開きっぱなしだから)ので注意と、いろいろ工夫が必要。