Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Side by Side Diff: webrtc/examples/androidapp/src/org/appspot/apprtc/TCPChannelClient.java

Issue 1963053002: Direct IP connect functionality for AppRTC Android demo. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Reorder field modifiers Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 package org.appspot.apprtc;
12
13 import android.util.Log;
14
15 import org.appspot.apprtc.util.LooperExecutor;
16
17 import java.io.BufferedReader;
18 import java.io.IOException;
19 import java.io.InputStreamReader;
20 import java.io.PrintWriter;
21 import java.net.InetAddress;
22 import java.net.ServerSocket;
23 import java.net.Socket;
24 import java.net.UnknownHostException;
25
26 /**
27 * Replacement for WebSocketChannelClient for direct communication between two I P addresses. Handles
28 * the signaling between the two clients using a TCP connection.
29 *
30 * <p>All public methods should be called from a looper executor thread
31 * passed in a constructor, otherwise exception will be thrown.
32 * All events are dispatched on the same thread.
33 */
34 public class TCPChannelClient {
35 private static final String TAG = "TCPChannelClient";
36
37 private final LooperExecutor executor;
38 private final TCPChannelEvents eventListener;
39 private TCPSocket socket;
40
41 /**
42 * Callback interface for messages delivered on TCP Connection. All callbacks are invoked from the
43 * looper executor thread.
44 */
45 public interface TCPChannelEvents {
46 void onTCPConnected(boolean server);
47 void onTCPMessage(String message);
48 void onTCPError(String description);
49 void onTCPClose();
50 }
51
52 /**
53 * Initializes the TCPChannelClient. If IP is a local IP address, starts a lis tening server on
54 * that IP. If not, instead connects to the IP.
55 *
56 * @param eventListener Listener that will receive events from the client.
57 * @param ip IP address to listen on or connect to.
58 * @param port Port to listen on or connect to.
59 */
60 public TCPChannelClient(
61 LooperExecutor executor, TCPChannelEvents eventListener, String ip, int po rt) {
62 this.executor = executor;
63 this.eventListener = eventListener;
64
65 InetAddress address;
66 try {
67 address = InetAddress.getByName(ip);
68 } catch (UnknownHostException e) {
69 reportError("Invalid IP address.");
70 return;
71 }
72
73 if (address.isAnyLocalAddress()) {
74 socket = new TCPSocketServer(address, port);
75 } else {
76 socket = new TCPSocketClient(address, port);
77 }
78
79 socket.start();
80 }
81
82 /**
83 * Disconnects the client if not already disconnected. This will fire the onTC PClose event.
84 */
85 public void disconnect() {
86 checkIfCalledOnValidThread();
87
88 socket.disconnect();
89 }
90
91 /**
92 * Sends a message on the socket.
93 *
94 * @param message Message to be sent.
95 */
96 public void send(String message) {
97 checkIfCalledOnValidThread();
98
99 socket.send(message);
100 }
101
102 /**
103 * Helper method for firing onTCPError events. Calls onTCPError on the executo r thread.
104 */
105 private void reportError(final String message) {
106 Log.e(TAG, "TCP Error: " + message);
107 executor.execute(new Runnable() {
108 @Override
109 public void run() {
110 eventListener.onTCPError(message);
111 }
112 });
113 }
114
115 /**
116 * Helper method for debugging purposes.
117 * Ensures that TCPChannelClient method is called on a looper thread.
118 */
119 private void checkIfCalledOnValidThread() {
120 if (!executor.checkOnLooperThread()) {
121 throw new IllegalStateException(
122 "TCPChannelClient method is not called on valid thread");
123 }
124 }
125
126
127 /**
128 * Base class for server and client sockets. Contains a listening thread that will call
129 * eventListener.onTCPMessage on new messages.
130 */
131 private abstract class TCPSocket extends Thread {
132 // Lock for editing out and rawSocket
133 protected final Object rawSocketLock;
134 private PrintWriter out;
135 private Socket rawSocket;
136
137 /**
138 * Connect to the peer, potentially a slow operation.
139 *
140 * @return Socket connection, null if connection failed.
141 */
142 public abstract Socket connect();
143 /** Returns true if sockets is a server rawSocket. */
144 public abstract boolean isServer();
145
146 TCPSocket() {
147 rawSocketLock = new Object();
148 }
149
150 /**
151 * The listening thread.
152 */
153 @Override
154 public void run() {
155 Log.d(TAG, "Listening thread started...");
156
157 // Receive connection to temporary variable first, so we don't block.
158 Socket tempSocket = connect();
159 BufferedReader in;
160
161 Log.d(TAG, "TCP connection established.");
162
163 synchronized (rawSocketLock) {
164 if (rawSocket != null) {
165 Log.e(TAG, "Socket already existed and will be replaced.");
166 }
167
168 rawSocket = tempSocket;
169
170 // Connecting failed, error has already been reported, just exit.
171 if (rawSocket == null) {
172 return;
173 }
174
175 try {
176 out = new PrintWriter(rawSocket.getOutputStream(), true);
177 in = new BufferedReader(new InputStreamReader(rawSocket.getInputStream ()));
178 } catch (IOException e) {
179 reportError("Failed to open IO on rawSocket: " + e.getMessage());
180 return;
181 }
182 }
183
184 Log.v(TAG, "Execute onTCPConnected");
185 executor.execute(new Runnable() {
186 @Override
187 public void run() {
188 Log.v(TAG, "Run onTCPConnected");
189 eventListener.onTCPConnected(isServer());
190 }
191 });
192
193 while (true) {
194 final String message;
195 try {
196 message = in.readLine();
197 } catch (IOException e) {
198 synchronized (rawSocketLock) {
199 // If socket was closed, this is expected.
200 if (rawSocket == null) {
201 break;
202 }
203 }
204
205 reportError("Failed to read from rawSocket: " + e.getMessage());
206 break;
207 }
208
209 // No data received, rawSocket probably closed.
210 if (message == null) {
211 break;
212 }
213
214 executor.execute(new Runnable() {
215 @Override
216 public void run() {
217 Log.v(TAG, "Receive: " + message);
218 eventListener.onTCPMessage(message);
219 }
220 });
221 }
222
223 Log.d(TAG, "Receiving thread exiting...");
224
225 // Close the rawSocket if it is still open.
226 disconnect();
227 }
228
229 /**
230 * Closes the rawSocket if it is still open. Also fires the onTCPClose event .
231 */
232 public void disconnect() {
233 try {
234 synchronized (rawSocketLock) {
235 if (rawSocket != null) {
236 rawSocket.close();
237 rawSocket = null;
238 out = null;
239
240 executor.execute(new Runnable() {
241 @Override
242 public void run() {
243 eventListener.onTCPClose();
244 }
245 });
246 }
247 }
248 } catch (IOException e) {
249 reportError("Failed to close rawSocket: " + e.getMessage());
250 }
251 }
252
253 /**
254 * Sends a message on the socket. Should only be called on the executor thre ad.
255 */
256 public void send(String message) {
257 Log.v(TAG, "Send: " + message);
258
259 synchronized (rawSocketLock) {
260 if (out == null) {
261 reportError("Sending data on closed socket.");
262 return;
263 }
264
265 out.write(message + "\n");
266 out.flush();
267 }
268 }
269 }
270
271 private class TCPSocketServer extends TCPSocket {
272 // Server socket is also guarded by rawSocketLock.
273 private ServerSocket serverSocket;
274
275 final private InetAddress address;
276 final private int port;
277
278 public TCPSocketServer(InetAddress address, int port) {
279 this.address = address;
280 this.port = port;
281 }
282
283 /** Opens a listening socket and waits for a connection. */
284 @Override
285 public Socket connect() {
286 Log.d(TAG, "Listening on [" + address.getHostAddress() + "]:" + Integer.to String(port));
287
288 final ServerSocket tempSocket;
289 try {
290 tempSocket = new ServerSocket(port, 0, address);
291 } catch (IOException e) {
292 reportError("Failed to create server socket: " + e.getMessage());
293 return null;
294 }
295
296 synchronized (rawSocketLock) {
297 if (serverSocket != null) {
298 Log.e(TAG, "Server rawSocket was already listening and new will be ope ned.");
299 }
300
301 serverSocket = tempSocket;
302 }
303
304 try {
305 return tempSocket.accept();
306 } catch (IOException e) {
307 reportError("Failed to receive connection: " + e.getMessage());
308 return null;
309 }
310 }
311
312 /** Closes the listening socket and calls super. */
313 @Override
314 public void disconnect() {
315 try {
316 synchronized (rawSocketLock) {
317 if (serverSocket != null) {
318 serverSocket.close();
319 serverSocket = null;
320 }
321 }
322 } catch (IOException e) {
323 reportError("Failed to close server socket: " + e.getMessage());
324 }
325
326 super.disconnect();
327 }
328
329 @Override
330 public boolean isServer() {
331 return true;
332 }
333 }
334
335 private class TCPSocketClient extends TCPSocket {
336 final private InetAddress address;
337 final private int port;
338
339 public TCPSocketClient(InetAddress address, int port) {
340 this.address = address;
341 this.port = port;
342 }
343
344 /** Connects to the peer. */
345 @Override
346 public Socket connect() {
347 Log.d(TAG, "Connecting to [" + address.getHostAddress() + "]:" + Integer.t oString(port));
348
349 try {
350 return new Socket(address, port);
351 } catch (IOException e) {
352 reportError("Failed to connect: " + e.getMessage());
353 return null;
354 }
355 }
356
357 @Override
358 public boolean isServer() {
359 return false;
360 }
361 }
362 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698