In my backend application, my load balancer uses round robin to distribute traffic. So, I have two A records against my hostname:
- x.x.x.x myapp.com
- y.y.y.y myapp.com
In any one of them is down it returns 503 Service Unavailable.
Now, in my Android application, I am using OkHttp 4.x to do network operations and the DNS resoulution is cached on the device. So, if the cahced IP goes down, it does not try to reach the other IP and the request fails. I tried multiple approaches to make it work but no cussess till now. The things I tried:
Add a Network interceptor and set Retry-After: 0 header to force it to retry the request.
Add a count variable and send the request again until count becomes 0
Set Connection Pool to .connectionPool(new ConnectionPool(0, 1, TimeUnit.MILLISECONDS)) to avoid caching the connection and retry the request
As per the docs, it says it should retry automatically on failed conenction, but it doesn't for status code 503. I verified this in the profiler and I could see only 1 request going.
To make sure both the IPs are resolved, I added the below code:
OkHttpClient client = new OkHttpClient.Builder().build();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
List<InetAddress> addr = client.dns().lookup("myapp.com");
for ( InetAddress a : addr ) {
Log.d(TAG, a.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
And I get both the entries in the log:
myapp.com/x.x.x.x
myapp.com/y.y.y.y
So, is there a way to make the OkHttp client try the other IP in case of failure?
This cannot be done by any app, unless you have access to your DNS server source to make him choose in which ip to send the request.
If the DNS records where editable by any means, like for example using a hosts file to determine to which IP a domain points, then you could change on the fly that file, and job done. but Android OS/IOS will not let you do that even if there was a hosts file.
You cannot choose to send the request to the a certain ip unless you know of a magic way to pass the domain name to that request in the headers and especially if it has an SSL on it.
The only way to fail over to one server, if the other is down, is if you place in your infrastructure a director which will know which of your two servers is up and running and routes the request to that one.
Related
I am creating a port scanner of a selected IP address, for my Android app. I use Runtime.getRuntime().exec("nc [host] [port]") to scan ports. My scanner only tests the top 100 ports, separately for TCP and UDP. Depending on the code for this process, I consider the port open or closed ("0" means the process was successful, so the port is open, any other number means failure).
override fun run() {
val response: String
val runtime = Runtime.getRuntime()
val process = runtime.exec("nc $host $port")
process.waitFor()
if (process.isAlive) {
process.destroy()
response = "$host:$port is closed"
} else {
val code = process.exitValue()
if (code == 0) {
response = "$host:$port is open"
} else {
response = "$host:$port is closed"
}
}
}
This method turned out to be only partially effective. When the host address is "8.8.8.8" or "8.8.4.4", the algorithm displays ports 53 and 443 for tcp as open. According to the Internet, this is true, so this case is successful. However, any other IP address returns all ports as closed, with a code of 1. These are both local network addresses and "global" addresses (sorry if I missed lingo). I should mention, that whole thing is running on separate threads, through executor service. It shouldn't matter though, result was the same with only one thread.
If all cases were unsuccessful, I would consider this a failure and try another solution, e.g. with the Socket class. However, the success at these two addresses haunts me. As you can read, I am not experienced in computer networks, protocols and the OSI model.
Do you know where the difference in the outcome of these cases comes from?
Our clients are starting to see 100s of these "SSLException error - Connection reset by peer" over the last couple of weeks and I can't figure out why
We're using Retrofit with okhttp, no special configuration
public class OkHttpClientProvider implements IOkHttpClientProvider {
OkHttpClient okHttpClient;
public OkHttpClientProvider() {
this.okHttpClient = createClient();
}
public OkHttpClient getOkHttpClient() {
return this.okHttpClient;
}
private OkHttpClient createClient() {
return new OkHttpClient();
}
}
The above client provider is a singleton. The RestAdapter is built using this injected client (we use dagger) -
RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder()
.setConverter(converter)
.setEndpoint(networkRequestDetails.getServerUrl())
.setClient(new OkClient(okHttpClientProvider.getOkHttpClient()))
.setErrorHandler(new NetworkSynchronousErrorHandler(eventBus))
);
Based on stack overflow solutions what I've found out -
The keep alive duration on the server is 180 seconds, OkHttp has a default of 300 seconds
The server returns "Connection: close" in its header but the client request sends "Connection: keepAlive"
The server supports TLS 1.0 / 1.1 / 1.2 and uses Open SSL
Our servers have moved to another hosting provider recently in another geography so I don't know if these are DNS failures or not
We've tried tweaking things like keepAlive, reconfigured OpenSSL on the server but for some reason the Android client keeps getting this error
It happens immediately without any delay when you try to use the app to post something or pull to refresh (it doesn't even go to network or have a delay before this exception happens which would imply the connection is already broken). But trying it multiple times somehow "fixes it" and we get a success. It happens again later
We've invalidated our DNS entries on the server to see if this what caused it but that hasn't helped
It mostly happens on LTE but I've seen it on Wifi as well
I don't want to disable keep alive because most modern clients don't do that. Also we're using OkHttp 2.4 and this is a problem on post Ice cream sandwich devices so I'm hoping it should take care of these underlying networking issues. The iOS client also gets these exceptions but close to a 100 times less (iOS client uses AFNetworking 2.0). I'm struggling to find new things to try at this point, any help / ideas?
Update - Adding full stack trace through okhttp
retrofit.RetrofitError: Read error: ssl=0x9dd07200: I/O error during system call, Connection reset by peer
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:390)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy15.getAccessTokenUsingResourceOwnerPasswordCredentials(Unknown Source)
at com.company.droid.repository.network.NetworkRepository.getAccessTokenUsingResourceOwnerPasswordCredentials(NetworkRepository.java:76)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:88)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:23)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: javax.net.ssl.SSLException: Read error: ssl=0x9dd07200: I/O error during system call, Connection reset by peer
at com.android.org.conscrypt.NativeCrypto.SSL_read(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:699)
at okio.Okio$2.read(Okio.java:137)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:306)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:300)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:196)
at com.squareup.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:191)
at com.squareup.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80)
at com.squareup.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:917)
at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:793)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:439)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:384)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:73)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:38)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy15.getAccessTokenUsingResourceOwnerPasswordCredentials(Unknown Source)
at com.company.droid.repository.network.NetworkRepository.getAccessTokenUsingResourceOwnerPasswordCredentials(NetworkRepository.java:76)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:88)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:23)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
]}
Recently I faced the issue while working on some legacy code. After googling I found that the issue is everywhere but without any concrete resolution. I worked on various parts of the exception message and analyzed below.
Analysis:
SSLException: exception happened with the SSL (Secure Socket Layer), which is implemented in javax.net.ssl package of the JDK (openJDK/oracleJDK/AndroidSDK)
Read error ssl=# I/O error during system call: Error occured while reading from the Secure socket. It happened while using the native system libraries/driver. Please note that all the platforms solaris, Windows etc. have their own socket libraries which is used by the SSL. Windows uses WINSOCK library.
Connection reset by peer: This message is reported by the system library (Solaris reports ECONNRESET, Windows reports WSAECONNRESET), that the socket used in the data transfer is no longer usable because an existing connection was forcibly closed by the remote host. One needs to create a new secure path between the host and client
Reason:
Understanding the issue, I try finding the reason behind the connection reset and I came up with below reasons:
The peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close.
This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with Network dropped connection on reset(On Windows(WSAENETRESET)) and Subsequent operations fail withConnection reset by peer(On Windows(WSAECONNRESET)).
If the target server is protected by Firewall, which is true in most of the cases, the Time to live (TTL) or timeout associated with the port forcibly closes the idle connection at given timeout. this is something of our interest
Resolution:
Events on the server side such as sudden service stop, rebooted, network interface disabled can not be handled by any means.
On the server side, Configure firewall for the given port with the higher Time to Live (TTL) or timeout values such as 3600 secs.
Clients can "try" keeping the network active to avoid or reduce the Connection reset by peer.
Normally on going network traffic keeps the connection alive and problem/exception is not seen frequently. Strong Wifi has least chances of Connection reset by peer.
With the mobile networks 2G, 3G and 4G where the packet data delivery is intermittent and dependent on the mobile network availability, it may not reset the TTL timer on the server side and results into the Connection reset by peer.
Here are the terms suggested to set on various forums to resolve the issue
ConnectionTimeout: Used only at the time out making the connection. If host takes time to connection higher value of this makes the client wait for the connection.
SoTimeout: Socket timeout-It says the maximum time within which the a data packet is received to consider the connection as active.If no data received within the given time, the connection is assumed as stalled/broken.
Linger: Upto what time the socket should not be closed when data is queued to be sent and the close socket function is called on the socket.
TcpNoDelay: Do you want to disable the buffer that holds and accumulates the TCP packets and send them once a threshold is reached? Setting this to true will skip the TCP buffering so that every request is sent immediately. Slowdowns in the network may be caused by an increase in network traffic due to smaller and more frequent packet transmission.
So none of the above parameter helps keeping the network alive and thus ineffective.
I found one setting that may help resolving the issue which is this functions
setKeepAlive(true)
setSoKeepalive(HttpParams params, enableKeepalive="true")
How did I resolve my issue?
Set the HttpConnectionParams.setSoKeepAlive(params, true)
Catch the SSLException and check for the exception message for Connection reset by peer
If exception is found, store the download/read progress and create a new connection.
If possible resume the download/read else restart the download
If using Nginx and getting a similar problem, then this might help:
Scan your domain on this sslTesturl, and see if the connection is allowed for your device version.
If lower version devices(like < Android 4.4.2 etc) are not able to connect due to TLS support, then try adding this to your Nginx config file,
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
we had this same issue starting this morning and goti it solved... hope this helps...
SSL on IIS 8
Everything was working fine yesterday and last night our SSL was updated on the IIS site.
While checking out the site Bindings to the SSL noticed that IIS8 has a new checkbox Require Server Name Indication, it was not checked so preceded to enable it.
That triggered the problem.
Went back to IIS, disabled the checkbox.... Problem Solved!!!!
Hope this helps!!!
Android Supports SSL implementation by default except for Android N (API level 24) and below Android 5.1 (API level 22)
I was getting the error when making the API call below API level 22 devices after implementing SSL at the server side; that was while creating OkHttpClient client object, and fixed by adding connectionSpecs() method OkHttpClient.Builder class.
the error received was
response failure: javax.net.ssl.SSLException: SSL handshake aborted:
ssl=0xb8882c00: I/O error during system call, Connection reset by peer
so I fixed this by added the check like
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
// Do something for below api level 22
List<ConnectionSpec> specsList = getSpecsBelowLollipopMR1(okb);
if (specsList != null) {
okb.connectionSpecs(specsList);
}
}
Also for the Android N (API level 24); I was getting the error while making the HTTP call like
HTTP FAILED: javax.net.ssl.SSLHandshakeException: Handshake failed
and this is fixed by adding the check for Android 7 particularly, like
if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N){
// Do something for naugat ; 7
okb.connectionSpecs(Collections.singletonList(getSpec()));
}
So my final OkHttpClient object will be like:
OkHttpClient client
HttpLoggingInterceptor httpLoggingInterceptor2 = new
HttpLoggingInterceptor();
httpLoggingInterceptor2.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder okb = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor2)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request request2 = request.newBuilder().addHeader(AUTH_KEYWORD, AUTH_TYPE_JW + " " + password).build();
return chain.proceed(request2);
}
}).connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS);
if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N){
// Do something for naugat ; 7
okb.connectionSpecs(Collections.singletonList(getSpec()));
}
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
List<ConnectionSpec> specsList = getSpecsBelowLollipopMR1(okb);
if (specsList != null) {
okb.connectionSpecs(specsList);
}
}
//init client
client = okb.build();
getSpecsBelowLollipopMR1 function be like,
private List<ConnectionSpec> getSpecsBelowLollipopMR1(OkHttpClient.Builder okb) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
okb.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
return specs;
} catch (Exception exc) {
Timber.e("OkHttpTLSCompat Error while setting TLS 1.2"+ exc);
return null;
}
}
The Tls12SocketFactory class will be found in below link (comment by gotev):
https://github.com/square/okhttp/issues/2372
For more support adding some links below this will help you in detail,
https://developer.android.com/training/articles/security-ssl
D/OkHttp: <-- HTTP FAILED: javax.net.ssl.SSLException: SSL handshake aborted: ssl=0x64e3c938: I/O error during system call, Connection reset by peer
Another possible cause for this error message is if the HTTP Method is blocked by the server or load balancer.
It seems to be standard security practice to block unused HTTP Methods. We ran into this because HEAD was being blocked by the load balancer (but, oddly, not all of the load balanced servers, which caused it to fail only some of the time). I was able to test that the request itself worked fine by temporarily changing it to use the GET method.
The error code on iOS was:
Error requesting App Code: Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
I was experiencing this error on Android 5.1.1 devices sending network requests using okhttp/4.0.0-RC1. Setting header Content-Length: <sizeof response> on the server side resolved the issue.
My problem was with TIMEZONE in emulator genymotion. Change TIMEZONE ANDROID EMULATOR equal TIMEZONE SERVER, solved problem.
reference
Well, gracefully handling exceptions can be very difficult.
ISSUE:
I got this javax.net.ssl.SSLException error whenever i make request to my web service knowing fully well that i have exhausted the internet data bundle i subscribed for, but my mobile app confirmed that i am connected to the internet after checking for Connectivity.NetworkAccess == NetworkAccess.Internet. And the worst part of this is that the javax.net.ssl.SSLException error crashes the app at any point the data bundle is exhausted.
MY CASE:
In my case, i was testing for what can go wrong if users tries to login when there is internet connection, but there is no data bundle subscription available for the particular ISP they are using. Well, the app crashes with javax.net.ssl.SSLException error, that was the result i got.
MY SOLUTION:
I searched Google on the best way to handle this, but the answers i got were specific to different persons so i decided to figure out how to implement mine.
Firstly, as general thing i surrounded my web request code with try-catch block as you can see below;
public static async Task<LoginResponse> Login(string email, string password)
{
try
{
var login = new Login()
{
email_address = email,
password = password
};
var httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(login);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(AppBaseUrl.ApiBaseUrl + "Users/LoginUser", content).ConfigureAwait(false);
var jsonResult = await response.Content.ReadAsStringAsync();
var apiResponse = JsonConvert.DeserializeObject<LoginResponse>(jsonResult);
return apiResponse;
}
catch (Exception ex)
{
return new LoginResponse
{
code = 0,
responsebody = null,
message = ex.Message
};
}
}
The focus of the above lines code is in the Catch block where i am returning the Login response from my API. Note: code = 0 means the web request was not successful.
And in my ViewModel i added the following lines of code below;
var loginResponse = await Login(Email, Password);
if (loginResponse.code == 1)
{
// Do something when Login is successful.
}
else
{
// Do something when Login is not successful.
await Application.Current.MainPage.DisplayAlert("Oops!", loginResponse.message + ". Try again", "Ok"); // This line will display a message like "Connection closed by peer. Try again"
}
I have attached the screenshot of my app running on a physical Android device.
Screenshot of the app
FINAL THOUGHT:
I still believe my own error handling method in this case can even be improved on. And what is good about this is that it's handled cross platform. However, the app don't crash anymore and i hope this helps someone out there.
All the questions here point to classes of the same app or different apps in separate processes yet in the same device. I would like to send data to and from two separate apps in two separate devices. I tried using broadcastreceiver but it didn't work. Here is my snippet to send the data.
addressstring = String.valueOf(acrilocation.getText());
if (addressstring != null && addressstring.length() > 0){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Constants.LOCATION_DATA_EXTRA, addressstring);
intent.setType("text/plain");
sendBroadcast(intent);
} else{
Toast.makeText(getApplicationContext(), "Enter valid location address", Toast.LENGTH_SHORT).show();
}
but when I receive the data in my other app using the following code snippet, It fails. When I debug the app I get null exception.
Intent intent = getIntent();
String action = intent.getAction();
String data = intent.getStringExtra(Intent.EXTRA_INTENT);
String type = intent.getType();
useraddress.setText(data);
startActivity(intent);
Is there another way to achieve this? I mean to send data to and from another app which is installed in another device?
Connecting over networks that accept incoming socket connections
The usual way to do this between Android devices (or between any peer devices) is to use sockets.
You set up one or both devices to 'listen' for connections on a socket and then accept a connection from the other when they want to communicate (or you can have a dedicated client and server and the client always initiates the connections).
Once the connection is established you can send messages back and forth.
There are many examples of Android client server socket applications, but one I found useful was:
Android Server/Client example - client side using Socket (and its companion server side blog article - link included in the client blog)
Note that you may have to add your own 'protocol' on top of this - for example if you are sending a file of unknown length without any special 'end' character, you may want to add a byte (or several byte to represent an int, long etc) at the start to indicate the length of the transmission so the receiving side knows when it has received everything (or that it has not received everything in case of an error).
Connecting over networks which do not allow incoming connections (e.g. most 3G/4G)
In these scenarios, while there is nothing theoretically stopping sockets working, in practice many mobile operators will not allow incoming socket connections. In addition you would need to find the public IP address of the Mobile, which is possible but is extra complexity. If your solution will only ever run on a single operators network you can experiment and see if it works, but if not you may find it better and easier to use a server in the 'middle':
Device A connectes to server
Device B connectes to server
Device A asks server for addresses of connected devices and 'discovers' device B
Device A send a message for device B. It actually sends the messages to the server with an indication that it is to be sent to device B
The server notifies device B that a message is available for it (using some sort of message notification like Google Cloud Messaging for example, or simply by the devices polling regularly to see if they have any messages).
Device B retrieves the messages from the server
The above will work on pretty much any network that allows connectivity to the internet. It does have the disadvantage of requiring a server but it is likely a necessary approach over most mobile networks.
If you want the two instances of your Android app on two different devices located on the different parts of the world to communicate with each other directly without the server, then the best way to do it is to use Tor Hidden Services. Tor Hidden Services allow the apps to bypass the firewall or NAT (if Tor is not blocked, of course), and the devices can easily communicate with each other without the need for a central server. Here, I will try to give some code examples that you can try. The best library suitable to this stuff is this.
Step 1: Add dependencies to your gradle.build in app module:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
dependencies {
compile 'com.github.jehy:Tor-Onion-Proxy-Library:0.0.7'
compile 'org.slf4j:slf4j-api:1.7.7'
compile 'org.slf4j:slf4j-android:1.7.7'
}
Step 2: Add permissions (Internet permissions or whatever) to your manifest file.
Step 3(i): Now we will just write the classic Client-Server programs in Java but with added Android and Tor flavor. To test this properly, try creating two different apps. One app will be the server and the other app will be a client. Preferably, you can even install the two apps on different phones.
In this example, we will try to send "Hello from Tor client" string from client app to server app.
For the server side: You can try this function inside any Activity and AsyncTask.
void server(Context context){
//For comments and documentation, visit the original repo
//https://github.com/thaliproject/Tor_Onion_Proxy_Library
String fileStorageLocation = "hiddenservicemanager";;
com.msopentech.thali.toronionproxy.OnionProxyManager onionProxyManager =
new com.msopentech.thali.android.toronionproxy.AndroidOnionProxyManager(context, fileStorageLocation);
int totalSecondsPerTorStartup = 4 * 60;
int totalTriesPerTorStartup = 5;
try {
boolean ok = onionProxyManager.startWithRepeat(totalSecondsPerTorStartup, totalTriesPerTorStartup);
if (!ok)
System.out.println("Couldn't start tor");
while (!onionProxyManager.isRunning())
Thread.sleep(90);
System.out.println("Tor initialized on port " + onionProxyManager.getIPv4LocalHostSocksPort());
int hiddenServicePort = 8080;
int localPort = 9343;
String onionAddress = onionProxyManager.publishHiddenService(hiddenServicePort, localPort);
System.out.println("Tor onion address of the server is: "+onionAddress);
ServerSocket serverSocket = new ServerSocket(localPort);
while(true) {
System.out.println("Waiting for client request");
Socket receivedSocket = serverSocket.accept();
ObjectInputStream ois = new ObjectInputStream(receivedSocket.getInputStream());
String message = (String) ois.readObject();
//Here we will print the message received from the client to the console.
/*You may want to modify this function to display the received
string in your View.*/
System.out.println("Message Received: " + message);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
Step 3(ii): For the client side try this function
//Inputs:
//'String onionAddress' should be the one obtained in server() function.
//It will be printed in the console and it will possibly remain the same
//even if the app restarts, because all the data/cache will be stored locally.
//Also, when you run the code for the first time, Tor will take about 1 or 2 mins
//to bootstrap. In the subsequent runs, Tor will start relatively faster as the
//data will be cached. 'int hiddenServicePort' is the port at which the hidden
//service has started on the server. In our example code, it is 8080. So, pass that here
void client(Context context, String onionAddress, int hiddenServicePort){
String fileStorageLocation = "clientmanager";
com.msopentech.thali.toronionproxy.OnionProxyManager onionProxyManager =
new com.msopentech.thali.android.toronionproxy.AndroidOnionProxyManager(context, fileStorageLocation);
int totalSecondsPerTorStartup = 4 * 60;
int totalTriesPerTorStartup = 5;
try {
boolean ok = onionProxyManager.startWithRepeat(totalSecondsPerTorStartup, totalTriesPerTorStartup);
int socksPort=onionProxyManager.getIPv4LocalHostSocksPort();
if (!ok)
System.out.println("Couldn't start tor in client");
while (!onionProxyManager.isRunning())
Thread.sleep(90);
System.out.println("Client Tor initialized on port " + socksPort);
System.out.println("Client is waiting for the server to get ready");
Thread.sleep(2000);
Socket clientSocket =
Utilities.socks4aSocketConnection(onionAddress, hiddenServicePort, "127.0.0.1", socksPort);
ObjectOutputStream oos = new ObjectOutputStream(clientSocket.getOutputStream());
oos.writeObject("Hello from Tor client\n");
System.out.println("Client has sent the message");
oos.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
It's done. Run your apps and test it. If you get stuck, try consulting here.
So, now your apps can communicate without any central server. Tor Hidden Services are so awesome in these use cases.
You can also use IP6, then you can do a direct socket connection from one phone to the another. I got latency as low as 60ms between two phones on different 4G operators (in the same country though). Note that you have to send some data to avoid getting down switch to lower speed to get such low latency. 10 concurrent ping was enough for me.
The listen side doesn't need any change at all, the client side just has to use an IP6-address:
s = new Socket("2a10:811:21c:22a1:7683:ae1:18c7:9827", 9343);
IP6 seems to be supported by many operators. If not, tor can be a good fallback, if latency isn't a problem.
Lets say there are two android devices and a webpage.
On each of those devices there is a button being clicked and the webpage shows the accumulated clicks.
What makes this possible?
Are the android devices sending that data to a sql database and that counter is reading what currently is in that database?
Or can the device really send the information into the page?
The device can communicate data to the server or to the client side code. You can then have to decide what to do with the data. If you send it to the client side code then it would only update on one device, whereas sending it to the server would allow one click counter across every device.
Changing the page on the server side would add caching problems. So you probably want a data file instead, just a json or something that the server overwrites every time it receives another click.
This isn't very fast though and causes synchronisation issues between the users. For starters for a standard html page the client code is in charge of the calls to the server, so we would have to check the server periodically to see if the value has changed (say every 5 seconds).
A faster alternative would be to use a web socket. The client would keep a connection open to the server and listen for updates from the server, removing the need for our periodic check. Additionally the socket can be used to send clicks to the server and the server can keep the value in its memory removing the need for file writes.
Node.js can be downloaded from http://nodejs.org and this is the plug in I use for web sockets https://github.com/Worlize/WebSocket-Node.
So here is some server side code for a simple web socket server that parrots messages from 1 user to all users, including the user who sent it.
var connections = [];
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
// process HTTP request. Since we're writing just WebSockets server
// we don't have to implement anything.
});
server.listen(1337, function() { });
// create the server
wsServer = new WebSocketServer({
httpServer: server
});
// WebSocket server
wsServer.on('request', function(request) {
//got a new user requesting a connection, so lets accept and store them
var connection = request.accept(null, request.origin);
connections.push(connection);
connection.on('message', function(message) { //inbound message
if (message.type === 'utf8') {
// process WebSocket message
send(message.utf8Data); //bounce to everyone else
console.log(message);
}
});
});
setInterval(function(){console.log(connections.length +" :users");},5000);
//every 5 seconds, tell us how many users we have
function send(message){
var i = connections.length;
while(i--)
connections[i].send(message);
//send the message to all users
}
Example client side
<html><head><script>
var connection, connIsActive = false;
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
connection = new WebSocket('ws://127.0.0.1:1337');
connection.onopen = function () {
// connection is opened and ready to use
connIsActive = true;
console.log('Connection loaded');
};
connection.onerror = function (error) {
// an error occurred when sending/receiving data
connIsActive = false
console.log('error');
};
connection.onmessage = function (message) {
// handle incoming message
console.log(message.data);
};
function send(message){
if(connIsActive)connection.send(message);
}
</script></head></html>
Save the first snippet as "myserver.js" (or similar) and start it up in node via command line. Save the second snippet as a "client.html" and open it on 2 devices, or just 2 browser instances. Theres no interface on the client but you can send and receive messages from the debug console using send("message string")
Provided you can wrap your head around my snippets, modifying it to work like your example should prove fairly easy!
To achieve what you are trying to do, you need to learn socket programming for android.
In order to use two device(clients) you also need to learn port forwarding for servers.
To begin try this tutorial.
I am facing a tricky situation while working on a restricted network. Though I have my System Proxy Set to connect to my Web Server, Below are the two different behaviors observed from Web Browser and from Android Sockets .
1) With Proxy set, The Web Browser Request for the my server URL (host:port) goes well and I get 200 OK with valid Response Data from the Server.
2) With Android App, I do Host Reachability check before making my Connection Request. In Host reach-ability check , I create a java.net Socket and if it returns without any exception i consider my host as reachable. PSB for code snippet.
public static boolean isHostReachable(String hostname, int port) {
boolean isReachable = false;
Socket socket = null;
try {
socket = new Socket(hostname, port);
isReachable = true;
} catch (Exception e) {
.....
} finally {
....
}
return isReachable;
}
The whole logic of Host Reachability check works fine when in work from unrestricted network (lets say my home network).
The problem comes in the Host Reachability Socket Call , when I run my app from a restricted network (with Proxy Set to access my Server). Here my Socket Creation call does not return leading to host reach ability failure !!
There is a clear discrepancy between the browser and my app behavior in restricted network!!
My Question : While the Web Appllication works perfectly fine, What can be reason for the failure of above Socket Creation from Android leading to my Host Reachability failure in restricted network (with Proxy set in System Preference) and any suggestion for me to overcome this ?