I am currently trying to send some data between two android devices using Bluetooth. I've read plenty of questions regarding bluetooth transfer, sockets, and streams. So far without any luck.
The connection part is working. I get the device address then open a connection using the following :
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(myOtherDeviceAdress);
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(myUUID));
socket.connect();
And then try to send some data using the OutputStream
OutputStream mmout=tmp.getOutputStream();
byte[] toSend="Hello World!".getBytes();
mmout.write(toSend);
mmout.flush();
On the receiving end:
mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("ccv_prototype", UUID.fromString(myUUID));
mBluetoothSocket = mBluetoothServerSocket.accept(3 * 1000);
InputStream is = mBluetoothSocket.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(is));
And then, different version trying to read the buffer, currently:
int c;
StringBuilder response = new StringBuilder();
try {
while ((c = r.read()) != -1) {
//Since c is an integer, cast it to a char. If it isn't -1, it will be in the correct range of char.
response.append((char) c);
}
} catch (IOException e) {
e.printStackTrace();
}
String result = response.toString();
Log.d("MyTag", "Received String: " + result);
My issue here is that if I don't close the OutputStream, the receiving end never receives the EOF, but if I add mmout.close();, it closes before it even had time to read the message I wanted to send. So far, my only idea is to send a specific token as an EOF but this doesn't sound right.
What did I miss ?
Any help appreciated.
The simple answer is yes. You should send a specific token to represent EOF. When you do a read() operation on a Bluetooth socket, it will either return immediately with some data if there's data ready to be read, or otherwise the read() call will block until there is some data, or some IO exception happens (e.g. the connection drops). This is why you must make use of Threads, particularly for Bluetooth socket read and write operations. What you're attempting to do is rely on the BufferedReader returning -1 to indicate "no more data". Sadly, this isn't how it works. The -1 will only happen in the event of some IO exception or the connection closing.
Detection of where your piece of information (i.e. your packet of data) starts and finishes, or indeed determining when an overall communication session is ended, is something that you handle yourself in your own application protocol (or of course an existing protocol) that works over the sockets. This is an important concept with any protocol that works through streaming sockets. A good example to look at is HTTP, which as you know is conventionally used over TCP. Taking a quick look at HTTP will show you (a) how the HTTP protocol uses headers to tell the recipient how many more bytes to expect for the overall HTTP "message", and (b) how HTTP headers are also used to negotiate when the connection should close. What you cannot do is attempt to use methods on the sockets themselves to determine when the sender has finished writing a message. Similarly if one end is to be aware that the other end wants to close the connection, that should be negotiated over the application protocol.
Related
In my app I have a Socket like this:
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), 5000);
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
I write to this socket like this:
outputStream.write((message).getBytes());
And read from it like this:
while (true) {
try {
String message = inputStream.readLine();
if (message != null) {
//do sth here
}
} catch (IOException e) {
e.printStackTrace();
}
}
Now the problem is sometimes Internet connection is so slow, in a way that connection stays alive (socket stays connected) but cannot send any messages. In such conditions it seems like the OutputStream holds the messages until it is able to deliver them to server. All I want is know when the socket is in this state?
I can ask the above question in this way too: How can I know if the written message is delivered to server?
Note that checking whether device is connected to Internet or not doesn't help because the device is really connected but probably experiencing a poor connection.
All I want is know when the socket is in this state?
It gets in that state when TCP acks from the server are not received by the client so those packets can be removed from the client's outgoing buffer. As long as there is room in the buffer, a send will not block the caller. But once the buffer fills up, subsequent sends block the caller until the buffer clears up.
The only way I know to detect this condition is to use the socket's getChannel() method to access its underlying SocketChannel, and then register() that with a Selector that you can then monitor/query using its select() method to know if the socket is writable (has room in its outgoing buffer).
But, that will only be able to tell you if the socket can accept at least 1 byte for sending without blocking the caller. It will NOT tell you if a given message can be accepted completely for sending, as the buffer could fill up before the end of the message is reached, in which case the send will block until the buffer frees up room for the whole message.
But, it would let you monitor the health of the socket connection, and if you find that sends are getting blocked too much due to slow/missing acks, you might have to just close the socket and reconnect.
How can I know if the written message is delivered to server?
The only way is to have the server send back its own message after receiving the client's message.
I am running into some issues with the Java socket API. I am trying to display the number of players currently connected to my game. It is easy to determine when a player has connected. However, it seems unnecessarily difficult to determine when a player has disconnected using the socket API.
Calling isConnected() on a socket that has been disconnected remotely always seems to return true. Similarly, calling isClosed() on a socket that has been closed remotely always seems to return false. I have read that to actually determine whether or not a socket has been closed, data must be written to the output stream and an exception must be caught. This seems like a really unclean way to handle this situation. We would just constantly have to spam a garbage message over the network to ever know when a socket had closed.
Is there any other solution?
There is no TCP API that will tell you the current state of the connection. isConnected() and isClosed() tell you the current state of your socket. Not the same thing.
isConnected() tells you whether you have connected this socket. You have, so it returns true.
isClosed() tells you whether you have closed this socket. Until you have, it returns false.
If the peer has closed the connection in an orderly way
read() returns -1
readLine() returns null
readXXX() throws EOFException for any other XXX.
A write will throw an IOException: 'connection reset by peer', eventually, subject to buffering delays.
If the connection has dropped for any other reason, a write will throw an IOException, eventually, as above, and a read may do the same thing.
If the peer is still connected but not using the connection, a read timeout can be used.
Contrary to what you may read elsewhere, ClosedChannelException doesn't tell you this. [Neither does SocketException: socket closed.] It only tells you that you closed the channel, and then continued to use it. In other words, a programming error on your part. It does not indicate a closed connection.
As a result of some experiments with Java 7 on Windows XP it also appears that if:
you're selecting on OP_READ
select() returns a value of greater than zero
the associated SelectionKey is already invalid (key.isValid() == false)
it means the peer has reset the connection. However this may be peculiar to either the JRE version or platform.
It is general practice in various messaging protocols to keep heartbeating each other (keep sending ping packets) the packet does not need to be very large. The probing mechanism will allow you to detect the disconnected client even before TCP figures it out in general (TCP timeout is far higher) Send a probe and wait for say 5 seconds for a reply, if you do not see reply for say 2-3 subsequent probes, your player is disconnected.
Also, related question
I see the other answer just posted, but I think you are interactive with clients playing your game, so I may pose another approach (while BufferedReader is definitely valid in some cases).
If you wanted to... you could delegate the "registration" responsibility to the client. I.e. you would have a collection of connected users with a timestamp on the last message received from each... if a client times out, you would force a re-registration of the client, but that leads to the quote and idea below.
I have read that to actually determine whether or not a socket has
been closed data must be written to the output stream and an exception
must be caught. This seems like a really unclean way to handle this
situation.
If your Java code did not close/disconnect the Socket, then how else would you be notified that the remote host closed your connection? Ultimately, your try/catch is doing roughly the same thing that a poller listening for events on the ACTUAL socket would be doing. Consider the following:
your local system could close your socket without notifying you... that is just the implementation of Socket (i.e. it doesn't poll the hardware/driver/firmware/whatever for state change).
new Socket(Proxy p)... there are multiple parties (6 endpoints really) that could be closing the connection on you...
I think one of the features of the abstracted languages is that you are abstracted from the minutia. Think of the using keyword in C# (try/finally) for SqlConnection s or whatever... it's just the cost of doing business... I think that try/catch/finally is the accepted and necesary pattern for Socket use.
I faced similar problem. In my case client must send data periodically. I hope you have same requirement. Then I set SO_TIMEOUT socket.setSoTimeout(1000 * 60 * 5); which is throw java.net.SocketTimeoutException when specified time is expired. Then I can detect dead client easily.
I think this is nature of tcp connections, in that standards it takes about 6 minutes of silence in transmission before we conclude that out connection is gone!
So I don`t think you can find an exact solution for this problem. Maybe the better way is to write some handy code to guess when server should suppose a user connection is closed.
As #user207421 say there is no way to know the current state of the connection because of the TCP/IP Protocol Architecture Model. So the server has to notice you before closing the connection or you check it by yourself.
This is a simple example that shows how to know the socket is closed by the server:
sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT);
socket = new Socket();
timeout = 5000;
socket.connect(sockAdr, timeout);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream());
while ((data = reader.readLine())!=null)
log.e(TAG, "received -> " + data);
log.e(TAG, "Socket closed !");
Here you are another general solution for any data type.
int offset = 0;
byte[] buffer = new byte[8192];
try {
do {
int b = inputStream.read();
if (b == -1)
break;
buffer[offset++] = (byte) b;
//check offset with buffer length and reallocate array if needed
} while (inputStream.available() > 0);
} catch (SocketException e) {
//connection was lost
}
//process buffer
Thats how I handle it
while(true) {
if((receiveMessage = receiveRead.readLine()) != null ) {
System.out.println("first message same :"+receiveMessage);
System.out.println(receiveMessage);
}
else if(receiveRead.readLine()==null)
{
System.out.println("Client has disconected: "+sock.isClosed());
System.exit(1);
} }
if the result.code == null
On Linux when write()ing into a socket which the other side, unknown to you, closed will provoke a SIGPIPE signal/exception however you want to call it. However if you don't want to be caught out by the SIGPIPE you can use send() with the flag MSG_NOSIGNAL. The send() call will return with -1 and in this case you can check errno which will tell you that you tried to write a broken pipe (in this case a socket) with the value EPIPE which according to errno.h is equivalent to 32. As a reaction to the EPIPE you could double back and try to reopen the socket and try to send your information again.
I want to know when TCP connection is lost(not closed), for example when server accidentally shuts down. So I send data with outputStream.write(s); When connection is lost and Im trying send data it should throwsSocketException`. But it throws only after second sending data. Why is this happening.
void send_socket(){
try{
outputStream = socket.getOutputStream();
outputStream.write(MSG.getBytes());
} catch (SocketException e){
Log.e(TAG, "connection closed");
}
catch (IOException e){
Log.e(TAG, "error 3 - ", e);
}
}
The best form of detecting a disconnect or pull in TCP is to wait for the IoException to occur and take the appropriate action. the only thing TCP can facilitate for you is when the other sends the FIN which ultimately returns a -1 and tells the user that the connection has been closed with an EOFException, which i'm sure you've found out by now.
Edit: thanks for the correction. I forgot to be a little more clear.
client server
====== ======
<-------FIN------ exit()/kill
-------ACK------>
write() -------PSH------>
<-------RST------
//RST is sent because FIN is already delivered
write()
//raise an error immediately due to RST
It is right to write a socket which has already got a FIN, however, it would make the peer that did a full close (which is the case of the question) and just sent the FIN now sends a RST. Then the 2nd write would raise an error immediately because it is wrong to write a socket that has got a RST. This is the TCP nature. This is why you have to send twice before knowing the server is closed.
client server
====== ======
<-------FIN------ exit()/kill
-------ACK------>
write() -------PSH------>
<-------RST------
//RST is sent because FIN is already delivered
read()
//return 0 immediately due to FIN
//or return error ECONNRESET due to RST
client server
====== ======
write() -------PSH------>
<-------ACK------
read()
//waits for response
<-------FIN------ exit()/kill
-------ACK------>
//return 0 immediately due to FIN
However, the program is usually designed like this (see the two illustrations above), a write is followed by a read. The read would return 0 or an error to indicate the server is closed immediately after the server is closed. No need to write twice.
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.
I am currently working on an Android app to be linked into an existing product for my employer. The system is a device running firmware that is controllable via serial, ethernet, or wi-fi using a .NET Windows program, direct serial communication, or a control webpage. The Android app is meant to auto-connect control webpage so that the device is controllable from a phone without anyone having to manually find IP addresses. In order to do that, I need to be able to find IP addresses and determine whether or not the address corresponds to one of these firmware devices.
Essentially what I want to do is run through all IP addresses that the Android sees and send each one to the method above. This thing should ping the address with a firmware command and see what is sent back to determine whether the address corresponds to a firmware device. I’ve been doing that at the moment by sending a command like "HOME" or "GETINFO" (correctly formatted for the firmware, of course) and comparing what, if anything is sent back with the expected response. Commands like home also have the benefit of causing a physical response, so we know without a microchip debugger if the command has at least been received.
As it stands I have the correct address hardcoded and sent in to this method. I know that the app can load the webpage controls successfully using the same address as a URL, so it must be correct and the connection must be good, but direct communication to the device is not working. No physical response is observed and no information is sent back—the Input stream just times out and returns -1. What’s stumping me so badly about this is that as far as I can tell, I’m sending information exactly the same way as the .NET Windows controls, and yet it isn’t working.
One further note: I’m aware that sending the IP Address string to the socket constructor as a hostname probably should not work, but since no UnknownHostException is thrown, I know that the socket can resolve it to an IP Address. Correct?
My code is as follows:
private class NetworkTask extends AsyncTask<String, Boolean, Boolean> {
protected Boolean doInBackground(String... addr){
try {
String message = "<FHGETHUBINFO>";
byte[] input = new byte[8];
//addr is an array of string parameters containing a single IP address string. E.g. addr[0]=”192.168.199.108”
Socket s = new Socket(addr[0],80);
//outgoing stream redirect to socket
OutputStream out = s.getOutputStream();
out.write(message.getBytes());
Log.v(TAG, "output sent. Waiting for input.");
InputStream in = s.getInputStream();
//Skip the exclamation mark and newline. Verified that nothing is received even without a skip.
in.skip(2);
int numBytes = in.read(input,0,8);
Log.v(TAG, "Input received: "+numBytes);
String st = input.toString();
//Close connection
s.close();
if(st != "HUB INFO"){
return true;
}
else{
return false;
}
}
catch (UnknownHostException e) {
Log.v(TAG,"UnknownHostException: "+e.getMessage());
e.printStackTrace();
}
catch (IOException e) {
Log.v(TAG,"IOException: "+e.getMessage());
e.printStackTrace();
}
return false;
Thanks for any help you can give, I really appreciate it!
Agreed that I should be calling isReachable on the socket just for verification purposes, so thanks for the tip! However, it turned out the problem was that the device is not communicating on port 80, so the fact that I have the wrong port is definitely the source of the problem. Thank you for the advice, regardless.