I already got displayed data received from microprocesor via bluetooth. It sends me an 8-bit frame with actual Voltage and Temperature state every second.
The issue is the TextView doesn't displaying actual data. When app loads, the data is displayed and stays like that. One method to refresh data is to load app again.
I got handler and ConnectedThread which code I attach.
All the best from Poland.
bluetoothIn = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) {
String readMessage = (String) msg.obj;
sb.append(readMessage);
int endOfLineIndex = sb.indexOf(";\r\n");
if (endOfLineIndex > 0) {
String dataInPrint = sb.substring(0, endOfLineIndex);
strDlugosc.setText(dataInPrint);
int dataLenght = dataInPrint.length();
strLenght.setText("ilość otrzymanych znakow =" + String.valueOf(dataLenght));
if (sb.charAt(0) == '9') {
String statusb = sb.substring(8,9);
String temperatura = sb.substring(21, 27);
String napiecie = sb.substring(12, 18);
status.setText("Status :" + statusb);
temp_1.setText("TEMPERATURA = " + temperatura + " *C");
nap_1.setText("NAPIECIE = " + napiecie + " V");
}
}
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
//creation of the connect thread
public ConnectedThread(BluetoothSocket socket) {
InputStream tmpIn = null;
try {
//Create I/O streams for connection
tmpIn = socket.getInputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
// Keep looping to listen for received messages
while (!ConnectedThread.interrupted()) {
try {
bytes = mmInStream.read(buffer); //read bytes from input buffer
String readMessage = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity via handler
bluetoothIn.obtainMessage(handlerState, bytes, -1, readMessage).sendToTarget();
} catch (IOException e) {
break;
}
}
}
I did it before. I used a timer to get data from BlueTooth socket every 1 second
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
// get data from socket and set to text view here
}
}, 0, 10000 /** milliseconds **/);
Related
I'm working on a project which receives information from an arduino module and shows it as a chart.
The problem is I have 5 elements ( temperature,humidity, etc... ) and the code I have can only receive one number at a time ( for example : 2838752458 ), as you see in the example the number has 10 digits which comes from Arduino and I want to separate them two by two so each two of them goes for one element.
you might ask why I don't set a handler so I can receive each two number at a separated time but I've tried that and it gives me closed application error because the code I have only can receive one number at a time.
public class Analysies extends AppCompatActivity {
Handler h;
String tekrar = "";
String dama = "";
String qaza = "";
String faaliat = "";
String rotobat = "";
private OutputStream outStream = null;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static String address = "00:21:13:00:02:5B";
final int RECIEVE_MESSAGE = 1; // Status for Handler
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private StringBuilder sb = new StringBuilder();
private Analysies.ConnectedThread mConnectedThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_analysies);
gifImageView = (GifImageView) findViewById(R.id.gifBro);
txt1 = (GifImageView) findViewById(R.id.afterAutotxt);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
txt1.animate().alpha(1f).setDuration(2000);
}
}, 1000);
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case RECIEVE_MESSAGE:
// if receive massage
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new
String(readBuf, 0, msg.arg1);
// create string from bytes array
sb.append(strIncom);
// append string
int endOfLineIndex = sb.indexOf("\r\n");
// determine the end-of-line
if (endOfLineIndex > 0) {
// if end-of-line,
String sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length());
sbprint.getBytes().toString();
////////////////////////////// HERE IS WHERE I CAN RECEIVE INFORMATION FROM ARDUINO ///////////////////////////////
}
//Log.d(TAG, "...String:"+ sb.toString() + "Byte:" + msg.arg1 + "...");
// Toast.makeText(CommunicationAuto.this, "String:" + sb.toString() , Toast.LENGTH_LONG).show();
break;
}
}
;
};
btAdapter = BluetoothAdapter.getDefaultAdapter(); // get Bluetooth adapter
checkBTState();
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, MY_UUID);
} catch (Exception e) {
// Toast.makeText(getApplicationContext(), "Could not Insecure", Toast.LENGTH_SHORT).show();
}
}
return device.createRfcommSocketToServiceRecord(MY_UUID);
}
#Override
public void onResume() {
super.onResume();
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = btAdapter.getRemoteDevice(address);
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
// Toast.makeText(getApplicationContext(), "Socket failed", Toast.LENGTH_SHORT).show();
}
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
// Toast.makeText(getApplicationContext(), "Connecting", Toast.LENGTH_SHORT).show();
try {
btSocket.connect();
// Toast.makeText(getApplicationContext(), "Connecting ok!", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
// errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
mConnectedThread = new Analysies.ConnectedThread(btSocket);
mConnectedThread.start();
}
#Override
public void onPause() {
super.onPause();
try {
btSocket.close();
} catch (IOException e2) {
// errorExit("Fatal Error", "In onPause() and failed to close socket." + e2.getMessage() + ".");
}
}
private void checkBTState() {
// Check for Bluetooth support and then check to make sure it is turned on
// Emulator doesn't support Bluetooth and will return null
if(btAdapter==null) {
Toast.makeText(getApplicationContext(), " Bluetooth is not supported. ", Toast.LENGTH_SHORT).show();
} else {
if (btAdapter.isEnabled()) {
} else {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}
private void errorExit(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[256]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer); // Get number of bytes and message in "buffer"
h.obtainMessage(RECIEVE_MESSAGE, bytes, -1, buffer).sendToTarget(); // Send to message queue Handler
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(String message) {
byte[] msgBuffer = message.getBytes();
try {
mmOutStream.write(msgBuffer);
} catch (IOException e) {
}
}
}
}
The question : How can i separate the 10 digit number two by two and add them to separated integers so i can pass them to the chart activity? Please give me an example for "1234567890" this number.
here is the output of the chart I created so far.
This seems to do what you want:
import java.util.regex.*;
public class HelloWorld{
public static void main(String []args){
String s = "1234567890";
String pattern = "(\\d\\d)";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(s);
while (m.find()) {
System.out.println(m.group(0));
}
}
}
And output:
12
34
56
78
90
At first make sure the number of your digits (e.g: 1234567890) always divisible by 2. Well, then I have a little hack for you.
First, convert the digits to String and run a for-loop with step 2, then just append the characters at i-th and (i+1)th position into each string producing using the loop into an array. And finally, you can just read item from the array list and send value to your chart just parsing the String into int value to your chart. Here is a sample code I did with Kotlin (but I can convert it into Java code if you need). If you face any issues, feel free to comment.
fun main() {
val digits = 1234567890
val arr = arrayListOf<String>()
for (i in 0 until digits.toString().length step 2) {
val sub = "${digits.toString()[i]}${digits.toString()[i+1]}"
arr.add(sub)
}
println(arr)
}
I want to use data sent from my Arduino to my android. I'm able to connect both together and display the incoming data on my screen. However, when I want to use the incoming data to set comments this doesn't seem to be working. So how can I get the values out of the incoming data?
String address1 = ("98:D3:81:FD:4B:87");
String name1 = ("Sensor_Shoe");
#SuppressLint("HandlerLeak")
protected void bluetoothconnect() {
btEnablingIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
requestCodeForEnable=1;
if (myBluetoothAdapter==null){
Toast.makeText(getApplicationContext(),"Bluetooth not supported", Toast.LENGTH_LONG).show();
} else {
if (!myBluetoothAdapter.isEnabled()) {
startActivityForResult(btEnablingIntent, requestCodeForEnable);
}
if (myBluetoothAdapter.isEnabled()) {
myBluetoothAdapter.disable();
Toast.makeText(getApplicationContext(),"Bluetooth disabled",Toast.LENGTH_SHORT).show();
TextView input1 = findViewById(R.id.input1); input1.setVisibility(View.INVISIBLE);
ImageButton btn_bluetooth = findViewById(R.id.btn_bluetooth); btn_bluetooth.setBackgroundColor(getResources().getColor(R.color.transparent));
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
if(requestCode==requestCodeForEnable){
ImageButton btn_bluetooth = findViewById(R.id.btn_bluetooth);
if(resultCode==RESULT_OK){
Toast.makeText(getApplicationContext(),"Bluetooth enabled",Toast.LENGTH_SHORT).show();
TextView input1 = findViewById(R.id.input1); input1.setVisibility(View.VISIBLE);
btn_bluetooth.setBackgroundColor(getResources().getColor(R.color.colorAccent));
createsocket();
}
else if(resultCode==RESULT_CANCELED){
Toast.makeText(getApplicationContext(),"Bluetooth enabling cancelled",Toast.LENGTH_SHORT).show();
TextView input1 = findViewById(R.id.input1); input1.setVisibility(View.INVISIBLE);
btn_bluetooth.setBackgroundColor(getResources().getColor(R.color.transparent));
}
}
}
protected void createsocket() {
new Thread() {
public void run() {
boolean fail = false;
BluetoothDevice device = myBluetoothAdapter.getRemoteDevice(address1);
try {
BTSocket = createBluetoothSocket(device);
} catch (IOException e) {
fail = true;
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
// Establish the Bluetooth socket connection.
try {
BTSocket.connect();
} catch (IOException e) {
try {
fail = true;
BTSocket.close();
BTHandler.obtainMessage(CONNECTING_STATUS, -1, -1)
.sendToTarget();
} catch (IOException e2) {
//insert code to deal with this
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
}
if (!fail) {
mConnectedThread = new ConnectedThread(BTSocket);
mConnectedThread.start();
BTHandler.obtainMessage(CONNECTING_STATUS, 1, -1, name1)
.sendToTarget();
}
}
}.start();
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", UUID.class);
return (BluetoothSocket) m.invoke(device, PORT_UUID);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
return device.createRfcommSocketToServiceRecord(PORT_UUID);
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.available();
if(bytes != 0) {
buffer = new byte[1024];
SystemClock.sleep(100); //pause and wait for rest of data. Adjust this depending on your sending speed.
bytes = mmInStream.available(); // how many bytes are ready to be read?
bytes = mmInStream.read(buffer, 0, bytes); // record how many bytes we actually read
BTHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget(); // Send the obtained bytes to the UI activity
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
}
This is how I make the connection, this seems to work properly. Than I use the code underneith to read out the incomming data.
BTHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == MESSAGE_READ) {
String readMessage = " ";
try {
readMessage = new String((byte[]) msg.obj, "UTF-8");
inputdata1 = readMessage;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
TextView input1 = findViewById(R.id.input1);
input1.setText(readMessage);
if (readMessage.equals("X")){
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(100);
}
}
}
};
Showing the incomming data in the textview works. But it doesn't recognize the X in the incomming data. I can however see that this data is incomming in the textView and i do send this in de arduino code.
if (fsrReadingHeel >= (fsrReadingHeelOld + 800)){
Serial.println("X");
}
I do know the code is processed because when I say if (!(readMessage.equals("X"))){ than it does vibrate.
Arduinos Serial.print sends in ASCII. When you build a String, you can use ASCII like this: Charset.setName("ASCII") instead of just "UTF-8". This works fine for me (with a Arduino Uno and HC-06 Bluetooth module):
readMessage = new String((byte[]) msg.obj, Charset.setName("ASCII"));
The String you created from the byteBuffer should be limited to the size of the actual data - you could use substring(0, sizeOfData) for that.
When I made my App to connect with my Arduino Uno, I had the problem, that depending on the configuration it ignored the first sent Byte in Android, so try to send a longer string and see if you get something, and that you can actually read it correctly.
I am suggesting that you use Serial.print instead of println, because you don't need the line break in sending. It might also change the message you receive on Android.
if (fsrReadingHeel >= (fsrReadingHeelOld + 800)){
Serial.print("X");
}
You could also use Serial.write instead, but you don't need to - have a look at the differences here: https://arduino.stackexchange.com/questions/10088/what-is-the-difference-between-serial-write-and-serial-print-and-when-are-they
This question already has answers here:
What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?
(26 answers)
Closed 3 years ago.
I am want to separate the strings incoming from arduino and store it in variables for database purposes but when I try to divide that string using split() after a few moments the exception occurs
java.lang.ArrayIndexOutOfBoundsException: length=1; index=1 and the app keeps stopping
the error is at Line 203 separated[1]
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Android-Arduino";
Button btnOn, btnOff;
TextView temperatureText, heartbeatText;
Handler h;
final int RECIEVE_MESSAGE = 1;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private StringBuilder sb = new StringBuilder();
private ConnectedThread connectedThread;
// Common service that bluetooth device support
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// MAC address of the bluetooth module.
private static String address = "00:18:E4:35:75:A6";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
temperatureText = (TextView) findViewById(R.id.temp);
heartbeatText = (TextView)findViewById(R.id.heartbeat);
getDataArduino();
// Bluetooth adapter
btAdapter = BluetoothAdapter.getDefaultAdapter();
// Check whether the bluetooth is enabled / disabled
checkBTState();
}
// Creates the communication chanel
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().
getMethod("createInsecureRfcommSocketToServiceRecord", new Class[]{UUID.class });
return (BluetoothSocket) m.invoke(device, MY_UUID);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}
return device.createRfcommSocketToServiceRecord(MY_UUID);
}
#Override
public void onResume() {
super.onResume();
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = btAdapter.getRemoteDevice(address);
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: "
+ e.getMessage() + ".");
}
btAdapter.cancelDiscovery();
try {
btSocket.connect();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException ex) {
errorExit("Fatal Error", "In onResume() and unable to close socket during " +
"connection failure" + ex.getMessage() + ".");
}
}
// Creates the data stream with the server
connectedThread = new ConnectedThread(btSocket);
connectedThread.start();
}
#Override
public void onPause() {
super.onPause();
try {
btSocket.close();
} catch (IOException ex) {
errorExit("Fatal Error", "In onPause() and failed to close socket." +
ex.getMessage() + ".");
}
}
private void checkBTState() {
// Checks if the devices has bluetooth functionalities
if(btAdapter==null) {
errorExit("Fatal Error", "Bluetooth not support");
} else {
// Checks if the bluetooth is on
if (btAdapter.isEnabled()) {
} else {
// Prompt user to turn on Bluetooth
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}
private void errorExit(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[256]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
// Get number of bytes and message in "buffer"
bytes = mmInStream.read(buffer);
// Send to message queue Handler
h.obtainMessage(RECIEVE_MESSAGE, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
break;
}
}
}
//Call this from the main activity to send data to the remote device
public void write(String message) {
byte[] msgBuffer = message.getBytes();
try {
mmOutStream.write(msgBuffer);
} catch (IOException e) {
Log.d(TAG, "...Error data send: " + e.getMessage() + "...");
}
}
}
public void getDataArduino() {
temperatureText.clearComposingText();
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case RECIEVE_MESSAGE:
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1);
sb.append(strIncom);
int endOfLineIndex = sb.indexOf("\n");
if (endOfLineIndex > 0) {
String sbprint = sb.substring(0, endOfLineIndex);
sb.delete(0, sb.length());
String[] separated = sbprint.split(":");
heartbeatText.setText(separated[0]+ "");
temperatureText.setText(separated[1]+ "");
}
Log.d(TAG, "...String:"+ sb.toString() + "Byte:" + msg.arg1 + "...");
break;
}
}
};
}
}
I need the temperature to display differently and heartrate to be displayed individually.
You can first try to print the input data (StringBuffer sb) and split String array(String[] separated). You are getting error because the Strings have not split as your expected and only 1 element is present in the array sbprint, this is why when you try to access the second element you get ArrayIndexOutofBound index.
Once you print the Split String Array it would give you an idea where your data is wrong.
So I'm making an app that has to send an Arraylist<String> over a Bluetooth connection. The ArrayList<String> could be hundreds of lines long. I'm just testing it right now with several lines of data. When I transfer data to the other phone, it immediately prints it out to the logcat. It transfers a portion of the start of the line, but then sends a whole bunch of these question marks. When I play around with the byte[] buffer size, lowering it seems to get rid of a lot of question marks but the lines aren't preserved, raising it seems to send more question marks. Anyways - Take a look at the console output:
Console output
Sending code:
public void run() {
btAdapter.cancelDiscovery(); // discovery is heavy on the Bluetooth bandwith
try {
socket.connect();
// Decompile the enter file system of this event into one giant arraylist<string>
EventDecompiler decompiler = new EventDecompiler(getApplicationContext());
ArrayList<String> lines = decompiler.decompile(eventName, false);
ConnectedThread thread = new ConnectedThread(socket);
thread.start();
// First, let the other device know how much information we're sending
thread.write(String.valueOf(lines.size()).getBytes());
for(int i = 0; i < lines.size(); i++) {
thread.write(lines.get(i).getBytes());
}
} catch(IOException e) {
cancel();
return;
}
}
public void write(byte[] bytes) {
try {
outStream.write(bytes);
} catch(IOException e) {}
}
Here's the receiving code:
public void run() {
byte[] buffer; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
buffer = new byte[1024];
// Read from the InputStream
bytes = inStream.read(buffer);
status.post(new Runnable() {
public void run() {
status.setText("Receiving "+progress+ " / "+total);
}
});
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
private static Handler mHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case Constants.MESSAGE_READ:
byte[] readBuf = (byte[])msg.obj;
String string = new String(readBuf);
received.add(string);
if(progress < 1) {
total = 100;
}
progress++;
if(progress == 15) {
for(int i = 0; i < received.size(); i++) {
System.out.println(received.get(i));
}
}
break;
}
}
};
Any ideas of how to dynamically scale the buffer size or something else that's going wrong?
I am trying to implement a Reliable UDP protocol for a class assignment in Java. I have managed to add the acknowledgments to every datagram packet that is received, but I am having trouble implementing Sequence Numbers in the datagram packets that I am sending.
Can anyone suggest an easy method to implement this?
#EJP I have tried implementing what you just suggested. This is my code till now (its still very raw - i was using hit and try method to implement it)
Server side
public class TestServer extends Activity {
private DatagramSocket serverSocket;
Thread serverThread = null;
byte[] incomingData;
byte[] outgoingData;
//int numBytesRead = 0;
int ackSent = 0;
int numPackRecv = 0;
int BUF_SIZE = 1024;
String msg = "ACK";
BufferedInputStream data=null;
BufferedOutputStream out =null;
public static final int SERVERPORT = 6000;
String outputFile = "/sdcard/Movies/asddcopy.mp4";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_server);
this.serverThread = new Thread(new ServerThread());
this.serverThread.start();
}
#Override
protected void onStop() {
super.onStop();
try {
serverSocket.close();
} catch (Exception e) {
Log.d("SERVER", "Inside onStop()");
Log.d("SERVER", Log.getStackTraceString(e));
}
}
class ServerThread implements Runnable {
#SuppressLint("NewApi")
public void run() {
try {
serverSocket = new DatagramSocket(SERVERPORT);
incomingData = new byte[BUF_SIZE];
//outgoingData = new byte[512];
outgoingData = msg.getBytes();
long startRxPackets = TrafficStats.getUidRxPackets(Process.myUid());
long startTime = System.nanoTime();
out = new BufferedOutputStream(new FileOutputStream(outputFile, true));
while (!Thread.currentThread().isInterrupted()) {
//serverSocket.setSoTimeout(5000);
while (true) {
try{
//DatagramPacket incomingPacket = new DatagramPacket(incomingData, incomingData.length);
DatagramPacket incomingPacket = new DatagramPacket(incomingData, BUF_SIZE);
serverSocket.receive(incomingPacket);
byte[] data = incomingPacket.getData();
//out.write(data,0,incomingPacket.getLength());
//String msg = new String(incomingPacket.getData());
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is = new ObjectInputStream(in);
if (is == null) {
is = new ObjectInputStream(in);
}
Message msg = (Message) is.readObject();
System.out.println(msg.getSeqNo());
/*if ("END".equals(msg.substring(0, 3).trim())) {
Log.d("SERVER", "Inside END condition");
break;
}*/
out.write(msg.getData(),0,msg.getData().length);
numPackRecv += 1;
Log.d("SERVER", "Packet Received: " + numPackRecv);
InetAddress client = incomingPacket.getAddress();
int client_port = incomingPacket.getPort();
DatagramPacket outgoingPacket = new DatagramPacket(outgoingData, outgoingData.length, client, client_port);
serverSocket.send(outgoingPacket);
ackSent += 1;
//Log.d("SERVER","Packet Received: " + numPackRecv + " :: " + "Ack Sent: " + ackSent);
}catch(Exception e) {
Log.d("SERVER", "Inside run() ex1");
Log.d("SERVER", Log.getStackTraceString(e));
break;
}
}
out.close();
serverSocket.disconnect();
serverSocket.close();
Log.d("SERVER", "Transfer Complete");
Log.d("SERVER", "Actual Time elapsed = " + (System.nanoTime() - startTime)/Math.pow(10, 9) + " s");
Log.d("SERVER", "Total Packets Received = " + Long.toString(TrafficStats.getUidRxPackets(Process.myUid()) - startRxPackets));
Log.d("SERVER", "Packets Received from Socket = " + numPackRecv);
break;
}
out.close();
serverSocket.disconnect();
serverSocket.close();
/* Log.d("SERVER", "Transfer Complete");
Log.d("SERVER", "Actual Time elapsed = " + (System.nanoTime() - startTime)/Math.pow(10, 9) + " s");
Log.d("SERVER", "Total Packets Received = " + Long.toString(TrafficStats.getUidRxPackets(Process.myUid()) - startRxPackets));
Log.d("SERVER", "Packets Received from Socket = " + numPackRecv);*/
}catch (Exception e) {
Log.d("SERVER", "Inside run() ex2");
Log.d("SERVER", Log.getStackTraceString(e));
serverSocket.disconnect();
serverSocket.close();
}
}
}
This is the Client side
public class TestClient extends Activity { private DatagramSocket clientSocket;
byte[] incomingData;
int BUF_SIZE = 500;
int numBytesRead = 0;
int numPackSent = 0;
private static final int SERVERPORT = 6000;
private static final String SERVER_IP = "10.0.0.22";
String inFile = "/sdcard/Movies/asdd.mp4";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_client);
new Thread(new ClientThread()).start();
}
public void onClick(View view) {
new workInProgress().execute("");
}
private class workInProgress extends AsyncTask<Object, Object, Object> {
#SuppressLint("NewApi")
#Override
protected Object doInBackground(Object... params) {
try {
Log.d("CLIENT", "Sending a file to the server...");
BufferedInputStream inputBuf = new BufferedInputStream(new FileInputStream(inFile));
//byte[] fileBytes = new byte[(int) inFile.length()];
byte[] fileBytes = new byte[BUF_SIZE];
incomingData = new byte[BUF_SIZE];
double numPktToSend = Math.ceil(inFile.length()*1.0/BUF_SIZE);
//Log.d("CLIENT", "Total packets to be sent = " + numPktToSend);
int sleepCycle = 1;
long sysPackSent = 0;
//long startTxPackets = TrafficStats.getTotalTxPackets();
long startTxPackets = TrafficStats.getUidTxPackets(Process.myUid());
Log.d("CLIENT", "startTxPacks: " + startTxPackets);
long packDrops = 0;
long startTime = System.nanoTime();
long count=0;
long ackRec=0;
int seqNo = 0;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(outStream);
while((numBytesRead = inputBuf.read(fileBytes)) != -1) {
//DatagramPacket packet = new DatagramPacket(fileBytes, fileBytes.length);
if (os == null) {
os = new ObjectOutputStream(outStream);
}
Message msg = new Message(++seqNo, fileBytes, false);
os.writeObject(msg);
os.flush();
os.reset();
byte[] data = outStream.toByteArray();
DatagramPacket packet = new DatagramPacket(data, data.length);
clientSocket.send(packet);
numPackSent += 1;
//Log.d("CLIENT", "No of packets sent = " + numPackSent);
sysPackSent = TrafficStats.getUidTxPackets(Process.myUid()) - startTxPackets;
try{
clientSocket.setSoTimeout(5000);
packet = new DatagramPacket(incomingData, incomingData.length);
clientSocket.receive(packet);
String recAck = new String(packet.getData());
ackRec++;
}
catch(Exception e) {
//Log.d("CLIENT", Log.getStackTraceString(e));
}
packDrops = numPackSent - ackRec;
if (packDrops > count) {
sleepCycle = Math.min(16, sleepCycle * 2);
count = packDrops;
Log.d("CLIENT",String.valueOf(sleepCycle) + " :: " + numPackSent);
} else {
sleepCycle = Math.max(sleepCycle - 1, 1);
}
Thread.sleep(sleepCycle);
}
if (numBytesRead == -1) {
fileBytes = "END".getBytes();
Log.d("CLIENT", "Sending END Packet");
clientSocket.send(new DatagramPacket(fileBytes, fileBytes.length));
}
Log.d("CLIENT", "Actual Time elapsed = " + (System.nanoTime() - startTime)/Math.pow(10, 9) + " s");
Log.d("CLIENT", "Total Packets Transmitted = " + Long.toString(sysPackSent));
Log.d("CLIENT", "No of packets dropped = " + String.valueOf(packDrops));
Log.d("CLIENT", "Packets Pushed to Socket = " + numPackSent);
Log.d("CLIENT", "Number of Acknoledgments received " +ackRec);
inputBuf.close();
os.close();
outStream.close();
clientSocket.disconnect();
clientSocket.close();
Log.d("CLIENT", "Sending file.. Complete!!!");
} catch (Exception e) {
Log.d("CLIENT", Log.getStackTraceString(e));
clientSocket.disconnect();
clientSocket.close();
}
return null;
}
}
class ClientThread implements Runnable {
#Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
clientSocket = new DatagramSocket();
clientSocket.connect(serverAddr, SERVERPORT);
Log.d("CLIENT", "Connection Successful");
} catch (UnknownHostException e1) {
Log.d("CLIENT", "Inside run() UnknownHostEx");
Log.d("CLIENT", Log.getStackTraceString(e1));
} catch (IOException e1) {
Log.d("CLIENT", "Inside run() IOEx");
Log.d("CLIENT", Log.getStackTraceString(e1));
}
}
}
I am getting a few errors at the Server side:
I am receiving the same sequence number for each packet (i.e. 1)
I am not sure about the buffer size for the incoming packet, as I am using 500 bytes at Client side and 1024 at the Sever. And if I take 500 bytes in both the codes I get a End of File exception.
I would really appreciate if you could suggest better ways to implement the same thing!
Thanks :)
Thanks!
Create a ByteArrayOutputStream.
Wrap it in a DataOutputStream
Use DataOutputStream.writeInt() to write the sequence number.
Use write() to write the data.
Construct the DatagramPacket from the byte array returned by the ByteArrayOutputStream.
At the receiver, do exactly the reverse, using the complementary classes and methods in each case. What those are is left as an exercise for the reader.
The simplest method would probably be to a look at the TCP protocol, and stick all the TCP headers into the start of each of your UDP packets.