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

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: 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 2015 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, in t port) {
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 executor.execute(new Runnable() {
107 @Override
108 public void run() {
109 eventListener.onTCPError(message);
110 }
111 });
112 }
113
114 /**
115 * Helper method for debugging purposes.
116 * Ensures that TCPChannelClient method is called on a looper thread.
117 */
118 private void checkIfCalledOnValidThread() {
119 if (!executor.checkOnLooperThread()) {
120 throw new IllegalStateException(
121 "TCPChannelClient method is not called on valid thread");
122 }
123 }
124
125
126 /**
127 * Base class for server and client sockets. Contains a listening thread that will call
128 * eventListener.onTCPMessage on new messages.
129 */
130 private abstract class TCPSocket extends Thread {
131 // Lock for editing out and rawSocket
132 protected final Object rawSocketLock;
133 private PrintWriter out;
134 private Socket rawSocket;
135
136 /**
137 * Connect to the peer, potentially a slow operation.
138 *
139 * @return Socket connection, null if connection failed.
140 */
141 public abstract Socket connect();
142 /** Returns true if sockets is a server rawSocket. */
143 public abstract boolean isServer();
144
145 TCPSocket() {
146 rawSocketLock = new Object();
147 }
148
149 /**
150 * The listening thread.
151 */
152 @Override
153 public void run() {
154 // Receive connection to temporary variable first, so we don't block
155 Socket tempSocket = connect();
156 BufferedReader in;
157
158 synchronized (rawSocketLock) {
159 if (rawSocket != null) {
160 Log.e(TAG, "Socket already existed and will be replaced.");
161 }
162
163 rawSocket = tempSocket;
164
165 // Connecting failed, error has already been reported, just exit
166 if (rawSocket == null) {
167 return;
168 }
169
170 try {
171 out = new PrintWriter(rawSocket.getOutputStream(), true);
172 in = new BufferedReader(new InputStreamReader(rawSocket.getInputStream ()));
173 } catch (IOException e) {
174 reportError("Failed to open IO on rawSocket: " + e.getMessage());
175 return;
176 }
177 }
178
179 executor.execute(new Runnable() {
180 @Override
181 public void run() {
182 eventListener.onTCPConnected(isServer());
183 }
184 });
185
186 String message;
187 while (true) {
188 try {
189 message = in.readLine();
190 } catch (IOException e) {
191 reportError("Failed to read from rawSocket: " + e.getMessage());
192 return;
magjed_webrtc 2016/05/10 14:50:02 break instead of return?
sakal 2016/05/11 08:38:50 Yep, you're right.
193 }
194
195 // No data received, rawSocket probably closed
196 if (message == null) {
197 break;
198 }
199
200 // We need final variable for the Runnable
201 final String executorMessage = message;
magjed_webrtc 2016/05/10 14:50:03 Move declaration of |message| inside while loop an
sakal 2016/05/11 08:38:50 Done.
202 executor.execute(new Runnable() {
203 @Override
204 public void run() {
205 Log.v(TAG, "Receive: " + executorMessage);
206 eventListener.onTCPMessage(executorMessage);
207 }
208 });
209 }
210
211 Log.d(TAG, "Receiving thread exiting...");
212
213 // Close the rawSocket if it is still open
214 disconnect();
215 }
216
217 /**
218 * Closes the rawSocket if it is still open. Also fires the onTCPClose event .
219 */
220 public void disconnect() {
221 try {
222 synchronized (rawSocketLock) {
223 if (rawSocket != null) {
224 rawSocket.close();
225 rawSocket = null;
226 out = null;
227
228 eventListener.onTCPClose();
229 }
230 }
231 } catch (IOException e) {
magjed_webrtc 2016/05/10 14:50:03 Catch just rawSocket.close() so that eventListener
sakal 2016/05/11 08:38:50 This is something I thought about. eventListener.o
magjed_webrtc 2016/05/11 11:22:55 Probably not. Then the code is good as it is.
232 reportError("Failed to close rawSocket: " + e.getMessage());
233 }
234 }
235
236 /**
237 * Sends a message on the socket. Should only be called on the executor thre ad.
238 *
239 * @param message
240 */
241 public void send(String message) {
242 Log.v(TAG, "Send: " + message);
243
244 synchronized (rawSocketLock) {
245 if (out == null) {
246 Log.e(TAG, "Sending data on closed socket.");
magjed_webrtc 2016/05/10 14:50:03 You need to return here, otherwise it will crash o
sakal 2016/05/11 08:38:50 Correct, fixed. Also changed this to reportError b
247 }
248
249 out.write(message + "\n");
250 out.flush();
251 }
252 }
253 }
254
255 private class TCPSocketServer extends TCPSocket {
256 // Server socket also guarded by rawSocketLock
257 private ServerSocket serverSocket;
258
259 private InetAddress address;
magjed_webrtc 2016/05/10 14:50:03 Make these variables final.
sakal 2016/05/11 08:38:50 Done.
260 private int port;
261
262 public TCPSocketServer(InetAddress address, int port) {
263 this.address = address;
264 this.port = port;
265 }
266
267 /** Opens a listening socket and waits for a connection. */
268 public Socket connect() {
269 Log.d(TAG, "Listening on " + address.getHostAddress() + ":" + Integer.toSt ring(port));
270
271 synchronized (rawSocketLock) {
272 if (serverSocket != null) {
273 Log.e(TAG, "Server rawSocket was already listening and new will be ope ned.");
274 }
275
276 try {
277 serverSocket = new ServerSocket(port, 0, address);
278 return serverSocket.accept();
279 } catch (IOException e) {
280 reportError("Failed to receive connection: " + e.getMessage());
281 return null;
282 }
283 }
284 }
285
286 /** Closes the listening socket and calls super. */
287 @Override
288 public void disconnect() {
289 try {
290 synchronized (rawSocketLock) {
291 if (serverSocket != null) {
292 serverSocket.close();
293 serverSocket = null;
294 }
295 }
296 } catch (IOException e) {
297 reportError("Failed to close server socket: " + e.getMessage());
298 }
299
300 super.disconnect();
301 }
302
303 public boolean isServer() {
magjed_webrtc 2016/05/10 14:50:03 add @Override?
sakal 2016/05/11 08:38:50 Done.
304 return true;
305 }
306 }
307
308 private class TCPSocketClient extends TCPSocket {
309 private InetAddress address;
magjed_webrtc 2016/05/10 14:50:02 Make these variables final.
sakal 2016/05/11 08:38:50 Done.
310 private int port;
311
312 public TCPSocketClient(InetAddress address, int port) {
313 this.address = address;
314 this.port = port;
315 }
316
317 /** Connects to the peer. */
318 public Socket connect() {
magjed_webrtc 2016/05/10 14:50:03 add @Override?
sakal 2016/05/11 08:38:50 Done.
319 Log.d(TAG, "Connecting to [" + address.getHostAddress() + "]:" + Integer.t oString(port));
320
321 try {
322 return new Socket(address, port);
323 } catch (IOException e) {
324 reportError("Failed to connect: " + e.getMessage());
325 return null;
326 }
327 }
328
329 public boolean isServer() {
magjed_webrtc 2016/05/10 14:50:03 add @Override?
sakal 2016/05/11 08:38:50 Done.
330 return false;
331 }
332 }
333 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698