1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.nsdchat; 18 19 import android.os.Bundle; 20 import android.os.Handler; 21 import android.os.Message; 22 import android.util.Log; 23 24 import java.io.BufferedReader; 25 import java.io.BufferedWriter; 26 import java.io.IOException; 27 import java.io.InputStreamReader; 28 import java.io.OutputStreamWriter; 29 import java.io.PrintWriter; 30 import java.net.InetAddress; 31 import java.net.ServerSocket; 32 import java.net.Socket; 33 import java.net.UnknownHostException; 34 import java.util.concurrent.ArrayBlockingQueue; 35 import java.util.concurrent.BlockingQueue; 36 37 public class ChatConnection { 38 39 private Handler mUpdateHandler; 40 private ChatServer mChatServer; 41 private ChatClient mChatClient; 42 43 private static final String TAG = "ChatConnection"; 44 45 private Socket mSocket; 46 private int mPort = -1; 47 48 public ChatConnection(Handler handler) { 49 mUpdateHandler = handler; 50 mChatServer = new ChatServer(handler); 51 } 52 53 public void tearDown() { 54 mChatServer.tearDown(); 55 mChatClient.tearDown(); 56 } 57 58 public void connectToServer(InetAddress address, int port) { 59 mChatClient = new ChatClient(address, port); 60 } 61 62 public void sendMessage(String msg) { 63 if (mChatClient != null) { 64 mChatClient.sendMessage(msg); 65 } 66 } 67 68 public int getLocalPort() { 69 return mPort; 70 } 71 72 public void setLocalPort(int port) { 73 mPort = port; 74 } 75 76 77 public synchronized void updateMessages(String msg, boolean local) { 78 Log.e(TAG, "Updating message: " + msg); 79 80 if (local) { 81 msg = "me: " + msg; 82 } else { 83 msg = "them: " + msg; 84 } 85 86 Bundle messageBundle = new Bundle(); 87 messageBundle.putString("msg", msg); 88 89 Message message = new Message(); 90 message.setData(messageBundle); 91 mUpdateHandler.sendMessage(message); 92 93 } 94 95 private synchronized void setSocket(Socket socket) { 96 Log.d(TAG, "setSocket being called."); 97 if (socket == null) { 98 Log.d(TAG, "Setting a null socket."); 99 } 100 if (mSocket != null) { 101 if (mSocket.isConnected()) { 102 try { 103 mSocket.close(); 104 } catch (IOException e) { 105 // TODO(alexlucas): Auto-generated catch block 106 e.printStackTrace(); 107 } 108 } 109 } 110 mSocket = socket; 111 } 112 113 private Socket getSocket() { 114 return mSocket; 115 } 116 117 private class ChatServer { 118 ServerSocket mServerSocket = null; 119 Thread mThread = null; 120 121 public ChatServer(Handler handler) { 122 mThread = new Thread(new ServerThread()); 123 mThread.start(); 124 } 125 126 public void tearDown() { 127 mThread.interrupt(); 128 try { 129 mServerSocket.close(); 130 } catch (IOException ioe) { 131 Log.e(TAG, "Error when closing server socket."); 132 } 133 } 134 135 class ServerThread implements Runnable { 136 137 @Override 138 public void run() { 139 140 try { 141 // Since discovery will happen via Nsd, we don't need to care which port is 142 // used. Just grab an available one and advertise it via Nsd. 143 mServerSocket = new ServerSocket(0); 144 setLocalPort(mServerSocket.getLocalPort()); 145 146 while (!Thread.currentThread().isInterrupted()) { 147 Log.d(TAG, "ServerSocket Created, awaiting connection"); 148 setSocket(mServerSocket.accept()); 149 Log.d(TAG, "Connected."); 150 if (mChatClient == null) { 151 int port = mSocket.getPort(); 152 InetAddress address = mSocket.getInetAddress(); 153 connectToServer(address, port); 154 } 155 } 156 } catch (IOException e) { 157 Log.e(TAG, "Error creating ServerSocket: ", e); 158 e.printStackTrace(); 159 } 160 } 161 } 162 } 163 164 private class ChatClient { 165 166 private InetAddress mAddress; 167 private int PORT; 168 169 private final String CLIENT_TAG = "ChatClient"; 170 171 private Thread mSendThread; 172 private Thread mRecThread; 173 174 public ChatClient(InetAddress address, int port) { 175 176 Log.d(CLIENT_TAG, "Creating chatClient"); 177 this.mAddress = address; 178 this.PORT = port; 179 180 mSendThread = new Thread(new SendingThread()); 181 mSendThread.start(); 182 } 183 184 class SendingThread implements Runnable { 185 186 BlockingQueue<String> mMessageQueue; 187 private int QUEUE_CAPACITY = 10; 188 189 public SendingThread() { 190 mMessageQueue = new ArrayBlockingQueue<String>(QUEUE_CAPACITY); 191 } 192 193 @Override 194 public void run() { 195 try { 196 if (getSocket() == null) { 197 setSocket(new Socket(mAddress, PORT)); 198 Log.d(CLIENT_TAG, "Client-side socket initialized."); 199 200 } else { 201 Log.d(CLIENT_TAG, "Socket already initialized. skipping!"); 202 } 203 204 mRecThread = new Thread(new ReceivingThread()); 205 mRecThread.start(); 206 207 } catch (UnknownHostException e) { 208 Log.d(CLIENT_TAG, "Initializing socket failed, UHE", e); 209 } catch (IOException e) { 210 Log.d(CLIENT_TAG, "Initializing socket failed, IOE.", e); 211 } 212 213 while (true) { 214 try { 215 String msg = mMessageQueue.take(); 216 sendMessage(msg); 217 } catch (InterruptedException ie) { 218 Log.d(CLIENT_TAG, "Message sending loop interrupted, exiting"); 219 } 220 } 221 } 222 } 223 224 class ReceivingThread implements Runnable { 225 226 @Override 227 public void run() { 228 229 BufferedReader input; 230 try { 231 input = new BufferedReader(new InputStreamReader( 232 mSocket.getInputStream())); 233 while (!Thread.currentThread().isInterrupted()) { 234 235 String messageStr = null; 236 messageStr = input.readLine(); 237 if (messageStr != null) { 238 Log.d(CLIENT_TAG, "Read from the stream: " + messageStr); 239 updateMessages(messageStr, false); 240 } else { 241 Log.d(CLIENT_TAG, "The nulls! The nulls!"); 242 break; 243 } 244 } 245 input.close(); 246 247 } catch (IOException e) { 248 Log.e(CLIENT_TAG, "Server loop error: ", e); 249 } 250 } 251 } 252 253 public void tearDown() { 254 try { 255 getSocket().close(); 256 } catch (IOException ioe) { 257 Log.e(CLIENT_TAG, "Error when closing server socket."); 258 } 259 } 260 261 public void sendMessage(String msg) { 262 try { 263 Socket socket = getSocket(); 264 if (socket == null) { 265 Log.d(CLIENT_TAG, "Socket is null, wtf?"); 266 } else if (socket.getOutputStream() == null) { 267 Log.d(CLIENT_TAG, "Socket output stream is null, wtf?"); 268 } 269 270 PrintWriter out = new PrintWriter( 271 new BufferedWriter( 272 new OutputStreamWriter(getSocket().getOutputStream())), true); 273 out.println(msg); 274 out.flush(); 275 updateMessages(msg, true); 276 } catch (UnknownHostException e) { 277 Log.d(CLIENT_TAG, "Unknown Host", e); 278 } catch (IOException e) { 279 Log.d(CLIENT_TAG, "I/O Exception", e); 280 } catch (Exception e) { 281 Log.d(CLIENT_TAG, "Error3", e); 282 } 283 Log.d(CLIENT_TAG, "Client sent message: " + msg); 284 } 285 } 286 } 287