ServerSocketを使用してクライアントからデータを受信する


Socketクラスを使用してソケット通信をするではクライアントとしてサーバーからデータを受信する方法を紹介しましたが、
今回はサーバーとしてクライアントからデータを受信する方法を紹介します。

詳細は以下から。

ServerSocketはクライアントからの接続要求を待機し、任意の処理するためのサーバーとしての機能を提供するクラスです。
(詳細はこちらのリファレンスを確認してください。)

ServerSocketを使用してサーバーとしての機能を実装するために以下の実装が必要になります。

  • ポート番号を指定してServerSocketクラスのオブジェクトを作成する
  • 接続要求を待機する
  • クライアントから送信されたデータを受信する

ポート番号を指定してServerSocketクラスのオブジェクトを作成する

ポート番号とは、デバイス上で動作する複数のプログラムのうちの一つを通信相手として指定するために使用する番号です。

このポート番号のおかげで同じIPアドレス上で複数のアプリケーションが通信を行えるようになっています。

ポート番号がかぶるとプログラムが正常に動かないため、他のアプリケーションで使用されていないポート番号を指定する必要があります。

well-knownと呼ばれるポート番号は他のアプリケーションで使用されていることが多いため避けたほうがいいでしょう。
well-knownは数が多いためここでの紹介は割愛します。well-knownポートについての詳細な情報はこちらを参考にしてください。

ServerSocketクラスではコンストラクタでこのポート番号を指定します。

ServerSocket mServer = new ServerSocket(8080);

上記のコードではポート番号8080でServerSocketクラスのオブジェクトを作成しています。

接続要求を待機する

接続要求を待機するにはacceptメソッドを使用します。

このacceptメソッドはブロッキングメソッドで、クライアントからの接続要求が発生するまでスレッドが止まりますので、
UIスレッドとは別のスレッドで実行してください。

ServerSocket mServer = new ServerSocket(8080);
mServer.accept();

上記のコードはポート番号8080でクライアントからの接続を待機します。

クライアントから送信されたデータを受信する

クライアントから送信されたデータを受信するにはacceptメソッドの引数で返ってくるSocketクラスのオブジェクトを使用します。

まずはサンプルコードから。

SocketServerSampleActivity.java

public class SocketServerSampleActivity extends Activity implements Runnable{

    private ServerSocket mServer;
    private Socket mSocket;
    int port = 8080;
    volatile Thread runner = null;
    Handler mHandler = new Handler();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        WifiManager wifiManager =  (WifiManager) getSystemService(WIFI_SERVICE);
        WifiInfo wifIinfo = wifiManager.getConnectionInfo();
        int address = wifIinfo.getIpAddress();
        String ipAddressStr = ((address >> 0) & 0xFF) + "."
                + ((address >> 8) & 0xFF) + "." + ((address >> 16) & 0xFF)
                + "." + ((address >> 24) & 0xFF);
        TextView tv = (TextView) findViewById(R.id.tv1);
        tv.setText(ipAddressStr);

        if(runner == null){
            runner = new Thread(this);
            runner.start();
        }
        Toast.makeText(this, "スレッドスタート", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void run() {
        try {
            mServer = new ServerSocket(port);
            mSocket = mServer.accept();
            BufferedReader in = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
            String message;
            final StringBuilder messageBuilder = new StringBuilder();
            while ((message = in.readLine()) != null){
                messageBuilder.append(message);
            }

            mHandler.post(new Runnable() {

                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(), messageBuilder.toString(), Toast.LENGTH_SHORT).show();
                }
            });
            runner.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

14〜21行目ではデバイスのIPアドレスを表示させています。このIPアドレスはデバッグの際にクライアントプログラムで指定するために使用します。
16~19行目のビット演算はWifiManagerから返ってくるIPアドレスの値がint型であるため、オクテット表記(よくみる192.168.x.xという表記方法)に
変換しています。WifiManagerについてはこちらを参考にしてください。

35行目でacceptメソッドで返されたSocketクラスのオブジェクトを使用してBufferedReaderクラスのオブジェクトを作成しています。
BufferedReaderクラスの詳細な使用方法はこちらを参考にして下さい。

またインターネット接続を使用するため、パーミッションにandroid.permission.INTERNETを追加する必要があります。