現在socket通信の勉強としてWiFiDirectServiceDiscoveryというサンプルを調べています。
このデモプログラムはwifi-directを用いたp2p通信でのチャットアプリとなっており、実機での動作確認をしたところ1対1のチャットは正常に実行できました。
本題です。今、端末A,B,Cと3つの端末があるとします。端末Aと端末Bが通信を構築した後に、グループオーナーとなった端末(ここでは端末Aとします)に対して端末Cが接続要求を送信したところ端末Aには承認の是非を問うダイアログが表示され、承認すると端末A,C間で通信が構築されました。
このとき、端末B,Cから送信したメッセージは端末Aに受信されることを確認しています。
一方で端末Aからのメッセージは後から接続された端末Cにのみ受信されていました。
このデモプログラムを書き換えることで端末Aからのメッセージを端末B,Cの両方に受信させるにはどのようにすればよいでしょうか。
デバッグのログにはBindException Address Already in Use Androidとあったので、接続ごとに異なるポートやスレッドを用意する必要があるのかなと考えたのですが、お恥ずかしながら知識不足のため実装の検討がつきません。
大変恐縮ではございますが実装案等のご助言をお願い致します。
参考までにデモプログラムでのソケット通信に関係のありそうな部分を以下に示します。
GroupOwnerSocketHandler
1public class GroupOwnerSocketHandler extends Thread { 2 3 ServerSocket socket = null; 4 private final int THREAD_COUNT = 10; 5 private Handler handler; 6 private static final String TAG = "GroupOwnerSocketHandler"; 7 8 public GroupOwnerSocketHandler(Handler handler) throws IOException { 9 try { 10 socket = new ServerSocket(4545); 11 this.handler = handler; 12 Log.d("GroupOwnerSocketHandler", "Socket Started"); 13 } catch (IOException e) { 14 e.printStackTrace(); 15 pool.shutdownNow(); 16 throw e; 17 } 18 19 } 20 21 /** 22 * A ThreadPool for client sockets. 23 */ 24 private final ThreadPoolExecutor pool = new ThreadPoolExecutor( 25 THREAD_COUNT, THREAD_COUNT, 10, TimeUnit.SECONDS, 26 new LinkedBlockingQueue<Runnable>()); 27 28 @Override 29 public void run() { 30 while (true) { 31 try { 32 // A blocking operation. Initiate a ChatManager instance when 33 // there is a new connection 34 pool.execute(new ChatManager(socket.accept(), handler)); 35 Log.d(TAG, "Launching the I/O handler"); 36 37 } catch (IOException e) { 38 try { 39 if (socket != null && !socket.isClosed()) 40 socket.close(); 41 } catch (IOException ioe) { 42 43 } 44 e.printStackTrace(); 45 pool.shutdownNow(); 46 break; 47 } 48 } 49 } 50 51}
ClientSocketHandler
1public class ClientSocketHandler extends Thread { 2 3 private static final String TAG = "ClientSocketHandler"; 4 private Handler handler; 5 private ChatManager chat; 6 private InetAddress mAddress; 7 8 public ClientSocketHandler(Handler handler, InetAddress groupOwnerAddress) { 9 this.handler = handler; 10 this.mAddress = groupOwnerAddress; 11 } 12 13 @Override 14 public void run() { 15 Socket socket = new Socket(); 16 try { 17 socket.bind(null); 18 socket.connect(new InetSocketAddress(mAddress.getHostAddress(), 19 WiFiServiceDiscoveryActivity.SERVER_PORT), 5000); 20 Log.d(TAG, "Launching the I/O handler"); 21 chat = new ChatManager(socket, handler); 22 new Thread(chat).start(); 23 } catch (IOException e) { 24 e.printStackTrace(); 25 try { 26 socket.close(); 27 } catch (IOException e1) { 28 e1.printStackTrace(); 29 } 30 return; 31 } 32 } 33 34 public ChatManager getChat() { 35 return chat; 36 } 37 38}
ChatManager
1public class ChatManager implements Runnable { 2 3 private Socket socket = null; 4 private Handler handler; 5 6 public ChatManager(Socket socket, Handler handler) { 7 this.socket = socket; 8 this.handler = handler; 9 } 10 11 private InputStream iStream; 12 private OutputStream oStream; 13 private static final String TAG = "ChatHandler"; 14 15 @Override 16 public void run() { 17 try { 18 19 iStream = socket.getInputStream(); 20 oStream = socket.getOutputStream(); 21 byte[] buffer = new byte[1024]; 22 int bytes; 23 handler.obtainMessage(WiFiServiceDiscoveryActivity.MY_HANDLE, this) 24 .sendToTarget(); 25 26 while (true) { 27 try { 28 // Read from the InputStream 29 bytes = iStream.read(buffer); 30 if (bytes == -1) { 31 break; 32 } 33 34 // Send the obtained bytes to the UI Activity 35 Log.d(TAG, "Rec:" + String.valueOf(buffer)); 36 handler.obtainMessage(WiFiServiceDiscoveryActivity.MESSAGE_READ, 37 bytes, -1, buffer).sendToTarget(); 38 } catch (IOException e) { 39 Log.e(TAG, "disconnected", e); 40 } 41 } 42 } catch (IOException e) { 43 e.printStackTrace(); 44 } finally { 45 try { 46 socket.close(); 47 } catch (IOException e) { 48 e.printStackTrace(); 49 } 50 } 51 } 52 53 public void write(byte[] buffer) { 54 try { 55 oStream.write(buffer); 56 } catch (IOException e) { 57 Log.e(TAG, "Exception during write", e); 58 } 59 } 60 61}
(追記)
socketをクライアント端末ごとに用意すればいいと考えGroupOwnerSocketHandler内でArrayList<Socket> socketListを作成し、リスト内のすべてのソケットに対してwrite処理をすればよいと考えたのですがどうでしょうか。
GroupOwnerSocketHandler
1public class GroupOwnerSocketHandler extends Thread { 2public static ArrayList<Socket> socketList = new ArrayList<Socket>(); 3--(略)-- 4 @Override 5 public void run() { 6 while (true) { 7 try { 8 //クライアントごとにsocketをリストで管理 9 Socket clientSocket = socket.accept(); 10 socketList.add(clientSocket); 11 pool.execute(new ChatManager(clientSocket, handler)); 12 Log.d(TAG, "Launching the I/O handler"); 13 14 } catch (IOException e) { 15 --(略)-- 16 } 17 } 18 } 19 20}
ChatManager
1public class ChatManager implements Runnable { 2--(略)-- 3 public void write(byte[] buffer) { 4 //ソケットの数だけwrite 5 for(Iterator<Socket> it = GroupOwnerSocketHandler.socketList.iterator(); it.hasNext();) { 6 Socket s = it.next(); 7 try { 8 OutputStream os = s.getOutputStream(); 9 os.write(buffer); 10 } catch (IOException e) { 11 Log.e(TAG, "Exception during write", e); 12 } 13 } 14 } 15}
このように書き換えたところ、socketListには確かに端末B,Cそれぞれのsocketが別のポートの値で格納されていましたが受信できたのは先に接続した端末Bだけでした。ループ処理も二回実行されていたためwrite自体は2回呼ばれているはずなのですが...
(追記2)
GroupOwnerSocketHandler
1public class GroupOwnerSocketHandler extends Thread { 2public static ArrayList<ChatManager> chatList = new ArrayList<ChatManager>(); 3--(略)-- 4 @Override 5 public void run() { 6 while (true) { 7 try { 8 //クライアントごとにsocketをリストで管理 9 Socket clientSocket = socket.accept(); 10 ChatManager clientChat = new ChatManager(clientSocket, handler); 11 chatList.add(clientChat); 12 pool.execute(clientChat); 13 Log.d(TAG, "Launching the I/O handler"); 14 } catch (IOException e) { 15 --(略)-- 16 } 17 } 18 } 19}
とすることで複数の端末に送信することができました。
ですが今度は同じ端末に対して一度しか送信がされなくなってしまいました。(端末AからB,Cに対して一度ずつ送信を確認)
ChatManagerを配列として管理したので更新が正常に行われていないのではないかと考えています。
ChatManagerからhandler.obtainMessageを受け取った際のアクティビティの処理は以下のようになっていました。
WiFiServiceDiscoveryActivity
1 @Override 2 public boolean handleMessage(Message msg) { 3 switch (msg.what) { 4 case MESSAGE_READ: 5 byte[] readBuf = (byte[]) msg.obj; 6 // construct a string from the valid bytes in the buffer 7 String readMessage = new String(readBuf, 0, msg.arg1); 8 Log.d(TAG, readMessage); 9 (chatFragment).pushMessage("Buddy: " + readMessage); 10 break; 11 12 case MY_HANDLE: 13 Object obj = msg.obj; 14 (chatFragment).setChatManager((ChatManager) obj); 15 16 } 17 return true; 18 }
setChatManager
1public void setChatManager(ChatManager obj) { 2 chatManager = obj; 3 }
あなたの回答
tips
プレビュー