I am using a Teensy microcontroller (https://www.pjrc.com/teensy/teensy31.html) to connect to an Android application to get Bluetooth.
I have used this Android application with other embedded bluetooth devices successfully, however now I am running into the following error I see in LogCat:
04-02 20:06:29.713: E/BTLD(5499):
######################################################################
04-02 20:06:29.713: E/BTLD(5499): #
04-02 20:06:29.713: E/BTLD(5499): # WARNING : BTU HCI(id=0) command timeout. opcode=0x405
04-02 20:06:29.713: E/BTLD(5499): #
04-02 20:06:29.713: E/BTLD(5499): ######################################################################
I have zero idea where this error is coming from. I also see the following error occasionally:
04-02 20:25:19.242: E/bt-btif(914): DISCOVERY_COMP_EVT slot id:11, failed to find channle, status:1, scn:0
The worst part is, with my current hardware setup, half the time it will work, the other half it wont. And I have no clue why. For reference, I am using this bluetooth module: https://www.sparkfun.com/products/12577 And I have the pins connected:
Vcc <---> Vcc
GND <---> GND
TX (BT) <---> RX1 (Teensy)
RX (BT) <---> TX1 (Teensy)
The lack of consistency is killing me. Sometimes just pushing the reset button on the Teensy fixes it.
I am working off of this tutorial: http://stafava.blogspot.ca/2012/12/connect-teensy-to-bluetooth-module.html Here is the Ardunio Microcontroller code running on the Teensy:
HardwareSerial bt = HardwareSerial();
#define Seria1_PORT_SPEED 115200
void setup()
{
bt.begin(Seria1_PORT_SPEED);
bt.println();
Wire.begin();
}
void loop()
{
////////////////// Bluetooth stuff /////////////////////////
if (bt.available() >= 2) {
if (bt.read() == '#') {// Start of new control message
int command = bt.read(); // Commands
if (command == 'f') {// request one output _f_rame
//output_single_on = true; //unused?
} else if (command == 's') { // _s_ynch request
// Read ID
byte id[2];
id[0] = readChar();
id[1] = readChar();
// Reply with synch message
bt.print("#SYNCH");
bt.write(id, 2);
bt.println();
}
}
}
sendDataToAndroid();
}
void sendDataToAndroid() { //arrays defined as float array[3] = ...
bt.write((byte*) array1, 12);
bt.write((byte*) array2, 12);
bt.write((byte*) array3, 12);
}
char readChar()
{
while (bt.available() < 1) { } // Block
return bt.read();
}
And here is the relevant Android code taken from this tutorial: https://github.com/ptrbrtz/razor-9dof-ahrs/tree/master/Arduino/Razor_AHRS
package com.jest.razor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import org.apache.http.util.EncodingUtils;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
/**
* Class to easily interface the Razor AHRS via Bluetooth.
* <p>
* Bluetooth seems to be even more picky on Android than it is anyway. Be sure to have a look at the
* section about Android Bluetooth in the tutorial at
* <a href="https://github.com/ptrbrtz/razor-9dof-ahrs">
* https://github.com/ptrbrtz/razor-9dof-ahrs</a>!
* <p>
* The app using this class has to
* <ul>
* <li>target Android 2.0 (API Level 5) or later.
* <li>specify the uses-permissions <code>BLUETOOTH</code> and <code>BLUETOOTH_ADMIN</code> in it's
* AndroidManifest.xml.
* <li>add this Library Project as a referenced library (Project Properties -> Android -> Library)
* </ul>
* <p>
* TODOs:
* <ul>
* <li>Add support for USB OTG (Android device used as USB host), if using FTDI is possible.
* </ul>
*
* #author Peter Bartz
*/
public class RazorAHRS {
private static final String TAG = "RazorAHRS";
private static final boolean DEBUG = false;
private static final String SYNCH_TOKEN = "#SYNCH";
private static final String NEW_LINE = "\r\n";
// Timeout to init Razor AHRS after a Bluetooth connection has been established
public static final int INIT_TIMEOUT_MS = 10000;
// IDs passed to internal message handler
private static final int MSG_ID__YPR_DATA = 0;
private static final int MSG_ID__IO_EXCEPTION_AND_DISCONNECT = 1;
private static final int MSG_ID__CONNECT_OK = 2;
private static final int MSG_ID__CONNECT_FAIL = 3;
private static final int MSG_ID__CONNECT_ATTEMPT = 4;
private static final int MSG_ID__AMG_DATA = 5;
private static final UUID UUID_SPP = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
/**
* Razor output modes.
* Use <code>YAW_PITCH_ROLL_ANGLES</code> to receive yaw, pitch and roll in degrees. <br>
* Use <code>RAW_SENSOR_DATA</code> or <code>CALIBRATED_SENSOR_DATA</code> to read raw or
* calibrated xyz sensor data of the accelerometer, magnetometer and the gyroscope.
*/
public enum RazorOutputMode {
YAW_PITCH_ROLL_ANGLES,
RAW_SENSOR_DATA,
CALIBRATED_SENSOR_DATA
}
private RazorOutputMode razorOutputMode;
private enum ConnectionState {
DISCONNECTED,
CONNECTING,
CONNECTED,
USER_DISCONNECT_REQUEST
}
volatile private ConnectionState connectionState = ConnectionState.DISCONNECTED;
volatile private BluetoothSocket btSocket;
volatile private BluetoothDevice btDevice;
volatile private InputStream inStream;
volatile private OutputStream outStream;
private RazorListener razorListener;
private boolean callbacksEnabled = true;
BluetoothThread btThread;
private int numConnectAttempts;
// Object pools
private ObjectPool<float[]> float3Pool = new ObjectPool<float[]>(new ObjectPool.ObjectFactory<float[]>() {
#Override
public float[] newObject() {
return new float[3];
}
});
private ObjectPool<float[]> float9Pool = new ObjectPool<float[]>(new ObjectPool.ObjectFactory<float[]>() {
#Override
public float[] newObject() {
return new float[9];
}
});
/**
* Constructor.
* Must be called from the thread where you want receive the RazorListener callbacks! So if you
* want to manipulate Android UI from the callbacks you have to call this from your main/UI
* thread.
*
* #param btDevice {#link android.bluetooth.BluetoothDevice BluetoothDevice} holding the Razor
* AHRS to connect to.
* #param razorListener {#link RazorListener} that will be notified of Razor AHRS events.
* #throws RuntimeException thrown if one of the parameters is null.
*/
public RazorAHRS(BluetoothDevice btDevice, RazorListener razorListener)
throws RuntimeException {
this(btDevice, razorListener, RazorOutputMode.CALIBRATED_SENSOR_DATA);
}
/**
* Constructor.
* Must be called from the thread where you want receive the RazorListener callbacks! So if you
* want to manipulate Android UI from the callbacks you have to call this from your main/UI
* thread.
*
* #param btDevice {#link android.bluetooth.BluetoothDevice BluetoothDevice} holding the Razor
* AHRS to connect to.
* #param razorListener {#link RazorListener} that will be notified of Razor AHRS events.
* #param razorOutputMode {#link RazorOutputMode} that you desire.
* #throws RuntimeException thrown if one of the parameters is null.
*/
public RazorAHRS(BluetoothDevice btDevice, RazorListener razorListener, RazorOutputMode razorOutputMode)
throws RuntimeException {
if (btDevice == null)
throw new RuntimeException("BluetoothDevice can not be null.");
this.btDevice = btDevice;
if (razorListener == null)
throw new RuntimeException("RazorListener can not be null.");
this.razorListener = razorListener;
if (razorOutputMode == null)
throw new RuntimeException("RazorMode can not be null.");
this.razorOutputMode = razorOutputMode;
}
/**
* #return <code>true</code> if listener callbacks are currently enabled, <code>false</code> else.
*/
public boolean getCallbacksEnabled() {
return callbacksEnabled;
}
/**
* Enables/disables listener callbacks.
* #param enabled
*/
public void setCallbacksEnabled(boolean enabled) {
callbacksEnabled = enabled;
}
/**
* Connect and start reading. Both is done asynchronously. {#link RazorListener#onConnectOk()}
* or {#link RazorListener#onConnectFail(IOException)} callbacks will be invoked.
*
* #param numConnectAttempts Number of attempts to make when trying to connect. Often connecting
* only works on the 2rd try or later. Bluetooth hooray.
*/
public void asyncConnect(int numConnectAttempts) {
if (DEBUG) Log.d(TAG, "asyncConnect() BEGIN");
// Disconnect and wait for running thread to end, if needed
if (btThread != null) {
asyncDisconnect();
try {
btThread.join();
} catch (InterruptedException e) { }
}
// Bluetooth thread not running any more, we're definitely in DISCONNECTED state now
// Create new thread to connect to Razor AHRS and read input
this.numConnectAttempts = numConnectAttempts;
connectionState = ConnectionState.CONNECTING;
btThread = new BluetoothThread();
btThread.start();
if (DEBUG) Log.d(TAG, "asyncConnect() END");
}
/**
* Disconnects from Razor AHRS. If still connecting this will also cancel the connection process.
*/
public void asyncDisconnect() {
if (DEBUG) Log.d(TAG, "asyncDisconnect() BEGIN");
synchronized (connectionState) {
if (DEBUG) Log.d(TAG, "asyncDisconnect() SNYNCHRONIZED");
// Don't go to USER_DISCONNECT_REQUEST state if we are disconnected already
if (connectionState == ConnectionState.DISCONNECTED)
return;
// This is a wanted disconnect, so we force (blocking) I/O to break
connectionState = ConnectionState.USER_DISCONNECT_REQUEST;
closeSocketAndStreams();
}
if (DEBUG) Log.d(TAG, "asyncDisconnect() END");
}
/**
* Writes out a string using ASCII encoding. Assumes we're connected. Does not handle
* exceptions itself.
*
* #param text Text to send out
* #throws IOException
*/
private void write(String text) throws IOException {
outStream.write(EncodingUtils.getAsciiBytes(text));
}
/**
* Closes I/O streams and Bluetooth socket.
*/
private void closeSocketAndStreams() {
if (DEBUG) Log.d(TAG, "closeSocketAndStreams() BEGIN");
// Try to switch off streaming output of Razor in preparation of next connect
try {
if (outStream != null)
write("#o0");
} catch (IOException e) { }
// Close Bluetooth socket => I/O operations immediately will throw exception
try {
if (btSocket != null)
btSocket.close();
} catch (IOException e) { }
if (DEBUG) Log.d(TAG, "closeSocketAndStreams() BT SOCKET CLOSED");
// Close streams
try {
if (inStream != null)
inStream.close();
} catch (IOException e) { }
try {
if (outStream != null)
outStream.close();
} catch (IOException e) { }
if (DEBUG) Log.d(TAG, "closeSocketAndStreams() STREAMS CLOSED");
// Do not set socket and streams null, because input thread might still access them
//inStream = null;
//outStream = null;
//btSocket = null;
if (DEBUG) Log.d(TAG, "closeSocketAndStreams() END");
}
/**
* Thread that handles connecting to and reading from Razor AHRS.
*/
private class BluetoothThread extends Thread {
byte[] inBuf = new byte[512];
int inBufPos = 0;
/**
* Blocks until it can read one byte of input, assumes we have a connection up and running.
*
* #return One byte from input stream
* #throws IOException If reading input stream fails
*/
private byte readByte() throws IOException {
int in = inStream.read();
if (in == -1)
throw new IOException("End of Stream");
return (byte) in;
}
/**
* Converts a buffer of bytes to an array of floats. This method does not do any error
* checking on parameter array sizes.
* #param byteBuf Byte buffer with length of at least <code>numFloats * 4</code>.
* #param floatArr Float array with length of at least <code>numFloats</code>.
* #param numFloats Number of floats to convert
*/
private void byteBufferToFloatArray(byte[] byteBuf, float[] floatArr, int numFloats) {
//int numFloats = byteBuf.length / 4;
for (int i = 0; i < numFloats * 4; i += 4) {
// Convert from little endian (Razor) to big endian (Java) and interpret as float
floatArr[i/4] = Float.intBitsToFloat((byteBuf[i] & 0xff) + ((byteBuf[i+1] & 0xff) << 8) +
((byteBuf[i+2] & 0xff) << 16) + ((byteBuf[i+3] & 0xff) << 24));
}
}
/**
* Parse input stream for given token.
* #param token Token to find
* #param in Next byte from input stream
* #return <code>true</code> if token was found
*/
private boolean readToken(byte[] token, byte in) {
if (in == token[inBufPos++]) {
if (inBufPos == token.length) {
// Synch token found
inBufPos = 0;
if (DEBUG) Log.d(TAG, "Token found");
return true;
}
} else {
inBufPos = 0;
}
return false;
}
/**
* Synches with Razor AHRS and sets parameters.
* #throws IOException
*/
private void initRazor() throws IOException {
long t0, t1, t2;
// Start time
t0 = SystemClock.uptimeMillis();
/* See if Razor is there */
// Request synch token to see when Razor is up and running
final String contactSynchID = "00";
final String contactSynchRequest = "#s" + contactSynchID;
final byte[] contactSynchReply = EncodingUtils.getAsciiBytes(SYNCH_TOKEN + contactSynchID + NEW_LINE);
write(contactSynchRequest);
t1 = SystemClock.uptimeMillis();
while (true) {
// Check timeout
t2 = SystemClock.uptimeMillis();
if (t2 - t1 > 200) {
// 200ms elapsed since last request and no answer -> request synch again.
// (This happens when DTR is connected and Razor resets on connect)
write(contactSynchRequest);
t1 = t2;
}
if (t2 - t0 > INIT_TIMEOUT_MS)
// Timeout -> tracker not present
throw new IOException("Can not init Razor: response timeout");
// See if we can read something
if (inStream.available() > 0) {
// Synch token found?
if (readToken(contactSynchReply, readByte()))
break;
} else {
// No data available, wait
delay(5); // 5ms
}
}
/* Configure tracker */
// Set binary output mode, enable continuous streaming, disable errors and request synch
// token. So we're good, no matter what state the tracker currently is in.
final String configSynchID = "01";
final byte[] configSynchReply = EncodingUtils.getAsciiBytes(SYNCH_TOKEN + configSynchID + NEW_LINE);
write("#ob#o1#oe0#s" + configSynchID);
while (!readToken(configSynchReply, readByte())) { }
}
/**
* Opens Bluetooth connection to Razor AHRS.
* #throws IOException
*/
private void connect() throws IOException {
// Create Bluetooth socket
btSocket = btDevice.createRfcommSocketToServiceRecord(UUID_SPP);
if (btSocket == null) {
if (DEBUG) Log.d(TAG, "btSocket is null in connect()");
throw new IOException("Could not create Bluetooth socket");
}
// This could be used to create the RFCOMM socekt on older Android devices where
//createRfcommSocketToServiceRecord is not present yet.
/*try {
Method m = btDevice.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
btSocket = (BluetoothSocket) m.invoke(btDevice, Integer.valueOf(1));
} catch (Exception e) {
throw new IOException("Could not create Bluetooth socket using reflection");
}*/
// Connect socket to Razor AHRS
if (DEBUG) Log.d(TAG, "Canceling bt discovery");
BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); // Recommended
if (DEBUG) Log.d(TAG, "Trying to connect() btSocket");
btSocket.connect();
// Get the input and output streams
if (DEBUG) Log.d(TAG, "Trying to create streams");
inStream = btSocket.getInputStream();
outStream = btSocket.getOutputStream();
if (inStream == null || outStream == null) {
if (DEBUG) Log.d(TAG, "Could not create I/O stream(s) in connect()");
throw new IOException("Could not create I/O stream(s)");
}
}
/**
* Bluetooth I/O thread entry method.
*/
public void run() {
if (DEBUG) Log.d(TAG, "Bluetooth I/O thread started");
try {
// Check if btDevice is set
if (btDevice == null) {
if (DEBUG) Log.d(TAG, "btDevice is null in run()");
throw new IOException("Bluetooth device is null");
}
// Make several attempts to connect
int i = 1;
while (true) {
if (DEBUG) Log.d(TAG, "Connect attempt " + i + " of " + numConnectAttempts);
sendToParentThread(MSG_ID__CONNECT_ATTEMPT, i);
try {
connect();
break; // Alrighty!
} catch (IOException e) {
if (DEBUG) Log.d(TAG, "Attempt failed: " + e.getMessage());
// Maximum number of attempts reached or cancel requested?
if (i == numConnectAttempts || connectionState == ConnectionState.USER_DISCONNECT_REQUEST)
throw e;
// We couldn't connect on first try, manually starting Bluetooth discovery
// often helps
if (DEBUG) Log.d(TAG, "Starting BT discovery");
BluetoothAdapter.getDefaultAdapter().startDiscovery();
delay(5000); // 5 seconds - long enough?
i++;
}
}
// Set Razor output mode
if (DEBUG) Log.d(TAG, "Trying to set Razor output mode");
initRazor();
// We're connected and initialized (unless disconnect was requested)
synchronized (connectionState) {
if (connectionState == ConnectionState.USER_DISCONNECT_REQUEST) {
closeSocketAndStreams();
throw new IOException(); // Dummy exception to force disconnect
}
else connectionState = ConnectionState.CONNECTED;
}
// Tell listener we're ready
sendToParentThread(MSG_ID__CONNECT_OK, null);
// Keep reading inStream until an exception occurs
if (DEBUG) Log.d(TAG, "Starting input loop");
while (true) {
// Read byte from input stream
inBuf[inBufPos++] = (byte) readByte();
if (razorOutputMode == RazorOutputMode.YAW_PITCH_ROLL_ANGLES) {
if (inBufPos == 12) { // We received a full frame
float[] ypr = float3Pool.get();
byteBufferToFloatArray(inBuf, ypr, 3);
// Forward to parent thread handler
sendToParentThread(MSG_ID__YPR_DATA, ypr);
// Rewind input buffer position
inBufPos = 0;
}
} else { // Raw or calibrated sensor data mode
if (inBufPos == 36) { // We received a full frame
float[] amg = float9Pool.get();
byteBufferToFloatArray(inBuf, amg, 9);
// Forward to parent thread handler
sendToParentThread(MSG_ID__AMG_DATA, amg);
// Rewind input buffer position
inBufPos = 0;
}
}
}
} catch (IOException e) {
if (DEBUG) Log.d(TAG, "IOException in Bluetooth thread: " + e.getMessage());
synchronized (connectionState) {
// Don't forward exception if it was thrown because we broke I/O on purpose in
// other thread when user requested disconnect
if (connectionState != ConnectionState.USER_DISCONNECT_REQUEST) {
// There was a true I/O error, cleanup and forward exception
closeSocketAndStreams();
if (DEBUG) Log.d(TAG, "Forwarding exception");
if (connectionState == ConnectionState.CONNECTING)
sendToParentThread(MSG_ID__CONNECT_FAIL, e);
else
sendToParentThread(MSG_ID__IO_EXCEPTION_AND_DISCONNECT, e);
} else {
// I/O error was caused on purpose, socket and streams are closed already
}
// I/O closed, thread done => we're disconnected now
connectionState = ConnectionState.DISCONNECTED;
}
}
}
/**
* Sends a message to Handler assigned to parent thread.
*
* #param msgId
* #param data
*/
private void sendToParentThread(int msgId, Object o) {
if (callbacksEnabled)
parentThreadHandler.obtainMessage(msgId, o).sendToTarget();
}
/**
* Sends a message to Handler assigned to parent thread.
*
* #param msgId
* #param data
*/
private void sendToParentThread(int msgId, int i) {
if (callbacksEnabled)
parentThreadHandler.obtainMessage(msgId, i, -1).sendToTarget();
}
/**
* Wrapper for {#link Thread#sleep(long)};
* #param ms Milliseconds
*/
void delay(long ms) {
try {
sleep(ms); // Sleep 5ms
} catch (InterruptedException e) { }
}
}
/**
* Handler that forwards messages to the RazorListener callbacks. This handler runs in the
* thread this RazorAHRS object was created in and receives data from the Bluetooth I/O thread.
*/
private Handler parentThreadHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ID__YPR_DATA: // Yaw, pitch and roll data
float[] ypr = (float[]) msg.obj;
razorListener.onAnglesUpdate(ypr[0], ypr[1], ypr[2]);
float3Pool.put(ypr);
break;
case MSG_ID__AMG_DATA: // Accelerometer, magnetometer and gyroscope data
float[] amg = (float[]) msg.obj;
razorListener.onSensorsUpdate(amg[0], amg[1], amg[2], amg[3], amg[4], amg[5],
amg[6], amg[7], amg[8]);
float9Pool.put(amg);
break;
case MSG_ID__IO_EXCEPTION_AND_DISCONNECT:
razorListener.onIOExceptionAndDisconnect((IOException) msg.obj);
break;
case MSG_ID__CONNECT_ATTEMPT:
razorListener.onConnectAttempt(msg.arg1, numConnectAttempts);
break;
case MSG_ID__CONNECT_OK:
razorListener.onConnectOk();
break;
case MSG_ID__CONNECT_FAIL:
razorListener.onConnectFail((IOException) msg.obj);
break;
}
}
};
}
Related
I have been using a tcp client extension by Jean-Rodolphe Letertre for app inventor 2 and it works flawlessly until you call the disconnect method and than it crashes the app. After looking at the code for the extension i found that disconnect only shuts down output, input and than closes the socket which shouldn't cause any crashes so my suspicion fell on the connect method which runs a thread because it keeps reading data in a loop from a tcp socket and when we call disconnect we don't finish the thread which causes application crash because input is closed and an exception goes unhandled.
NOTE: The code is not mine and i don't ask to fix it for me i only want to know if iv'e found the problem which causes crashes and if so i will fix it myself. Thanks in advance for any help!
The code:
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.gmail.at.moicjarod;
import com.google.appinventor.components.runtime.*;
import com.google.appinventor.components.runtime.util.RuntimeErrorAlert;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.runtime.util.AsynchUtil;
import com.google.appinventor.components.runtime.util.ErrorMessages;
import com.google.appinventor.components.runtime.util.YailList;
import com.google.appinventor.components.runtime.util.SdkLevel;
import com.google.appinventor.components.runtime.errors.YailRuntimeError;
import android.app.Activity;
import android.text.TextUtils;
import android.util.Log;
import android.os.StrictMode;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/**
* Simple Client Socket
* #author moicjarod#gmail.com (Jean-Rodolphe Letertre)
* with the help of the work of lizlooney # google.com (Liz Looney) and josmasflores # gmail.com (Jose Dominguez)
* the help of Alexey Brylevskiy for debugging
* and the help of Hossein Amerkashi from AppyBuilder for compatibility with AppyBuilder
*/
#DesignerComponent(version = 4,
description = "Non-visible component that provides client socket connectivity.",
category = ComponentCategory.EXTENSION,
nonVisible = true,
iconName = "http://jr.letertre.free.fr/Projets/AIClientSocket/clientsocket.png")
#SimpleObject(external = true)
#UsesPermissions(permissionNames = "android.permission.INTERNET")
public class ClientSocketAI2Ext extends AndroidNonvisibleComponent implements Component
{
private static final String LOG_TAG = "ClientSocketAI2Ext";
private final Activity activity;
// the socket object
private Socket clientSocket = null;
// the address to connect to
private String serverAddress = "";
// the port to connect to
private String serverPort = "";
// boolean that indicates the state of the connection, true = connected, false = not connected
private boolean connectionState = false;
// boolean that indicates the mode used, false = string sent as is, true = String is considered as hexadecimal data and will be converted before sending
// same behavior is used when receiving data
private boolean hexaStringMode = false;
InputStream inputStream = null;
/**
* Creates a new Client Socket component.
*
* #param container the Form that this component is contained in.
*/
public ClientSocketAI2Ext(ComponentContainer container)
{
super(container.$form());
activity = container.$context();
// compatibility with AppyBuilder (thx Hossein Amerkashi <kkashi01 [at] gmail [dot] com>)
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
/**
* Method that returns the server's address.
*/
#SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "The address of the server the client will connect to.")
public String ServerAddress()
{
return serverAddress;
}
/**
* Method to specify the server's address
*/
#DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING)
#SimpleProperty
public void ServerAddress(String address)
{
serverAddress = address;
}
/**
* Method that returns the server's port.
*/
#SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "The port of the server the client will connect to.")
public String ServerPort()
{
return serverPort;
}
/**
* Method to specify the server's port
*/
#DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING)
#SimpleProperty
public void ServerPort(String port)
{
serverPort = port;
}
/**
* Method that returns the connection state
*/
#SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "The state of the connection - true = connected, false = disconnected")
public boolean ConnectionState()
{
return connectionState;
}
/**
* Method that returns the mode (string or hexastring)
*/
#SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "The mode of sending and receiving data.")
public boolean HexaStringMode()
{
return hexaStringMode;
}
/**
* Method to specify the mode (string or hexastring)
*/
#DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN)
#SimpleProperty
public void HexaStringMode(boolean mode)
{
hexaStringMode = mode;
}
/**
* Creates the socket, connect to the server and launches the thread to receive data from server
*/
#SimpleFunction(description = "Tries to connect to the server and launches the thread for receiving data (blocking until connected or failed)")
public void Connect()
{
if (connectionState == true)
{
throw new YailRuntimeError("Connect error, socket connected yet, please disconnect before reconnect !", "Error");
}
try
{
// connecting the socket
clientSocket = new Socket();
clientSocket.connect(new InetSocketAddress(serverAddress, Integer.parseInt(serverPort)), 5000);
connectionState = true;
// begin the receive loop in a new thread
AsynchUtil.runAsynchronously(new Runnable()
{
#Override
public void run()
{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int bytesRead;
try
{
// get the input stream and save the data
inputStream = clientSocket.getInputStream();
while (true)
{
// test if there is a server problem then close socket properly (thx Axeley :-))
try
{
bytesRead = inputStream.read(buffer);
if(bytesRead == -1)
break;
}
catch(SocketException e)
{
if(e.getMessage().indexOf("ETIMEDOUT") >= 0)
break;
throw e;
}
byteArrayOutputStream.write(buffer, 0, bytesRead);
final String dataReceived;
// hexaStringMode is false, so we don't transform the string received
if (hexaStringMode == false)
{
dataReceived = byteArrayOutputStream.toString("UTF-8");
}
// hexaStringMode is true, so we make a string with each character as an hexa symbol representing the received message
else
{
int i;
char hexaSymbol1, hexaSymbol2;
String tempData = "";
byte[] byteArray = byteArrayOutputStream.toByteArray();
for (i = 0; i < byteArrayOutputStream.size(); i++)
{
if (((byteArray[i] & 0xF0) >> 4) < 0xA)
// 0 to 9 symbol
hexaSymbol1 = (char)(((byteArray[i] & 0xF0) >> 4) + 0x30);
else
// A to F symbol
hexaSymbol1 = (char)(((byteArray[i] & 0xF0) >> 4) + 0x37);
if ((byteArray[i] & 0x0F) < 0xA)
hexaSymbol2 = (char)((byteArray[i] & 0x0F) + 0x30);
else
hexaSymbol2 = (char)((byteArray[i] & 0x0F) + 0x37);
tempData = tempData + hexaSymbol1 + hexaSymbol2;
}
dataReceived = tempData;
}
// reset of the byteArrayOutputStream to flush the content
byteArrayOutputStream.reset();
// then we send the data to the user using an event
// events must be sent by the main thread (UI)
activity.runOnUiThread(new Runnable()
{
#Override
public void run()
{
DataReceived(dataReceived);
}
} );
}
// When we go there, either we have
// - server shutdown
// - disconnection asked (inputstream closed => -1 returned)
// - connection problem
// so, if it is not disconnected yet, we disconnect the socket and inform the user of it.
if (connectionState == true)
{
Disconnect();
// events must be sent by the main thread (UI)
activity.runOnUiThread(new Runnable()
{
#Override
public void run()
{
RemoteConnectionClosed();
}
} );
}
}
catch (SocketException e)
{
Log.e(LOG_TAG, "ERROR_READ", e);
throw new YailRuntimeError("Connect error (read)" + e.getMessage(), "Error");
}
catch (IOException e)
{
Log.e(LOG_TAG, "ERROR_READ", e);
throw new YailRuntimeError("Connect error (read)", "Error");
}
catch (Exception e)
{
connectionState = false;
Log.e(LOG_TAG, "ERROR_READ", e);
throw new YailRuntimeError("Connect error (read)" + e.getMessage(), "Error");
}
}
} );
}
catch (SocketException e)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
throw new YailRuntimeError("Connect error" + e.getMessage(), "Error");
}
catch (Exception e)
{
connectionState = false;
Log.e(LOG_TAG, "ERROR_CONNECT", e);
throw new YailRuntimeError("Connect error (Socket Creation)" + e.getMessage(), "Error");
}
}
/**
* Send data through the socket to the server
*/
#SimpleFunction(description = "Send data to the server")
public void SendData(final String data)
{
final byte [] dataToSend;
byte [] dataCopy = data.getBytes();
if (connectionState == false)
{
throw new YailRuntimeError("Send error, socket not connected.", "Error");
}
if (hexaStringMode == false)
{
//dataSend = new byte [data.length()];
// if hexaStringMode is false, we send data as is
dataToSend = data.getBytes();
}
else
{
// if hexaStringMode is true, we begin to verify we can transcode the symbols
// verify if the data we want to send contains only hexa symbols
int i;
for (i = 0; i < data.length(); i++)
{
if (((dataCopy[i] < 0x30) || (dataCopy[i] > 0x39)) && ((dataCopy[i] < 0x41) || (dataCopy[i] > 0x46)) && ((dataCopy[i] < 0x61) || (dataCopy[i] > 0x66)))
throw new YailRuntimeError("Send data : hexaStringMode is selected and non hexa symbol found in send String.", "Error");
}
// verify that the number of symbols is even
if ((data.length() %2) == 1)
{
throw new YailRuntimeError("Send data : hexaStringMode is selected and send String length is odd. Even number of characters needed.", "Error");
}
// if all tests pass, we transcode the data :
dataToSend=new byte[data.length()/2+1];
for (i = 0; i < data.length(); i=i+2)
{
byte [] temp1 = new byte [2];
temp1 [0] = dataCopy[i];
temp1 [1] = dataCopy[i+1];
String temp2 = new String (temp1);
dataToSend[i/2]=(byte)Integer.parseInt(temp2, 16);
}
// end of c-type string character
dataToSend[i/2] = (byte)0x00;
}
// we then send asynchonously the data
AsynchUtil.runAsynchronously(new Runnable()
{
#Override
public void run()
{
try
{
OutputStream out;
out = clientSocket.getOutputStream();
out.write(dataToSend);
}
catch (SocketException e)
{
Log.e(LOG_TAG, "ERROR_SEND", e);
throw new YailRuntimeError("Send data" + e.getMessage(), "Error");
}
catch (Exception e)
{
Log.e(LOG_TAG, "ERROR_UNABLE_TO_SEND_DATA", e);
throw new YailRuntimeError("Send Data", "Error");
}
}
} );
}
/**
* Close the socket
*/
#SimpleFunction(description = "Disconnect to the server")
public void Disconnect()
{
if (connectionState == true)
{
connectionState = false;
try
{
// shutdown the input socket,
clientSocket.shutdownInput();
clientSocket.shutdownOutput();
clientSocket.close();
}
catch (SocketException e)
{
// modifications by axeley too :-)
if(e.getMessage().indexOf("ENOTCONN") == -1)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
throw new YailRuntimeError("Disconnect" + e.getMessage(), "Error");
}
// if not connected, then just ignore the exception
}
catch (IOException e)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
throw new YailRuntimeError("Disconnect" + e.getMessage(), "Error");
}
catch (Exception e)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
throw new YailRuntimeError("Disconnect" + e.getMessage(), "Error");
}
finally
{
clientSocket=null;
}
}
else
throw new YailRuntimeError("Socket not connected, can't disconnect.", "Error");
}
/**
* Event indicating that a message has been received
*
* #param data the data sent by the server
*/
#SimpleEvent
public void DataReceived(String data)
{
// invoke the application's "DataReceived" event handler.
EventDispatcher.dispatchEvent(this, "DataReceived", data);
}
/**
* Event indicating that the remote socket closed the connection
*
*/
#SimpleEvent
public void RemoteConnectionClosed()
{
// invoke the application's "RemoteConnectionClosed" event handler.
EventDispatcher.dispatchEvent(this, "RemoteConnectionClosed");
}
}
By commenting the following lines, it could be achieved:
throw new YailRuntimeError("...
The problem on this approach is that we wouldn't be able do know the disconnection reason, however we must agree that these messages are rather informative than functional in the sense of the application workflow, therefore an alternative option is to add the ability to disable these calls in runtime.
Hello I have a code written in Java and I need to create an TCP Connection with GPS device in android studio, where you can type in IP/PORT addresses, if someone can help me thanks in advance.
public class TCPConnection implements Runnable {
/**
* <h1>TCP Connection construct</h1>
* <p>The tcp connection requires two parameters socket and view model. </p>
* #param socket to establish connection.
* */
TCPConnection(Socket socket) {
super();
this.socket = socket;
converter = new Converter();
crc16 = new Crc16();
}
/**
* <h1>Run function to start listener</h1>
* <p>Simply runs the runnable thread to listen everything from client</p>
* */
public void run() {
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
Listen();
} catch (IOException e) {
e.printStackTrace();
}
}
Probably I need to create an button to start Listen incoming connections, also use Log class .....
/**
* <h1>Listen</h1>
* <p>Function for listening connected client</p>
* #throws IOException throws exception if input stream is interrupted
* */
private void Listen() throws IOException {
while (flag) {
System.out.println("listening...");
while (!socket.isClosed() && inputStream.available() == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
Communicate();
}
inputStream.close();
outputStream.close();
socket.close();
}
/**
* <h1>Get Number Of Records</h1>
* <p>Reads the number of records to send back to the sender</p>
* #param data the parameter is a received hex data
* #return String format number of records
* */
private String GetNumberOfRecords(String data) {
return data.substring(18, 20);
}
Everything is written in a comments line, why stackoverflow says add more details :D...
/**
* <h1>Communicate</h1>
* <p>A reader and sender with client, first it reads imei, then sends back 01.
* It receives data, as soon it receives it sends back number of records.
* The while loop initializes and runs until it get interrupted or client disconnects.</p>
* */
private void Communicate() {
imei = Objects.requireNonNull(ReadInput()).substring(4);
imei = converter.ReadImei(imei);
String path = System.getProperty("user.home") + "/Desktop";
logger = new Logger(path+"/Logs/TCPLogs/"+imei);
logger.PrintToLOG(GetTime()+" IMEI: " +imei);
if(imei.length() < 15){
SendOutput("00");
}
else{
SendOutput("01");
logger.PrintToLOG("\tResponse: [0" + 1 + "]");
String input = ReadInput();
Log(Objects.requireNonNull(input));
while(flag){
String recordsCount = GetNumberOfRecords(input);
SendOutput("000000" + recordsCount);
logger.PrintToLOG("\tCrc: " + Integer.toHexString(CRC(input)));
logger.PrintToLOG("\tResponse: [000000" + recordsCount + "]\n");
input = ReadInput();
Log(Objects.requireNonNull(input));
}
}
/**
* <h1>Send Output</h1>
* <p>Sends output to the client</p>
* #param message the parameter is a received hex data
* */
private void SendOutput(String message) {
try {
outputStream.write(converter.StringToByteArray(message));
outputStream.flush();
} catch (IOException e) {
System.out.println("Output stream was interrupted");
}
}
/**
* <h1>CRC</h1>
* <p>Calculates CRC of received data</p>
* #param str the parameter is a received hex data
* #return int of crc16
* */
private int CRC(String str) {
str = str.substring(16, str.length() - 8);
byte[] bytes = converter.StringToByteArray(str);
return crc16.getCRC(bytes);
}
/**
* <h1>Read Input</h1>
* <p>Reads the input from client. Currently maximum message byte is set up to 8192,
* if message is bigger then message will not be properly readable and displayed.</p>
* #return String of received data
* */
private String ReadInput() {
byte[] messageByte = new byte[8192];
int dataSize;
try {
dataSize = inputStream.read(messageByte);
String finalInput = converter.BytesArrayToHex(messageByte, dataSize);
SendToConsole(finalInput);
return finalInput;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* <h1>Send To Console</h1>
* <p>Simply prints out the results to the text area for user</p>
* #param input the parameter is String format to print in text area
* */
private void SendToConsole(String input) {
if(imei!=null)
{
String message = viewModel.getClientMessage() + "\r\nFrom imei - "+imei+" : " + input + "\n" + repeatChar();
Platform.runLater(() -> viewModel.setClientMessage(message));
}
else {
String message = viewModel.getClientMessage() + "\r\nReceived imei - : " + input + "\n" + repeatChar();
Platform.runLater(() -> viewModel.setClientMessage(message));
}
}
/**
* <h1>Log</h1>
* <p>Given String is being written to log file.</p>
* #param data the parameter is a received data
* */
private void Log(String data) {
logger.PrintToLOG("\tcodec : " + data.substring(16, 18));
logger.PrintToLOG("\tNumber of Records : " + GetNumberOfRecords(data));
logger.PrintToLOG("\tAVL data : " + data + "\n");
}
/**
* <h1>Set Running</h1>
* <p>Sets flag to run or stop while loop in order to interrupt the thread.</p>
* */
void setRunning() {
this.flag = false;
}
/**
* <h1>Repeat Char</h1>
* <p>Repeats the '=' character multiple times.</p>
* #return String is being returned.
* */
private String repeatChar() {
char[] data = new char[50];
Arrays.fill(data, '=');
return new String(data);
}
/**
* <h1>Get Time</h1>
* <p>Gets time when method is being called</p>
* #return Time in String format
* */
private String GetTime()
{
LocalDateTime localDateTime = LocalDateTime.now();
LocalTime localTime = localDateTime.toLocalTime();
return localTime.toString();
}
}
public class TCPServer implements Runnable {
private int port;
private Socket socket;
private ServerSocket ss;
private boolean running = true;
private ArrayList<TCPConnection> tcpConnections;
/**
* <h1>TCP server construct</h1>
* <p>The tcp server takes port parameter </p>
* #param port is required for server to listen all incoming connections
* */
public TCPServer(int port) {
this.port = port;
}
/**
* <h1>Run</h1>
* <p>Runs the runnable thread to listen connections, it accepts a connection, if accept was successful,
* the connection is added to tcpConnections list and runs the TCPConnection for further listening.
* The server is running in while loop and stops when Running is set to false,
* then break is called and shutdowns every connected client.</p>
* */
public void run() {
tcpConnections = new ArrayList<>();
try {
ss = new ServerSocket(port);
System.out.println("Listening on port : " + ss.getLocalPort());
ExecutorService executorService;
while (true) {
executorService = Executors.newSingleThreadExecutor();
socket = ss.accept();
TCPConnection connection = new TCPConnection(socket);
executorService.submit(connection);
tcpConnections.add(connection);
if (!running) {
StopConnections();
break;
}
}
executorService.shutdownNow();
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (IOException e) {
System.out.println("socket is closed");
}
}
/**
* <h1>Set Flag</h1>
* <p>Function is being called when we want to interrupt server thread and stop it.</p>
* #param flag the parameter sets whenever to true(run server) or false(stop server)
* */
public void setFlag(boolean flag) {
running = flag;
if (!running) {
try {
ss.close();
if (socket != null)
socket.close();
} catch (IOException e) {
System.out.println("Socket is " + socket.isClosed());
}
}
}
/**
* <h1>Stop Connections</h1>
* <p>Function is being called when we are stopping server,
* this function iterates through every connection and stops it.</p>
* */
private void StopConnections() {
if (!tcpConnections.isEmpty()) {
for (TCPConnection connections : tcpConnections) {
connections.setRunning();
}
tcpConnections.clear();
}
}
}
Android supports Java code as long as your android API level supports the Java version you are using. There shouldn't be any reason why you cannot use this in Android.
Just note that Android will throw an exception if you run any network tasks on the UI thread. E.g. Creating a socket should be run as an IntentService or AsyncTask, there are other options as well.
I trying to create a TCP server using NIO(Selector based) which can check whether any client is idle for more than 5 minutes.
I had been using time out on read operations using blocking IO, but there is no such option is provided in NIO.
Is there any efficient way to achieve this?
I achieved this by periodically checking idle clients.
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Logger;
/**
* {#link NIOServer} class is used to create a Non-Blocking TCP server listening
* on the port specified in the constructor parameter and listens for clients
* sending data.
*
* #author AchuthaRanga.Chenna
*
*/
public class NIOServer implements Runnable {
private Logger logger = Logger.getLogger(this.getClass().getName());
private InetAddress hostAddress;
private int port;
private ServerSocketChannel serverSocketChannel;
private Selector selector;
private boolean runServer = true;
private ConcurrentHashMap<SocketChannel, Long> clients = new ConcurrentHashMap<SocketChannel, Long>();
/**
* Constructor to pass the host and port of server.
*
* #param hostAddress
* address on which server has to be run.
* #param port
* port of the server.
**/
public NIOServer(InetAddress hostAddress, int port) throws IOException {
this.hostAddress = hostAddress;
this.port = port;
this.selector = initSelector();
IdleSocketChecker isc = new IdleSocketChecker();
new Thread(isc).start();
}
/**
* Method to create a ServerSocket and register to selector with option
* OP_ACCEPT to accept connections from clients.
*
* #return Selector registered with a serverSocket listening on a port to
* accept connections.
* #throws IOException
* on fail to create a selector or bind the server to the
* address.
*/
private Selector initSelector() throws IOException {
Selector socketSelector = SelectorProvider.provider().openSelector();
this.serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
InetSocketAddress isa = new InetSocketAddress(this.hostAddress, this.port);
serverSocketChannel.socket().setReuseAddress(true);
serverSocketChannel.socket().bind(isa);
serverSocketChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
return socketSelector;
}
/**
* Method with a infinite loop to iterate over the selected keys of the
* selector to know the option interested of the client sockets connected.
* Loop breaks when on IOException occurs on the selector which is listening
* for IOOperations of the client.
**/
#Override
public void run() {
while (runServer) {
try {
/*
* ".select" is a blocking call which invokes when any channel
* registered with the selector has an I/O operation to be done.
*/
this.selector.select();
/*
* Get a Iterator of the channels having I/O event to be
* handled.
*/
Iterator<SelectionKey> selectedKeys = this.selector.selectedKeys().iterator();
/* Iterate over the selected keys having I/O event to be done. */
while (selectedKeys.hasNext()) {
SelectionKey key = (SelectionKey) selectedKeys.next();
/* Remove the key to avoid infinite loop. */
selectedKeys.remove();
try {
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
this.accept(key);
} else if (key.isReadable()) {
this.read(key);
}
} catch (CancelledKeyException e) {// key has been canceled
}
}
/* Exception is generated when the Selector fails. */
/*
* Close the server and return from the while loop when Selector
* fails.
*/
} catch (IOException e) {
logger.error("[run] Exception on generation of client event", e);
try {
serverSocketChannel.close();
selector.close();
} catch (IOException e1) {
logger.error("[run] Exception on shutting down NIOSERVER due to selector is closed", e1);
}
break;
}
}
logger.info("[NIOSERVER thread closed normally]");
/* Clean up the resources */
this.releaseResources();
}
/**
* Method to release the resources used to create NIOSERVER SOCKET.
*/
public void releaseResources() {
try {
this.serverSocketChannel.close();
} catch (IOException e) {
logger.warn("[run]", e);
}
try {
this.selector.close();
} catch (IOException e) {
logger.warn("[run]", e);
}
}
/**
* Method to return socket status.
*
* #return
*/
public boolean isConencted() {
boolean status = false;
try {
status = serverSocketChannel.isOpen();
} catch (Exception e) {
}
return status;
}
/**
* Utility method to stop the server thread.
*
* #param runServer
* Flag decides to stop Server
*/
public void shutDown() {
this.runServer = false;
logger.info("[shutDown] Server is stopped");
}
/**
* Method to accept connections from clients and registering for reading
* data from clients.Set's a KeepAlive option of the socket true and
* register the connected socket for READ option.
*
* #param key
* which is ready to acceptable
*/
private void accept(SelectionKey key) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
try {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
/* Set the KeepAlive flag to avoid continuous open of files */
socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
socketChannel.configureBlocking(false);
/* Register the client connected with our interested Option Read */
socketChannel.register(this.selector, SelectionKey.OP_READ);
// key.attach(System.currentTimeMillis());
clients.put(socketChannel, System.currentTimeMillis());
logger.debug("[accept] New Client connected from " + socketChannel.getRemoteAddress());
logger.info("[accept] Total connected : " + clients.size());
// System.out.println(socketChannel.hashCode());
} else {
key.cancel();
}
} catch (IOException e) {
key.cancel();
logger.error("[accept] Error while accepting new connectins", e);
}
}
/**
* * Method to read data from key having read event pending.
*
* #param key
* SelectionKey having read event.
*/
private void read(SelectionKey key) {
SocketChannel socketChannel = (SocketChannel) key.channel();
synchronized (socketChannel) {
if (socketChannel.isOpen()) {
try {
ByteBuffer readBuffer = ByteBuffer.allocate(150);
readBuffer.clear();
int numRead = 0;
try {
/* ".read" is nonblocking */
numRead = socketChannel.read(readBuffer);
/*
* Some other IO error on reading socketChannel.
*/
} catch (IOException e) {
logger.debug("[run] Connection abruptly terminated from client", e);
key.channel().close();
clients.remove(socketChannel);
return;
}
if (numRead == -1) {// socket closed cleanly
key.channel().close();
clients.remove(socketChannel);
return;
}
String data = null;
data = new String(readBuffer.array(), Charset.forName("ASCII"));
logger.info(data);
/* Send the read data to the DataDispatcher Actor */
clients.put(socketChannel, System.currentTimeMillis());
} catch (IOException e) {
logger.debug("[run] ", e);
return;
}
} else {// socketChannel is closed
try {
key.channel().close();// Sanitary close operation
clients.remove(key);
return;
} catch (IOException e) {
}
}
}
logger.info("[checkIdleSockets] Total connected : " + clients.size());
}
/**
* Method to check the sockets idle for 15 minutes and close the socket.
*/
private void checkIdleSockets() {
// synchronized (clients) {
Iterator<Entry<SocketChannel, Long>> iter = clients.entrySet().iterator();
while (iter.hasNext()) {
try {
Map.Entry<SocketChannel, Long> entry = iter.next();
SocketChannel client = entry.getKey();
long mills = entry.getValue();
double minutes = (System.currentTimeMillis() - mills) / (double) (1000 * 60);
if (minutes > 5) {
/* key is idle for */
logger.info("[IdleSocketChecker] Socket is idle for " + Math.round(minutes) + ", closing......");
try {
client.close();
} catch (IOException e) {
} finally {
iter.remove();
}
}
} catch (Exception e) {
logger.info("[IdleSocketChecker] ", e);
}
}
// }
logger.info("[checkIdleSockets] Total connected : " + clients.size());
}
/**
* {#link IdleSocketChecker} is a thread to check for any idle sockets in
* the selector.
*
* #author AchuthaRanga.Chenna
*
*/
private class IdleSocketChecker implements Runnable {
private boolean RUN = true;
#Override
public void run() {
try {
while (RUN) {
/* Wait for 5 Minutes */
Thread.sleep(5 * 60 * 1000);
checkIdleSockets();
}
} catch (InterruptedException e) {
logger.warn("[IdleSocketChecker]<run> IdleSocketChecker thread stopped", e);
}
}
}
}
This question already has answers here:
Continuously read objects from an ObjectInputStream in Java
(4 answers)
Closed 7 years ago.
A couple of weeks ago, I posted the following question because I had problems with reading objects from an ObjectInputStream using readObject:
Continuously read objects from an ObjectInputStream in Java
With the responds I got, I think I was able to understand what is going wrong -> I am calling readObject in a loop, even if no data has been send en therefore I receive an EOFException.
However, because I really want a mechanism where I am continuesly reading from the input stream I am looking for a solution for this problem.
I tried to use the following to create a mechanism where I only call readObject when there is data available:
if(mObjectIn.available() > 0)
mObjectIn.readObject()
But unfornately, mObjectIn.available() always returns 0.
Can anyone get me in the good direction. Is it possible at all to implement what I want??
You can send an int through the ObjectOutputStream to let the other side know when you will stop sending objects.
For example:
public static void main(String[] args) {
//SERVER
new Thread(new Runnable() {
#Override
public void run() {
try (ServerSocket ss = new ServerSocket(1234)) {
try (Socket s = ss.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(
s.getInputStream())) {
while (ois.readInt() != -1) {//Read objects until the other side sends -1.
System.out.println(ois.readObject());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
//CLIENT
try (Socket s = new Socket(InetAddress.getByName("localhost"), 1234)) {
try (ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream())) {
for (int i = 0; i < 10; i++) {
oos.writeInt(1);//Specify that you are still sending objects.
oos.writeObject("Object" + i);
oos.flush();
}
oos.writeInt(-1);//Let the other side know that you've stopped sending object.
}
} catch (Exception e) {
e.printStackTrace();
}
}
Or you can write a null object at the end to let the other side know you won't be sending any more objects. This will work only if you are sure that none of the objects you need to send are null.
new Thread(new Runnable() {
#Override
public void run() {
try (ServerSocket ss = new ServerSocket(1234)) {
try (Socket s = ss.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(
s.getInputStream())) {
String obj;
while ((obj = (String) ois.readObject()) != null) {
System.out.println(obj);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
try (Socket s = new Socket(InetAddress.getByName("localhost"), 1234)) {
try (ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream())) {
for (int i = 0; i < 10; i++) {
oos.writeObject("Object" + i);
oos.flush();
}
oos.writeObject(null);
}
} catch (Exception e) {
e.printStackTrace();
}
I have written client/server tcp code in Java before. Streams can be tricky. When you work with streams you cannot assume all the data is there when you do a read. Instead you have to pull bytes out of the stream and if some msg delimiter is reached, then you take all those acquired bytes and process them. In your case you will turn them into an object.
Below are three classes I use to extract msg's from a socket.
First the MsgExtractor that get as input bytes that would essentially come from a socket. It checks to see if a msg has arrived (via the delimiter).
import org.apache.log4j.Logger;
//~--- JDK imports ------------------------------------------------------------
import java.text.ParseException;
import java.util.Arrays;
import java.util.MissingResourceException;
import java.util.concurrent.BlockingQueue;
/**
* This class parses the data retrieved from the TCP socket streams for new messages. All new messages found are inserted into the shared message queue.
*
* #author jmartinez
*/
public class MsgExtractor {
//////////////////////////////////////////////////////////////////////////
// STATIC VARIBLES
private static final Logger logger = Logger.getLogger(MsgExtractor.class);
///////////////////////////////////////////////////////////////////////
// CONSTANTS
private final int INIT_BUFFER_SIZE = 10000;
// <buffer variables>
private byte[] bufferedMsg = new byte[INIT_BUFFER_SIZE];
private int bufferSize = INIT_BUFFER_SIZE;
private int curBufferPos = 0; // ...current position on the buffered message
private int curMsgSize = 0; // ...current amount of buffered chars
///////////////////////////////////////////////////////////////////////
// VARIABLES
private final byte[] delimiter;
private final int delimiterSize;
private final BlockingQueue msgQueue;
private final int maxMsgSize;
// </>
////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/**
* Creates a new MsgExtractor.
*
* #param msgService
*/
public MsgExtractor(MessageService msgService) {
ServerProperties properties = ServerProperties.getInstance();
if (properties == null) {
throw new MissingResourceException("unable to obtain properties", MsgExtractor.class.getName(),
"ServerProperties");
}
this.maxMsgSize = Integer.parseInt(properties.getProperty(ServerProperties.MAX_MESSAGE_SIZE));
this.delimiter = Arrays.copyOf(msgService.getMsgHandler().getMessageDelmiter(),
msgService.getMsgHandler().getMessageDelmiter().length);
this.delimiterSize = delimiter.length;
this.msgQueue = msgService.getSharedMsgQueue();
}
/**
* Inserts new chars into the message buffer. It then extracts any messages found in the buffer by checking for any occurrences of the message delimiter. Extracted messages are removed from the buffer, converted to String, and inserted into the ManagedQueue.
*
* #param cbuf - An array containing the new chars that need to be added to the message buffer.
* #param offset - Array offset from where on the array to start collecting the new chars.
* #param length - The number of chars that need to be collected.
* #throws java.lang.InterruptedException
* #throws java.text.ParseException
*/
public void insertNewChars(byte[] cbuf, int offset, int length) throws InterruptedException, ParseException {
// ...check if the message buffer has enough room to add the new chars... if not, increase the buffer size.
if (bufferSize < curMsgSize + length) {
increaseBufferSize();
}
// ...add the new chars to the buffer one at a time
for (int i = 0; i < length; i++) {
bufferedMsg[curMsgSize++] = cbuf[i + offset];
}
// ...keep checking for new messages as long as they are being found
boolean rv;
do {
rv = checkForNewMsg();
} while (rv == true);
if (curMsgSize > maxMsgSize) {
throw new ParseException("max message size reached and still not found delimiter", curMsgSize);
}
}
/**
* Doubles the message buffer size.
*/
private void increaseBufferSize() {
bufferSize *= 2;
byte[] temp = new byte[bufferSize];
System.arraycopy(bufferedMsg, 0, temp, 0, curMsgSize);
bufferedMsg = temp;
}
/**
* Checks if the delimiter is found in the currently buffered message.
* checkForNewMsg starts its search where it last left off at.
*
* Performance can be improved if this method checks for all occurrences of the message delimiter, instead of one.
*
* #return true if delimiter was found in buffer, else false
*/
private boolean checkForNewMsg() throws InterruptedException {
while (curBufferPos <= curMsgSize - delimiterSize) {
boolean delimitterFound = true;
for (int i = 0; i < delimiterSize; i++) {
if (delimiter[i] != bufferedMsg[i + curBufferPos]) {
delimitterFound = false;
break;
}
}
if (delimitterFound) {
extractNewMsg(curBufferPos);
return true;
} else {
curBufferPos++;
}
}
return false;
}
/**
* A new message is located at index = 0 through delimiterPos - 1. the method extracts that message and inserts it into a local String array.
*
* Performance can be improved if this method extracted a messages for all occurrences of the message delimiter, instead of one.
*
* #param delimiterPos - The position where the delimiter was found.
*/
private void extractNewMsg(int delimiterPos) throws InterruptedException {
try {
msgQueue.put(new String(bufferedMsg, 0, delimiterPos - 1));
} catch (InterruptedException ie) {
logger.error("Interrupted while putting message to ManagedQueue", ie);
throw ie;
} catch (Exception e) {
logger.error("Unable to put message to ManagedQueue", e);
}
// <reset the message buffer and corresponding variables>
byte[] tmpBuffer = new byte[this.bufferSize];
int tmpMsgSize = 0;
for (int i = delimiterPos + this.delimiterSize; i < curMsgSize; i++) {
tmpBuffer[tmpMsgSize++] = bufferedMsg[i];
}
curBufferPos = 0;
bufferedMsg = tmpBuffer;
curMsgSize = tmpMsgSize;
// </>
}
}
Here is the ConnectionHandler that manages the connection and feeds the MsgExtractor with bytes:
import org.apache.log4j.Logger;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.text.ParseException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class handles all new connections. It reads data from the socket stream
* and sends it to a MsgExtractor for further processing.
*
* This class in Runnable. use the run method to start it up and interrupt it to
* shut it down.
*
* #author Jose
*/
public class ConnectionHandler implements Runnable {
//////////////////////////////////////////////////////////////////////////
// STATIC VARIBLES
// ...log4j's Logger is thread safe
private static final Logger logger = Logger.getLogger(ConnectionHandler.class);
private final static AtomicBoolean isRunning = new AtomicBoolean(false);
/////////////////////////////////////////////////////////////////////////
// Constants
private final int BUFFER_SIZE = 8000;
private final byte[] rcvdChars = new byte[BUFFER_SIZE];
////////////////////////////////////////////////////////////////////////
// INSTANCE VARIABLES
private final Socket socket;
private final MsgExtractor msgExtractor;
/////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/**
* Creates a new ConnectionHandler.
*
* #param socket - The socket that this object is to read from.
* #param msgService - The MessageService that is used to create the
* MsgExtractor object that this object uses.
*/
public ConnectionHandler(Socket socket, MessageService msgService) {
this.socket = socket;
logger.info("ConnectionHandler thread ID:" + Thread.currentThread().getId() + " instanctiated for listen port "
+ socket.getLocalPort());
msgExtractor = new MsgExtractor(msgService);
}
/**
* Starts the ConnectionHandler. Creates an input stream from this objects
* socket to read data from. all read data is sent to a MsgExtractor. The
* MSgExtractor will extract messages from the read data and will add any
* messages to this objects ManagedQueue. This method continues operating
* till the thread is interrupted or the socket is no longer available for
* providing input. Returns right away if validation of this object fails.
*/
public void run() {
// ...if validation fails, return
if (isValid() == false) {
return;
}
// ...if already running, return
if (!isRunning.compareAndSet(false, true)) {
logger.warn("ConnectionHandler thead ID:" + Thread.currentThread().getId()
+ " is already running, not going to run again.");
return;
}
logger.info("ConnectionHandler thead ID:" + Thread.currentThread().getId() + " is starting up.");
// <get input reader from socket>
InputStream inputReader;
try {
inputReader = socket.getInputStream();
} catch (IOException ex) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", failed to get socket input stream in ParserThread.run", ex);
return;
}
// </>
// ...bytes read from the socket
int bytesRead;
try {
// ...stops when the thread is interrupted or the socket no longer provides input
while ((socket.isInputShutdown() == false) || (Thread.interrupted() == false)) {
try {
// ...get data from socket stream
bytesRead = inputReader.read(rcvdChars, 0, BUFFER_SIZE);
} catch (IOException e) { // ... catch any exception and call it a day for this thread
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", encountered error reading from socket, could be a closed connection.", e);
break;
}
try {
msgExtractor.insertNewChars(rcvdChars, 0, bytesRead);
} catch (ParseException pe) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", encountered parsing error, closing connection.", pe);
break;
} catch (InterruptedException ex) {
break;
}
}
} finally {
// ...close the socket if it is still open
if (socket.isClosed() == false) {
try {
socket.close();
} catch (IOException ex) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", failed to close socket.", ex);
}
}
isRunning.set(false);
} // end of: finally
logger.info("ConnectionHandler thead ID:" + Thread.currentThread().getId() + " is shutting down.");
}
/**
* Used by the run() method to validate this object. If validation fails,
* the run() method returns right away.
*
* #return - Returns true is this object is valid, else false.
*/
private boolean isValid() {
if (socket == null) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", validation failed, Socket is null");
return false;
}
return true;
}
}
And for completion here is the MsgService that pops up in the above code:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* The Model Object, or DTO, of the CMS server. It contains information that is needed by just about every component of the CMS server.
*
* There is a separate instance of this class for each messaging service that the CMS server is configured for.
*
* #author Jose
*/
public class MessageService {
/**
* the shared message queue where new messages are inserted into by the MsgExtractor, and taken by the MessageQueueWorker.
*/
private final BlockingQueue<byte[]> sharedMsgQueue = new LinkedBlockingQueue<byte[]>();
/**
* the TCP listen port
*/
private final int port;
/**
* the MessageHandler that will process new messages
*/
private final MessageHandler msgHandler;
/**
* optional. max number of TCP connections
*/
private final int maxConnections;
/**
* optional. max number of worker threads that will be used to process new messages
*/
private final int maxWorkerThreads;
/**
* Creates new message service object. Sets max connections and max worker threads to 0.
*
* #param port - TCP port this message service will listen on.
* #param msgHandler - the MessageHandler that will be called to process messages received for this message service.
*/
public MessageService(int port, MessageHandler msgHandler) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = 0;
this.maxWorkerThreads = 0;
}
/**
* Creates new message service object. Sets max worker threads to 0.
*
* #param port - TCP port this message service will listen on.
* #param msgHandler - the MessageHandler that will be called to process messages received for this message service.
* #param connections - max concurrent connections available for this service.
*/
public MessageService(int port, MessageHandler msgHandler, int connections) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = connections;
this.maxWorkerThreads = 0;
}
/**
* Creates new message service object.
*
* #param port - TCP port this message service will listen on.
* #param msgHandler - the MessageHandler that will be called to process messages received for this message service.
* #param connections - max concurrent connections available for this service.
* #param workerThreads - max worker threads that will process messages for this message service.
*/
public MessageService(int port, MessageHandler msgHandler, int connections, int workerThreads) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = connections;
this.maxWorkerThreads = workerThreads;
}
/**
*
* #return this object's MessageHandler
*/
public MessageHandler getMsgHandler() {
return msgHandler.getNewInstance();
}
/**
*
* #return the TCP port this MessageService will listen on
*/
public int getPort() {
return port;
}
/**
*
* #return the BlockingQueue used to store new messages.
*/
public BlockingQueue<byte[]> getSharedMsgQueue() {
return sharedMsgQueue;
}
/**
*
* #return max concurrent connections available for this service
*/
public int getMaxConnections() {
return maxConnections;
}
/**
*
* #return max worker threads that will process messages for this message service
*/
public int getMaxWorkerThreads() {
return this.maxWorkerThreads;
}
}
I have modified the android app "Bluetooth Chat" that you can find in android sdk examples version 2.1
The app estabilished a bluetooth connection with arduino, and, when with the app I send 0 or 1, arduino send a simple message "You have pressed 0 or 1".
It works if I test with eclipse's debug, but when I test with my smartphone, the result in the display is different, arduino's string is fragmented
example: smartphone: 0 -> arduino "You have pressed 0 or 1"
smartphone display: "y"
"ou pr"
The rest of the string was lost or not shown in the display.
Can you help me?
No error in logcat, only this bug.
This is the code:
public class BluetoothLampService {
// Debugging
private static final String TAG = "BluetoothLampService";
private static final boolean D = true;
// Name for the SDP record when creating server socket
private static final String NAME = "BluetoothLamp";
// Unique UUID for this application - Standard SerialPortService ID
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// Member fields
private final BluetoothAdapter Adapter;
private final Handler Handler;
// private AcceptThread AcceptThread;
private ConnectThread ConnectThread;
private ConnectedThread ConnectedThread;
private int State;
// Constants that indicate the current connection state
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3; // now connected to a remote device
/**
* Constructor. Prepares a new BluetoothChat session.
* #param context The UI Activity Context
* #param handler A Handler to messages back to the UI Activity
*/
public BluetoothLampService(Context context, Handler handler) {
Adapter = BluetoothAdapter.getDefaultAdapter();
State = STATE_NONE;
Handler = handler;
}
/**
* Set the current state of the chat connection
* #param state An integer defining the current connection state
*/
private synchronized void setState(int state) {
State = state;
// Give the new state to the Handler so the UI Activity can update
Handler.obtainMessage(MainActivity.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}
/**
* Return the current connection state. */
public synchronized int getState() {
return State;
}
/**
* Start the chat service. Specifically start AcceptThread to begin a
* session in listening (server) mode. Called by the Activity onResume() */
public synchronized void start() {
// Cancel any thread attempting to make a connection
if (ConnectThread != null) {ConnectThread.cancel(); ConnectThread = null;}
// Cancel any thread currently running a connection
if (ConnectedThread != null) {ConnectedThread.cancel(); ConnectedThread = null;}
// Start the thread to listen on a BluetoothServerSocket
// if (AcceptThread == null) {
// AcceptThread = new AcceptThread();
// AcceptThread.start();
// }
setState(STATE_LISTEN);
}
/**
* Start the ConnectThread to initiate a connection to a remote device.
* #param device The BluetoothDevice to connect
*/
public synchronized void connect(BluetoothDevice device) {
// Cancel any thread attempting to make a connection
if (State == STATE_CONNECTING) {
if (ConnectThread != null) {ConnectThread.cancel(); ConnectThread = null;}
}
// Cancel any thread currently running a connection
if (ConnectedThread != null) {ConnectedThread.cancel(); ConnectedThread = null;}
// Start the thread to connect with the given device
ConnectThread = new ConnectThread(device);
ConnectThread.start();
setState(STATE_CONNECTING);
}
/**
* Start the ConnectedThread to begin managing a Bluetooth connection
* #param socket The BluetoothSocket on which the connection was made
* #param device The BluetoothDevice that has been connected
*/
public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
// Cancel the thread that completed the connection
if (ConnectThread != null) {ConnectThread.cancel(); ConnectThread = null;}
// Cancel any thread currently running a connection
if (ConnectedThread != null) {ConnectedThread.cancel(); ConnectedThread = null;}
// Cancel the accept thread because we only want to connect to one device
// if (AcceptThread != null) {AcceptThread.cancel(); AcceptThread = null;}
// Start the thread to manage the connection and perform transmissions
ConnectedThread = new ConnectedThread(socket);
ConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = Handler.obtainMessage(MainActivity.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(MainActivity.DEVICE_NAME, device.getName());
msg.setData(bundle);
Handler.sendMessage(msg);
setState(STATE_CONNECTED);
}
/**
* Stop all threads
*/
public synchronized void stop() {
if (ConnectThread != null) {ConnectThread.cancel(); ConnectThread = null;}
if (ConnectedThread != null) {ConnectedThread.cancel(); ConnectedThread = null;}
// if (AcceptThread != null) {AcceptThread.cancel(); AcceptThread = null;}
setState(STATE_NONE);
}
/**
* Write to the ConnectedThread in an unsynchronized manner
* #param out The bytes to write
* #see ConnectedThread#write(byte[])
*/
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (State != STATE_CONNECTED) return;
r = ConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}
/**
* Indicate that the connection attempt failed and notify the UI Activity.
*/
private void connectionFailed() {
setState(STATE_LISTEN);
// Send a failure message back to the Activity
Message msg = Handler.obtainMessage(MainActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(MainActivity.TOAST, "Unable to connect device");
msg.setData(bundle);
Handler.sendMessage(msg);
}
/**
* Indicate that the connection was lost and notify the UI Activity.
*/
private void connectionLost() {
setState(STATE_LISTEN);
// Send a failure message back to the Activity
Message msg = Handler.obtainMessage(MainActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(MainActivity.TOAST, "Device connection was lost");
msg.setData(bundle);
Handler.sendMessage(msg);
}
/**
* This thread runs while listening for incoming connections. It behaves
* like a server-side client. It runs until a connection is accepted
* (or until cancelled).
*/
/*
private class AcceptThread extends Thread {
// The local server socket
private final BluetoothServerSocket ServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
// Create a new listening server socket
try {
tmp = Adapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) {
}
ServerSocket = tmp;
}
public void run() {
//Looper.prepare();
setName("AcceptThread");
BluetoothSocket socket = null;
// Listen to the server socket if we're not connected
while (State != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = ServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
synchronized (BluetoothLampService.this) {
switch (State) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
// Either not ready or already connected. Terminate new socket.
try {
socket.close();
} catch (IOException e) {
}
break;
}
}
}
}
// Looper.loop();
}
public void cancel() {
try {
ServerSocket.close();
} catch (IOException e) {
}
}
}
*/
/**
* This thread runs while attempting to make an outgoing connection
* with a device. It runs straight through; the connection either
* succeeds or fails.
*/
private class ConnectThread extends Thread {
private final BluetoothSocket Socket;
private final BluetoothDevice Device;
public ConnectThread(BluetoothDevice device) {
Device = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
}
Socket = tmp;
}
public void run() {
Looper.prepare();
setName("ConnectThread");
// Always cancel discovery because it will slow down a connection
Adapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
Socket.connect();
} catch (IOException e) {
connectionFailed();
// Close the socket
try {
Socket.close();
} catch (IOException e2) {}
// Start the service over to restart listening mode
BluetoothLampService.this.start();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothLampService.this) {
ConnectThread = null;
}
// Start the connected thread
connected(Socket, Device);
Looper.loop();
}
public void cancel() {
try {
Socket.close();
} catch (IOException e) {
}
}
}
/**
* This thread runs during a connection with a remote device.
* It handles all incoming and outgoing transmissions.
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket Socket;
private final InputStream InStream;
private final OutputStream OutStream;
public ConnectedThread(BluetoothSocket socket) {
Socket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
InStream = tmpIn;
OutStream = tmpOut;
}
public void run() {
Looper.prepare();
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
// FUNZIONANTE
bytes = InStream.read(buffer);
Log.i("BYTES", Integer.toString(bytes));
//String dati = new String(buffer);
//fine aggiunto da me
// Send the obtained bytes to the UI Activity
Handler.obtainMessage(MainActivity.MESSAGE_READ, 27, -1, buffer).sendToTarget();//buffer
} catch (IOException e) {
connectionLost();
break;
}
}
Looper.loop();
}
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
OutStream.write(buffer);
// Share the sent message back to the UI Activity
Handler.obtainMessage(MainActivity.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
} catch (IOException e) {
}
}
public void cancel() {
try {
Socket.close();
} catch (IOException e) {
}
}
}
}
Have you already gived permission to your android app on your Manifest file?
uses-permission android:name="android.permission.BLUETOOTH" /
uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /
Then check the serial baude rate on your arduino serial connection too.
Kindly try by first pairing the device explicitly from your Android phone's settings. Usually the pair code is 1234