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

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: Removed empty param description since they are not allowed according to the style guide. 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 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 while (true) {
187 final String message;
188 try {
189 message = in.readLine();
190 } catch (IOException e) {
191 reportError("Failed to read from rawSocket: " + e.getMessage());
192 break;
193 }
194
195 // No data received, rawSocket probably closed
196 if (message == null) {
197 break;
198 }
199
200 executor.execute(new Runnable() {
201 @Override
202 public void run() {
203 Log.v(TAG, "Receive: " + message);
204 eventListener.onTCPMessage(message);
205 }
206 });
207 }
208
209 Log.d(TAG, "Receiving thread exiting...");
210
211 // Close the rawSocket if it is still open
212 disconnect();
213 }
214
215 /**
216 * Closes the rawSocket if it is still open. Also fires the onTCPClose event .
217 */
218 public void disconnect() {
219 try {
220 synchronized (rawSocketLock) {
221 if (rawSocket != null) {
222 rawSocket.close();
223 rawSocket = null;
224 out = null;
225
226 eventListener.onTCPClose();
227 }
228 }
229 } catch (IOException e) {
230 reportError("Failed to close rawSocket: " + e.getMessage());
231 }
232 }
233
234 /**
235 * Sends a message on the socket. Should only be called on the executor thre ad.
236 */
237 public void send(String message) {
238 Log.v(TAG, "Send: " + message);
239
240 synchronized (rawSocketLock) {
241 if (out == null) {
242 reportError("Sending data on closed socket.");
243 return;
244 }
245
246 out.write(message + "\n");
247 out.flush();
248 }
249 }
250 }
251
252 private class TCPSocketServer extends TCPSocket {
253 // Server socket is also guarded by rawSocketLock.
254 private ServerSocket serverSocket;
255
256 final private InetAddress address;
257 final private int port;
258
259 public TCPSocketServer(InetAddress address, int port) {
260 this.address = address;
261 this.port = port;
262 }
263
264 /** Opens a listening socket and waits for a connection. */
265 @Override
266 public Socket connect() {
267 Log.d(TAG, "Listening on [" + address.getHostAddress() + "]:" + Integer.to String(port));
268
269 synchronized (rawSocketLock) {
270 if (serverSocket != null) {
271 Log.e(TAG, "Server rawSocket was already listening and new will be ope ned.");
272 }
273
274 try {
275 serverSocket = new ServerSocket(port, 0, address);
276 return serverSocket.accept();
277 } catch (IOException e) {
278 reportError("Failed to receive connection: " + e.getMessage());
279 return null;
280 }
281 }
282 }
283
284 /** Closes the listening socket and calls super. */
285 @Override
286 public void disconnect() {
287 try {
288 synchronized (rawSocketLock) {
289 if (serverSocket != null) {
290 serverSocket.close();
291 serverSocket = null;
292 }
293 }
294 } catch (IOException e) {
295 reportError("Failed to close server socket: " + e.getMessage());
296 }
297
298 super.disconnect();
299 }
300
301 @Override
302 public boolean isServer() {
303 return true;
304 }
305 }
306
307 private class TCPSocketClient extends TCPSocket {
308 final private InetAddress address;
309 final private int port;
310
311 public TCPSocketClient(InetAddress address, int port) {
312 this.address = address;
313 this.port = port;
314 }
315
316 /** Connects to the peer. */
317 @Override
318 public Socket connect() {
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 @Override
330 public boolean isServer() {
331 return false;
332 }
333 }
334 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698