I have written an app to start my server at home remotely. The app works without problems in the emulator and also on my smartphone (HTC desire, Android 2.2) when WiFi is enabled.
However it does not work when WiFi is disabled.
Before restarting I first check if it's already running. To do this I use sockets and I first connect to a dyndns address. After that I try to connect to my ip-box where I can switch on my computer by sending commands via a socket.
When the connection to that socket fails I know the server is not running.
The relevant code is:
socket = new Socket();
socket.connect(new InetSocketAddress(serverName, port), 10000);
status = socket.isConnected() == true;
socket.close();
If there's an exception (SocketException) I know that the server is not running.
This approach works perfectly when I have switched WiFi on. However if WiFi's not switched on then the connect always says it's ok, even if it could not establish a connection since the server is not available.
Is there a way to check if the the connection is really established, even if WiFi is disabled?
Any suggestions welcome!
Thanks,
Rudi
Try to open your socket like this :
public boolean connect(String ip, int port) {
try {
this.clientSocket = new Socket(ip, port);
clientSocket.setSoTimeout(timeout);
this.outToServer = new DataOutputStream(clientSocket
.getOutputStream());
this.inFromServer = clientSocket.getInputStream();
isconnected = true;
} catch (IOException e) {
Log.e("TCPclient", "connection failed on " + ip + ":" + port);
isconnected = false;
return isconnected;
}
Log.e("TCPclient", "connection to " + ip + " sucessfull");
return isconnected;
}
If connection is not successful , it will generate an IOException (work when wifi enabled and no server , and when wifi is not enabled(HTC desire 2.3)).
This code is not really correct ,it's just a short version
EDIT
Try to check wfi state like this (it is not practical but it should work)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (ni.isConnected()) {
Toast.makeText(this,"Wifi enabled", Toast.LENGTH_LONG).show();
Log.d("WiFiStateTestActivity", "WiFi!");
} else {
Toast.makeText(this,"Wifi not enabled", Toast.LENGTH_LONG).show();
Log.d("WiFiStateTestActivity", "not WiFi!");
}
}
Don't forget to set the permission in manifest.xml to allow you app to open a socket.
Related
I am required to design an application in Android which requires the phone to connect to a server by opening a socket. I am able to achieve this when I am just connected to the particular wifi network (ie the Wifi network which hosts the server ) but in a situation when I am connected to the wifi network and the Mobile data network I get a socket exception thrown as android tries to connect the socket over the mobile network
I have already been able to connect the device when its just connected to the wifi of the device that needs the socket connection to be established
static class StartTCPconnection extends AsyncTask<Void, Void, Void> {
final WeakReference<RemoteActivity> activity;
StartTCPconnection(WeakReference<RemoteActivity> activity) {
this.activity = activity;
}
#Override
protected Void doInBackground(Void... voids) {
try {
socket = new Socket("192.168.4.1", 900);
Log.d(TAG, "is socket connected ? ...." + socket.isConnected());
printWriter = new PrintWriter(socket.getOutputStream(), true);
Log.i(TAG, "Checking if socket is really connected " + (socket.getLocalSocketAddress()));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (socket != null) {
if (socket.isConnected() && isWifi) {
Log.d(TAG, "onPostExecute: " + socket.isConnected());
Toast.makeText(activity.get(), "Connection established", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onPostExecute: " + activity.get().getSharedPreferences(Constants.REMOTE_SWITCH_SHARED_PREFERENCE, Context.MODE_PRIVATE).getInt(Constants.REMOTE_SWITCH_KEY, 99));
if (activity.get().getSharedPreferences(Constants.REMOTE_SWITCH_SHARED_PREFERENCE, Context.MODE_PRIVATE).getInt(Constants.REMOTE_SWITCH_KEY, 1) == 1) {
activity.get().joyStickFragment.checkSocketInstance(socket);
activity.get().joyStickFragment.changeUIForConnect();
} else if (activity.get().getSharedPreferences(Constants.REMOTE_SWITCH_SHARED_PREFERENCE, Context.MODE_PRIVATE).getInt(Constants.REMOTE_SWITCH_KEY, 1) == 2) {
Log.e(TAG, "onPostExecute:Check " + socket.isConnected());
activity.get().buttonRemoteFragment.checkSocketInstance(socket);
activity.get().buttonRemoteFragment.changeUIForConnect();
}
activity.get().connectionIndicatorImage.setImageResource(R.drawable.avishkaar_logo_on);
activity.get().wifiIndicator.setImageResource(R.drawable.wifi_connected_icon);
}
} else {
// Toast.makeText(activity.get(), "Wrong Wifi Network connected", Toast.LENGTH_SHORT).show();
}
}
}
The above mentioned code connects me to the socket if the only network available is the WiFi of the device and the mobile network is turned off
There is nothing special from the programming perspective between connecting to a server which is in the local network and a server which is not. The only requirement is that the server is actually reachable in the first place, i.e. not in a private unreachable network and not blocked by a firewall or similar. And of course that the public reachable address of the server is used as destination in the program.
socket = new Socket("192.168.4.1", 900);
192.168.4.1 is an address in a private network. This means it is not accessible from the internet, which also means that it cannot be reached if your are connecting with mobile data or if you use wifi within a different network (like a public hotspot).
To make a connection from outside this private network possible the server must be reachable from outside this network, i.e. needs to have a public routable IP address. If the server is in some typical home network this can be achieved with port forwarding in the router. For larger setups such servers are located at data centers directly reachable from the internet or (as a special case of this) in the cloud.
everyone!
I'm developing an Android app that allows to chat with nearby devices that have installed this app. In order to accomplish this, I'm using the Wi-Fi P2P API and Network Service Discovery to search for such nearby devices.
I've written the code for searching the nearby devices in a thread started by a Service. When a device is detected, the Service sends it (through a broadcast intent) to an Activity which displays the devices detected so far.
The detected devices are added to a recyclerView and, when the user presses one of them, a connection must be established to such device.
The Wi-Fi Direct connection gets established successfully (that is, the WifiP2pManager.connect() method succeeds) and the WIFI_P2P_CONNECTION_CHANGED_ACTION is caught.
In the broadcast receiver, when such broadcast intent is caught, the following code is executed:
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
mManager.requestConnectionInfo(mChannel, connectionInfoListener); }
With the requestConnectionInfo() method I can obtain more information about the connection, such as the IP address of the device I'm trying to connect to.
To obtain such information, I provide an implementation of WifiP2pManager.ConnectionInfoListener to that method, which is denoted by the connectionInfoListener variable.
This is the code of my implementation of WifiP2pManager.ConnectionInfoListener:
private WifiP2pManager.ConnectionInfoListener connectionInfoListener = new WifiP2pManager.ConnectionInfoListener() {
#Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
InetAddress deviceIP = info.groupOwnerAddress;
int port = servicesConnectionInfo.get(device);
ConnectThread connectThread = new ConnectThread(deviceIP, port, device);
connectThread.start();
"device" is an instance variable of my implementation of BroadcastReceiver which is not important right now. What is important, instead, is the ConnectThread thread. That's the thread that handles the code necessary to connect the socket between the two devices. When I try to connect to a detected device, ConnectThread, in its run() method, creates a new instance of ChatConnection passing the IP address and the port number previously obtained to this constructor:
public ChatConnection(InetAddress srvAddress, int srvPort, String macAddress) throws IOException {
...
connSocket = new Socket(srvAddress, srvPort);
...
}
And here is where the problem occurs. When I test my app on my physical device, all I get is this exception:
W/System.err: java.net.ConnectException: failed to connect to /192.168.49.1 (port 6770): connect failed: ECONNREFUSED (Connection refused)
Of course, I installed my app on a second physical device too, which gets successfully detected and a Wi-Fi Direct connection gets successfully established. But, when comes to this line of code:
connSocket = new Socket(srvAddress, srvPort);
that exception is thrown...
I apologize for the length of this question, but I wanted to be the clearest possible.
I really thank you in advance for any help.
EDIT: I forgot to mention the code for initializing the ServerSocket.
The ServerSocket is initialized in a thread that is started as soon as the Wi-Fi is enabled.
That is, when the WifiP2pBroadcastReceiver (an inner class of the app's Service which extends BroadcastReceiver) catches a WIFI_P2P_STATE_CHANGED_ACTION intent, it checks if the Wi-Fi is enabled and, if enabled, it starts the the thread where the ServerSocket is located:
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
int statoWiFi = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (statoWiFi == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
mNsdService = new NsdProviderThread();
mNsdService.start();
}
The ServerSocket is initialized in the run() method of NsdProviderThread:
public void run() {
...
try {
server = new ServerSocket(0);
} catch (IOException ex) {
return;
}
...
while (!Thread.currentThread().isInterrupted()) {
Socket clientSocket = null;
try {
clientSocket = server.accept();
} catch (IOException ex) {
break;
}
try {
ChatConnection chatConn = new ChatConnection(clientSocket);
synchronized (connections) {
connections.add(chatConn);
}
} catch (IOException ex) {
continue;
}
}
"server" is an instance variable of NsdProviderThread declared as ServerSocket.
It looks like you just need to use the correct port number on both ends.
You're using zero, which from the documentation means:
A port number of 0 means that the port number is automatically
allocated, typically from an ephemeral port range.
So, when you create your ServerSocket, make sure it is listening on the same port that the other device uses to initiate the connection:
private static final int port = 6770;
//.....
try {
server = new ServerSocket(port);
} catch (IOException ex) {
ex.printStackTrace();
}
again! I've finally managed to get my app working. Here's what I've done:
Hard-code the port number;
When you get the group owner address in the ConnectionInfoListener implementation, make sure if it is the IP address of the device in use. If it is not, connect a client socket to the group owner address; otherwise, make your app wait for an incoming connection;
Initialize the ServerSocket as soon as possible (for example, when the app starts up).
In order to get the device actual IP address after a Wi-Fi Direct connection has been established, I've used this function which I've found in this project (which is derived by the original Android WiFiDirectdemo) in the "Utils" class:
public static String getLocalIPAddress() {
/*
* modified from:
*
* http://thinkandroid.wordpress.com/2010/03/27/incorporating-socket-programming-into-your-applications/
*
* */
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
String iface = intf.getName();
if(iface.matches(".*" +p2pInt+ ".*")){
if (inetAddress instanceof Inet4Address) { // fix for Galaxy Nexus. IPv4 is easy to use :-)
return getDottedDecimalIP(inetAddress.getAddress());
}
}
}
}
} catch (SocketException ex) {
Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
} catch (NullPointerException ex) {
Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
}
return null;
}
"p2pInt" is a private static String costant declared in the Utils class as:
private final static String p2pInt = "p2p-p2p0"
However, in my app, I've changed the "p2p-p2p0" string in "p2p-wlan0" since it looks like the network interface of my device for Wi-Fi Direct has that (different) name.
I hope this can help any developer who's trying to create an app that uses Wi-Fi Direct connectivity.
I'm developing an Android application which connects to an OBD2 device by Wifi and app can read Speed, RPM, Engine coolant temperature details etc. So wifi is used only for connecting with the OBD2 device(it doesn't have facility to connect with internet, only for communication with local clients). I also need an internet connection for web services. But after connecting my wifi I am not able to connect internet via my mobile data network in android.
The similar application is also developed for iOS. In iOS, I can use device over Wifi (Static Wifi setting) and Internet connection from my cellular network. It means configure my wifi with some static ip I am able to use mobile data network for Internet connection in iOS.
But in Android, If I use static wifi and check for Internet connection, it is not available.
How can I use Wifi and Internet connection both run parallel or any other way by configuring wifi settings in android ?
Firstly, the problem we may face here is that because there is no internet connection on WiFi network, HTTP data will not go through that connection. See Send request over WiFi (without connection) even if Mobile data is ON (with connection) on Android M for solution
However, I have faced issue where sometimes no HTTP request is successful. To solve this problem, we can use ConnectivityManager.requestNetwork() and Network.openConnection() to achieve this.
Make sure that Mobile data and WiFi network is enabled and Android Manifest has proper connections:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
Variables:
private ConnectivityManager.NetworkCallback mWifiNetworkCallback, mMobileNetworkCallback;
private Network mWifiNetwork, mMobileNetwork;
Get the connectivity manager:
final ConnectivityManager manager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
Build network callbacks:
if(mWifiNetworkCallback == null){
//Init only once
mWifiNetworkCallback = new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(final Network network) {
try {
//Save this network for later use
mWifiNetwork = network;
} catch (NullPointerException npe) {
npe.printStackTrace();
}
}
};
}
if(mMobileNetworkCallback == null){
//Init only once
mMobileNetworkCallback = new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(final Network network) {
try {
//Save this network for later use
mMobileNetwork = network;
} catch (NullPointerException npe) {
npe.printStackTrace();
}
}
};
}
Request networks:
NetworkRequest.Builder wifiBuilder;
wifiBuilder = new NetworkRequest.Builder();
//set the transport type do WIFI
wifiBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
manager.requestNetwork(wifiBuilder.build(), mWifiNetworkCallback);
NetworkRequest.Builder mobileNwBuilder;
mobileNwBuilder = new NetworkRequest.Builder();
//set the transport type do Cellular
mobileNwBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
manager.requestNetwork(mobileNwBuilder.build(), mMobileNetworkCallback);
Make the appropriate request like this:
public void makeHTTPRequest(final String httpUrl, final String payloadJson, final int timeout,
final boolean hasHeaders, final String header1, final String header2) {
try {
URL url = new URL(httpUrl);
HttpURLConnection conn = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
conn = (HttpURLConnection) mWifiNetwork.openConnection(url);
//Or use mMobileNetwork, if and when required
//conn = (HttpURLConnection) mMobileNetwork.openConnection(url);
} else {
conn = (HttpURLConnection) url.openConnection();
}
conn.setRequestProperty("Content-Type", "application/json");
conn.setReadTimeout(timeout * 1000);
conn.setConnectTimeout(timeout * 1000);
conn.setDoInput(true);
conn.setDoOutput(true);
if(hasHeaders){
conn.setRequestProperty("header1", header1);
conn.setRequestProperty("header2", header2);
}
conn.setRequestMethod("PUT");
OutputStream os = conn.getOutputStream();
os.write(payloadJson.getBytes());
os.close();
final int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
final String statusMessage = conn.getResponseMessage();
//Log this
}
} catch (SocketException se){
se.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
}
Note:
These functions are avaialble from Android Lollipop and above. So, it is necessary to use Build.Version.SDK_INT at appropriate place, like this:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
connectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
You can request for a certain hostAddress that it must use that type of connectivity.
IF you use Hipri then it will take the mobile network.
But this can fail ! If it works, then ALL connections to that address will go over that type of connectivity.
You might have to activate it first.
int resultInt = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
This can take a couple of seconds, since the hardware modules has to start up.
I've use this on several projects and works great.
On old device such as 2.2 it will react really unstable !
But I haven't found any problems on 4.0+
So, my question is how to check if I am receiving any data or not. The scenario is that I am connected to a wifi network such as Starbucks wifi (which user should first connect to the network and then accept the agreement before receiving any data.)
This code is not serving my purpose.
ConnectivityManager cm =
(ConnectivityManager) _mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnected()) {
return true;
}
return false;
It returns true when I am connected to the network but I have not accepted the agreement yet. Thanks,
There's no way to know that, because that isn't something handled at an OS level- its handled at the router. THe OS only knows whether it has completed the handshake with the router and received an IP. Whether the router is going to throw him to a login screen is beyond the scope of the OS.
My app connects to a bunch of web services running on a web server on the cloud. So I need to make sure this connection is established before my app tries to do stuff. Instead of trying to check if internet is available, all I do is call one of my services. I have a simple one called TestService that just returns the word "success". So my app calls the service, receives the string "success" and knows that it is connected to the internet and successfully reaching my services. I don't have to worry about ConnectivityManager and NetworkInfo because this solution works well.
Of course, if my services themselves were to go down, then I would not know for sure if the device is connecting to the internet, I would only know for sure it is not reaching my service. However a) I make sure my services are redundant and have 99.9% uptime, and b) my app wouldn't be much use if not connecting to my services anyway. An alternative to just check for internet might be to try to connect to a public service from a reputable company that you know will alyways be online.
YOu can check this by trying to ping a website. For example
try {
Socket s = new Socket("www.google.com", 80);
return s.getLocalAddress().getHostAddress();
// network connection available
} catch (Exception e) {
// no network connection
}
I haven't tried it thogh.. But I think it should work.
If it doesn't work then you can try another thing. Send a httprequest to google and wait for response.
Heres what I have run in an asynctask:
public Boolean testServer(){
//stackoverflow.com/questions/9552743/proper-way-to-test-if-server-is-up-in-java
//stackoverflow.com/questions/9180072/android-check-connection-to-server
Boolean boolconnect = false;
try {
myUrl = new URL(UPLOAD_URL);
connection = myUrl.openConnection();
connection.setConnectTimeout(30*1000);
connection.connect();
Log.i(TAG, "FROM SERVER Connection made to: " + UPLOAD_URL);
boolconnect = true;
} catch (SocketException e) {
// TODO Auto-generated catch block
Log.i(TAG, "FROM SERVER socket exception:" + e);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
Log.i(TAG, "FROM SERVER Unknown host:" + e);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.i(TAG, "FROM SERVER IOexception:" + e);
}
return boolconnect;
}
I have a code to determine if there is a network connection or not :
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnected())
{
// There is an internet connection
}
But if there is a network connection and no internet this is useless. I have to ping a website and wait for a response or timeout to determine the internet connection:
URL sourceUrl;
try {
sourceUrl = new URL("http://www.google.com");
URLConnection Connection = sourceUrl.openConnection();
Connection.setConnectTimeout(500);
Connection.connect();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// no Internet
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// no Internet
}
But it is a slow detection. I should learn a good and fast way to detect it.
Thanks in advance.
Try following method to detect different type of connection:
private boolean haveNetworkConnection(Context context)
{
boolean haveConnectedWifi = false;
boolean haveConnectedMobile = false;
ConnectivityManager cm = (ConnectivityManager) Your_Activity_Name.this.getSystemService(Context.CONNECTIVITY_SERVICE);
// or if function is out side of your Activity then you need context of your Activity
// and code will be as following
// (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo[] netInfo = cm.getAllNetworkInfo();
for (NetworkInfo ni : netInfo)
{
if (ni.getTypeName().equalsIgnoreCase("WIFI"))
{
if (ni.isConnected())
{
haveConnectedWifi = true;
System.out.println("WIFI CONNECTION AVAILABLE");
} else
{
System.out.println("WIFI CONNECTION NOT AVAILABLE");
}
}
if (ni.getTypeName().equalsIgnoreCase("MOBILE"))
{
if (ni.isConnected())
{
haveConnectedMobile = true;
System.out.println("MOBILE INTERNET CONNECTION AVAILABLE");
} else
{
System.out.println("MOBILE INTERNET CONNECTION NOT AVAILABLE");
}
}
}
return haveConnectedWifi || haveConnectedMobile;
}
The problem with all such schemes is that 'the internet' does not exist as an entity. There is a reason why failed connection attempts are reported as 'unreachable' or 'cannot connect to server at blahblah'. Examples:
1) You have no signal. Are you connected to the internet? Will PING succeed? Can you connect to your target server?
2) You have a signal, but your provider data allowance has been exceeded. Are you connected to the internet? Will PING succeed? Can you connect to your target server?
3) Your provider connection is fine, but their backbone router is down. Are you connected to the internet? Will PING succeed? Can you connect to your target server?
4) Your provider connection is fine, their backbone router is up but the fibre connection to country X where the server is has been interrupted by some drunken Captain and his ship's anchor. Are you connected to the internet? Will PING succeed? Can you connect to your target server?
5) All the links to the target country are up but Fred, with his ditch-digging JCB, has cut the power cable to the server farm. One of Fred's other jobs is to service the backup generator:( Are you connected to the internet? Will PING succeed? Can you connect to your target server?
6) All the hardware is up, but the server code was written by Fred before he was demoted to ditch-digger for incompetence and has now crashed, again. Are you connected to the internet? Will PING succeed? Can you connect to your target server?
7) Fred has had a day off, but his replacement, competent server admin has blocked off ICMP ping in the routers to prevent ping DOS attacks. Are you connected to the internet? Will PING succeed? Can you connect to your target server?
So, the ony way to be sure is to attempt to connect to the target server and see what happens.
You can surely detect some negative cases more quickly - surely if there is no signal, you cannot get a connection:) Past that, you should just try to connect. Tell the user what is going on, use a timeout and supply the user with a 'Cancel' button. That's about the best you can do.
How about this?
Make sure you have an active WiFi connection, now Use WifiManager.getConnectionInfo() which returns dynamic information about the current Wi-Fi connection, WifiInfo , you can get WifiInfo.getLinkSpeed(), which gives you the current link speed and check that against some minimum value.