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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: webrtc/examples/androidapp/src/org/appspot/apprtc/TCPChannelClient.java
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/TCPChannelClient.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/TCPChannelClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..d348cda653c0f7edffa288424f389b2d188d4bf4
--- /dev/null
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/TCPChannelClient.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.appspot.apprtc;
+
+import android.util.Log;
+
+import org.appspot.apprtc.util.LooperExecutor;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+ * Replacement for WebSocketChannelClient for direct communication between two IP addresses. Handles
+ * the signaling between the two clients using a TCP connection.
+ *
+ * <p>All public methods should be called from a looper executor thread
+ * passed in a constructor, otherwise exception will be thrown.
+ * All events are dispatched on the same thread.
+ */
+public class TCPChannelClient {
+ private static final String TAG = "TCPChannelClient";
+
+ private final LooperExecutor executor;
+ private final TCPChannelEvents eventListener;
+ private TCPSocket socket;
+
+ /**
+ * Callback interface for messages delivered on TCP Connection. All callbacks are invoked from the
+ * looper executor thread.
+ */
+ public interface TCPChannelEvents {
+ void onTCPConnected(boolean server);
+ void onTCPMessage(String message);
+ void onTCPError(String description);
+ void onTCPClose();
+ }
+
+ /**
+ * Initializes the TCPChannelClient. If IP is a local IP address, starts a listening server on
+ * that IP. If not, instead connects to the IP.
+ *
+ * @param eventListener Listener that will receive events from the client.
+ * @param ip IP address to listen on or connect to.
+ * @param port Port to listen on or connect to.
+ */
+ public TCPChannelClient(
+ LooperExecutor executor, TCPChannelEvents eventListener, String ip, int port) {
+ this.executor = executor;
+ this.eventListener = eventListener;
+
+ InetAddress address;
+ try {
+ address = InetAddress.getByName(ip);
+ } catch (UnknownHostException e) {
+ reportError("Invalid IP address.");
+ return;
+ }
+
+ if (address.isAnyLocalAddress()) {
+ socket = new TCPSocketServer(address, port);
+ } else {
+ socket = new TCPSocketClient(address, port);
+ }
+
+ socket.start();
+ }
+
+ /**
+ * Disconnects the client if not already disconnected. This will fire the onTCPClose event.
+ */
+ public void disconnect() {
+ checkIfCalledOnValidThread();
+
+ socket.disconnect();
+ }
+
+ /**
+ * Sends a message on the socket.
+ *
+ * @param message Message to be sent.
+ */
+ public void send(String message) {
+ checkIfCalledOnValidThread();
+
+ socket.send(message);
+ }
+
+ /**
+ * Helper method for firing onTCPError events. Calls onTCPError on the executor thread.
+ */
+ private void reportError(final String message) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ eventListener.onTCPError(message);
+ }
+ });
+ }
+
+ /**
+ * Helper method for debugging purposes.
+ * Ensures that TCPChannelClient method is called on a looper thread.
+ */
+ private void checkIfCalledOnValidThread() {
+ if (!executor.checkOnLooperThread()) {
+ throw new IllegalStateException(
+ "TCPChannelClient method is not called on valid thread");
+ }
+ }
+
+
+ /**
+ * Base class for server and client sockets. Contains a listening thread that will call
+ * eventListener.onTCPMessage on new messages.
+ */
+ private abstract class TCPSocket extends Thread {
+ // Lock for editing out and rawSocket
+ protected final Object rawSocketLock;
+ private PrintWriter out;
+ private Socket rawSocket;
+
+ /**
+ * Connect to the peer, potentially a slow operation.
+ *
+ * @return Socket connection, null if connection failed.
+ */
+ public abstract Socket connect();
+ /** Returns true if sockets is a server rawSocket. */
+ public abstract boolean isServer();
+
+ TCPSocket() {
+ rawSocketLock = new Object();
+ }
+
+ /**
+ * The listening thread.
+ */
+ @Override
+ public void run() {
+ // Receive connection to temporary variable first, so we don't block
+ Socket tempSocket = connect();
+ BufferedReader in;
+
+ synchronized (rawSocketLock) {
+ if (rawSocket != null) {
+ Log.e(TAG, "Socket already existed and will be replaced.");
+ }
+
+ rawSocket = tempSocket;
+
+ // Connecting failed, error has already been reported, just exit
+ if (rawSocket == null) {
+ return;
+ }
+
+ try {
+ out = new PrintWriter(rawSocket.getOutputStream(), true);
+ in = new BufferedReader(new InputStreamReader(rawSocket.getInputStream()));
+ } catch (IOException e) {
+ reportError("Failed to open IO on rawSocket: " + e.getMessage());
+ return;
+ }
+ }
+
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ eventListener.onTCPConnected(isServer());
+ }
+ });
+
+ String message;
+ while (true) {
+ try {
+ message = in.readLine();
+ } catch (IOException e) {
+ reportError("Failed to read from rawSocket: " + e.getMessage());
+ return;
magjed_webrtc 2016/05/10 14:50:02 break instead of return?
sakal 2016/05/11 08:38:50 Yep, you're right.
+ }
+
+ // No data received, rawSocket probably closed
+ if (message == null) {
+ break;
+ }
+
+ // We need final variable for the Runnable
+ 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.
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ Log.v(TAG, "Receive: " + executorMessage);
+ eventListener.onTCPMessage(executorMessage);
+ }
+ });
+ }
+
+ Log.d(TAG, "Receiving thread exiting...");
+
+ // Close the rawSocket if it is still open
+ disconnect();
+ }
+
+ /**
+ * Closes the rawSocket if it is still open. Also fires the onTCPClose event.
+ */
+ public void disconnect() {
+ try {
+ synchronized (rawSocketLock) {
+ if (rawSocket != null) {
+ rawSocket.close();
+ rawSocket = null;
+ out = null;
+
+ eventListener.onTCPClose();
+ }
+ }
+ } 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.
+ reportError("Failed to close rawSocket: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Sends a message on the socket. Should only be called on the executor thread.
+ *
+ * @param message
+ */
+ public void send(String message) {
+ Log.v(TAG, "Send: " + message);
+
+ synchronized (rawSocketLock) {
+ if (out == null) {
+ 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
+ }
+
+ out.write(message + "\n");
+ out.flush();
+ }
+ }
+ }
+
+ private class TCPSocketServer extends TCPSocket {
+ // Server socket also guarded by rawSocketLock
+ private ServerSocket serverSocket;
+
+ private InetAddress address;
magjed_webrtc 2016/05/10 14:50:03 Make these variables final.
sakal 2016/05/11 08:38:50 Done.
+ private int port;
+
+ public TCPSocketServer(InetAddress address, int port) {
+ this.address = address;
+ this.port = port;
+ }
+
+ /** Opens a listening socket and waits for a connection. */
+ public Socket connect() {
+ Log.d(TAG, "Listening on " + address.getHostAddress() + ":" + Integer.toString(port));
+
+ synchronized (rawSocketLock) {
+ if (serverSocket != null) {
+ Log.e(TAG, "Server rawSocket was already listening and new will be opened.");
+ }
+
+ try {
+ serverSocket = new ServerSocket(port, 0, address);
+ return serverSocket.accept();
+ } catch (IOException e) {
+ reportError("Failed to receive connection: " + e.getMessage());
+ return null;
+ }
+ }
+ }
+
+ /** Closes the listening socket and calls super. */
+ @Override
+ public void disconnect() {
+ try {
+ synchronized (rawSocketLock) {
+ if (serverSocket != null) {
+ serverSocket.close();
+ serverSocket = null;
+ }
+ }
+ } catch (IOException e) {
+ reportError("Failed to close server socket: " + e.getMessage());
+ }
+
+ super.disconnect();
+ }
+
+ public boolean isServer() {
magjed_webrtc 2016/05/10 14:50:03 add @Override?
sakal 2016/05/11 08:38:50 Done.
+ return true;
+ }
+ }
+
+ private class TCPSocketClient extends TCPSocket {
+ private InetAddress address;
magjed_webrtc 2016/05/10 14:50:02 Make these variables final.
sakal 2016/05/11 08:38:50 Done.
+ private int port;
+
+ public TCPSocketClient(InetAddress address, int port) {
+ this.address = address;
+ this.port = port;
+ }
+
+ /** Connects to the peer. */
+ public Socket connect() {
magjed_webrtc 2016/05/10 14:50:03 add @Override?
sakal 2016/05/11 08:38:50 Done.
+ Log.d(TAG, "Connecting to [" + address.getHostAddress() + "]:" + Integer.toString(port));
+
+ try {
+ return new Socket(address, port);
+ } catch (IOException e) {
+ reportError("Failed to connect: " + e.getMessage());
+ return null;
+ }
+ }
+
+ public boolean isServer() {
magjed_webrtc 2016/05/10 14:50:03 add @Override?
sakal 2016/05/11 08:38:50 Done.
+ return false;
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698