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

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

【PC】Android のお勉強(UDPとTCP)

f:id:tsuchinoko118:20120727152348j:image:left:w320ここ数日 Androidアプリの開発に没頭している。と言っても、筆者のことなので Android (スマホ)をハンディターミナルとして使うことで頭がいっぱいで アプリ単体の開発や、足りないところをネット(クラウド)で補うことはあまり考えていない。

今日は、UDP/TCP。

インターネット用の(といっても語弊はないだろう)の通信方式のことで、糸電話の糸みたいなものだ。筆者的には「ある日突然持ち込んだ Android端末 で サーバーとなるPCのIPを固定にして、端末やアプリで 通信相手の サーバーの IPアドレスを設定させる」ということをさせたくないので(したい人もいるのかも知れないが)、

(1)UDPマルチキャストで、同一LAN内にいるすべてのマシンに「私はこういうものです」と IPアドレスを通知

(2)鎮座しているサーバー係のPCが通知を受け取ったら、TCPで接続して、あとは 1対1の通信をする

ということを考えて Androidのお勉強。

Android=Java のスレッドが、あまりよくわからなくて、その動き、制限事項などを勉強しつつ、UDPのマルチキャストは、PCでも触ったことがないのでこのあたりを念入りに研究?しながらお勉強をした。

Zxingのカメラものに比べると、さほど難解なところはなく(なにしろ世界標準の規格だから)朝からはじめて夕方には理解することができた。
試しに、人工無能もどきをつくってみた。

f:id:tsuchinoko118:20120727152351j:image:right:w200(1)Android アプリを起動する。
(2)なにか文字を入れてボタンを押すと、UDPマルチキャストで一斉配信
(3)サーバーソフトが起動しているPCが受け取り、適当な文字列を返す
(4)Android アプリは サーバーとTCP通信で文字を受け取り表示する

f:id:tsuchinoko118:20120727152349j:image:left:w180チャットにしようかとも思ったが、大量の(あらかじめ用意したバッファを超えるような)データ送信があった時どうなるかとか、いろいろ見てると時間がなくなってしまったのだった。

Zxingの時もそうだったが、Android の画面作成はけっこうクセがあって、理解するのに骨が折れる。ターゲットが Android 2.3以上にしか設定してないので、3.0以降に出てくるタブレットの時はこうだ、みたいなことが出来ない中やっているので、こんなに決め打ち(絶対値入力)でいいのか、と、悩むところしかり。かれこれ2週間触っているので、ずいぶんと慣れてきた。




f:id:tsuchinoko118:20120727150328j:image:right:w240
さて、お次のお勉強は SQ Lite 。
今日学んだ TCP通信で、持ち歩くデータをPCから送ってもらって、Android内のデータベースに保存。これをフリックで検索させる。みたいなことをすると、ハンディターミナルの基礎勉強はおしまい。
本番のアプリを組み立てることが可能になる。

参考になるのかどうかよくわからないけど、恥ずかしい(汚らしい)ソースをさらしておこう。






public class MainActivity extends Activity{

private final int UDPPORT = 8080;
private final int TCPPORT = 8888;

private ServerSocket serverSocket;
private Socket socket;
private InputStream in;
private String txtReceive;
private final Handler receiveHandler = new Handler();
private Thread receiveRunnerThread = null;

private EditText sendEditText;
private Button sendBtn;
private TextView receiveLbl;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Screen Layout
requestWindowFeature(Window.FEATURE_NO_TITLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);

LinearLayout layout = new LinearLayout(this);
layout.setBackgroundColor(Color.rgb(0xff,0xff,0xff));
layout.setOrientation(LinearLayout.VERTICAL);
setContentView(layout);

sendEditText = new EditText(this);
sendEditText.setId(2);
sendEditText.setText("", BufferType.NORMAL);
sendEditText.setTextSize(24.0f);
sendEditText.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT ));
layout.addView(sendEditText);

sendBtn = new Button(this);
sendBtn.setText("UDPで送信/TCPで受信!!");
sendBtn.setTextSize(24.0f);
sendBtn.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
sendUDPCall();
}
});
sendBtn.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT ));
layout.addView(sendBtn);

receiveLbl = new TextView(this);
receiveLbl.setId(1);
receiveLbl.setText("");
receiveLbl.setTextSize(24.0f);
receiveLbl.setTextColor(Color.rgb(0,0,0));
receiveLbl.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT ));
layout.addView(receiveLbl);

}


@Override
public void onStart(){
super.onStart();

// EditTextにフォーカス
sendEditText.requestFocus();

// Wifiの状態を Enabled にする
WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
try{
wifiManager.setWifiEnabled(true);
} catch( Exception e){
Toast.makeText(this, "Wifi失敗", Toast.LENGTH_LONG).show();
}

int wifiState = wifiManager.getWifiState();
String msgString = "";
switch (wifiState) {
case WifiManager.WIFI_STATE_DISABLING:
msgString = "使用不可になっています";
break;
case WifiManager.WIFI_STATE_DISABLED:
msgString = "使用不可です";
break;
case WifiManager.WIFI_STATE_ENABLED:
msgString = "";
break;
case WifiManager.WIFI_STATE_ENABLING:
msgString = "使える設定になってます";
break;
case WifiManager.WIFI_STATE_UNKNOWN:
msgString = "不明です";
break;
default:
msgString = "よくわかりません";
break;
}

if (msgString != ""){
Toast.makeText(this, msgString, Toast.LENGTH_LONG).show();
}



// 簡易サーバーの起動(TCP)
if (receiveRunnerThread == null){
receiveRunnerThread =new Thread(
new Runnable() {
public void run() {
receiveTcp();
}
}
);
receiveRunnerThread.start();
}

}

private void sendUDPCall(){

// EditTextのリセット
String string = sendEditText.getText().toString();
addText( "[Ad]" + string );
sendEditText.setText("");

// 自分のIPアドレスを取得する
String myAddr = "UnKnown";
try {
myAddr = getMyAddress();
} catch (IOException e) {
}

// マルチキャスト
String sendStr = "*001" + myAddr;
DatagramSocket udpSocket;
try {
udpSocket = new DatagramSocket(UDPPORT);
udpSocket.setBroadcast(true);

DatagramPacket packet;
packet = new DatagramPacket(sendStr.getBytes(), sendStr.length(),getBroadcastAddress(), UDPPORT);

udpSocket.send(packet);

udpSocket.close();

} catch (SocketException e1) {
} catch (IOException e1) {
}

}

InetAddress getBroadcastAddress() throws IOException {
// マルチキャスト用のAddressを取得する
WifiManager wifiManager = (WifiManager)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);
}

String getMyAddress() throws IOException{
// WIFI 自分のIPアドレスを返す
WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();

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

return strIp;

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}

private void receiveTcp() {

Thread thread = Thread.currentThread();

int size;
byte w=new byte[1024];
txtReceive="";

try {
serverSocket = new ServerSocket(TCPPORT);
while( thread == receiveRunnerThread ){
// receiveRunnnerThread に null をセットすると終わる

socket = serverSocket.accept();
in =socket.getInputStream();
size=in.read(w);
if (size > 0){

txtReceive=new String(w,0,size,"UTF-8");
addText(txtReceive);

}


}

} catch (IOException e) {
e.printStackTrace();
}
}

private void addText(final String s ){
//ハンドラによるユーザーインタフェース操作(デリゲートのようなもの)
receiveHandler.post(new Runnable(){
public void run() {

receiveLbl.setText(
receiveLbl.getText() +
System.getProperty("line.separator") +
s
);

}
});
}
}

それにしても、この「引用」という機能、どうも使いにくくていけませんね・・・