Maintaining a TCP Connection in an AsyncTask - java

I'm using an AsyncTask to establish a TCP Connection and sending/receiving data through it.
My current Code looks like this at the moment:
public class NetworkTask extends AsyncTask<Void, byte[], Boolean> {
Socket nsocket; //Network Socket
InputStream nis; //Network Input Stream
OutputStream nos; //Network Output Stream
boolean bSocketStarted = false;
byte[] buffer = new byte[4096];
#Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute");
}
#Override
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
// Connect to address
Log.i(TAG, "doInBackground: Creating socket");
SocketAddress sockaddr = new InetSocketAddress("google.de", 80);
nsocket = new Socket();
nsocket.connect(sockaddr, 5000); //10 second connection timeout
if (nsocket.isConnected()) {
bSocketStarted = true;
nis = nsocket.getInputStream();
nos = nsocket.getOutputStream();
Log.i("AsyncTask", "doInBackground: Socket created, streams assigned");
Log.i("AsyncTask", "doInBackground: Waiting for inital data...");
int read = nis.read(buffer, 0, 4096); //This is blocking
while(bSocketStarted) {
if (read > 0){
byte[] tempdata = new byte[read];
System.arraycopy(buffer, 0, tempdata, 0, read);
publishProgress(tempdata);
Log.i(TAG, "doInBackground: Got some data");
}
}
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "doInBackground: IOException");
result = true;
} catch (Exception e) {
e.printStackTrace();
Log.i(TAG, "doInBackground: Exception");
result = true;
} finally {
try {
nis.close();
nos.close();
nsocket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Log.i(TAG, "doInBackground: Finished");
}
return result;
}
public boolean SendDataToNetwork(final byte[] cmd) { //You run this from the main thread.
// Wait until socket is open and ready to use
waitForSocketToConnect();
if (nsocket.isConnected()) {
Log.i(TAG, "SendDataToNetwork: Writing received message to socket");
new Thread(new Runnable() {
public void run() {
try {
nos.write(cmd);
}
catch (Exception e) {
e.printStackTrace();
Log.i(TAG, "SendDataToNetwork: Message send failed. Caught an exception");
}
}
}
).start();
return true;
}
else
Log.i(TAG, "SendDataToNetwork: Cannot send message. Socket is closed");
return false;
}
public boolean waitForSocketToConnect() {
// immediately return if socket is already open
if (bSocketStarted)
return true;
// Wait until socket is open and ready to use
int count = 0;
while (!bSocketStarted && count < 10000) {
try {
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
count += 500;
}
return bSocketStarted;
}
#Override
protected void onProgressUpdate(byte[]... values) {
try {
if (values.length > 0) {
Log.i(TAG, "onProgressUpdate: " + values[0].length + " bytes received.");
String str = new String(buffer, "UTF8");
Log.i(TAG,str);
tv.setText(str);
tv.setMovementMethod(new ScrollingMovementMethod());
}
} catch (Exception e) {
e.printStackTrace();
}
finally {}
}
#Override
protected void onCancelled() {
Log.i(TAG, "Cancelled.");
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
Log.i(TAG, "onPostExecute: Completed with an Error.");
} else {
Log.i(TAG, "onPostExecute: Completed.");
}
}
}
I can instantiate the Task and call SendDataToNetwork from my activity. However, all the text I pass to SendDataToNetwork, for example, 'GET / HTTP/1.1' is continously sent to the server.
How can I modify my Code to maintain the connection in doInBackground and do nothing until I call SendDataToNetwork and after sending bytes to the server just wait until new data is ready to be sent? Basically I want to run the AsyncTask until I explicitly cancel (= close the connection) it.

nsocket.connect(sockaddr, 5000); //10 second connection timeout
if (nsocket.isConnected()) {
The test is pointless. If the socket wasn't connected, connect() would have thrown an exception.
Your read loop is also fundamentally flawed, in that it doesn't keep reading. There are standard solutions as to how to read a stream, e.g.:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Your waitForSocketToConnect() method doesn't really do anything useful either.
You need to rethink all this.

Related

Wrong messages sequence with socket DataInputStream BufferedInputStream in Android app

I have a problem with receiving irregular sequence of the byte messages I send from another device.
The setup is the following: I have an Android app (client) and Real-Time system (server) with Ethernet both connected in a LAN through router, which talk with raw bytes communication.
From the Android app I send request, which causes the server to respond with several messages - the first one with 8 bytes, the following messages have 27 bytes. I have debugged the server and I am sure the first message it sends is the 8th-byte one, followed by the others.
About the app - I use the Main Activity to handle transmission of data through the socket, and additional thread to handle reception of data.
The thread makes post through Handler to the Main Activity, when new data has been received. In this post is called a process to parse the received data.
TbProtocolProcessor is a class I use to handle my custom protocol. It can create a byte array for me to send as request for specific function, and it has a state-machine to process expected response from the server. InetHandler is nested class I use to handle my connectivity only.
My question is - why would my Android app return me the first message having size 8, but contents like the next messages? Interesting effect is that if I send ONLY the 8-byte message, without any others, it is received and passed to my app correctly.
Here is the code:
public class MainActivity extends AppCompatActivity
{
private TbProtocolProcessor tbProtPrcs = null;
private InetHandler inetHandler = new InetHandler(this);
private static Handler msgHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tbProtPrcs = new TbProtocolProcessor(this);
}
// Implementation of InetControl interface
public void ConnectToIP(String strIP, int port)
{
inetHandler.AttachToIP(strIP, port);
}
public void Disconnect()
{
inetHandler.DetachFromIP();
}
public void GetFilesList()
{
byte[] data = TbProtocolProcessor.buildFilesGetList();
inetHandler.SendData(data, data.length);
TbProtocolProcessor.setExpectedResult(
TbProtocolProcessor.TB_STATE_WAIT_MUL_FILESLIST,
data[1],
1);
}
private class InetHandler
{
protected static final int cTARGET_PORT_UNASSIGNED = 0xFFFF;
protected String targetIP = null;
protected int targetPort = cTARGET_PORT_UNASSIGNED;
protected boolean isConnected = false;
protected Socket socket = null;
protected DataOutputStream sockStrmOut = null;
protected DataInputStream sockStrmIn = null;
protected Context context = null;
public InetHandler(Context ctx) {
if (ctx != null)
{
context = ctx;
}
}
class ClientThread implements Runnable {
byte[] indata = new byte[100];
int inCntr;
#Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(targetIP);
socket = new Socket(serverAddr, targetPort);
socket.setKeepAlive(true);
// DataOutputStream is used to write primitive data types to stream
sockStrmOut = new DataOutputStream(socket.getOutputStream());
sockStrmIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
if (socket.isConnected()) {
isConnected = true;
//Toast.makeText(context, "CONNECTED", Toast.LENGTH_SHORT).show();
//findViewById(R.id.action_connect).setBackgroundColor(0xFF60FF60);
}
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
// TODO:
while (isConnected) {
try {
inCntr = sockStrmIn.read(indata);
}
catch (IOException e) {
e.printStackTrace();
}
if (inCntr > 0) {
msgHandler.post(new Runnable() {
#Override
public void run() {
if ( tbProtPrcs.Process(indata, inCntr) ) {
Toast.makeText(context, "Operation Success", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(context, "Operation ERROR", Toast.LENGTH_SHORT).show();
}
}
});
}
}
}
}
public void AttachToIP(String sIP, int iPort)
{
if ( (isIPValid(sIP)) && (iPort < cTARGET_PORT_UNASSIGNED) )
{
targetIP = sIP;
targetPort = iPort;
// Start the connection thread
new Thread(new ClientThread()).start();
}
}
public void DetachFromIP()
{
try {
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
public boolean SendData(byte[] data, int size)
{
boolean bResult = false;
try
{
if ( (data != null) && (size > 0) && (sockStrmOut != null) ) {
Toast.makeText(context, "Sending...", Toast.LENGTH_SHORT).show();
sockStrmOut.write(data, 0, size);
bResult = true;
}
} catch (Exception e) {
e.printStackTrace();
}
return bResult;
}
public boolean isIPValid (String ip) {
try {
if (ip == null || ip.isEmpty()) {
return false;
}
String[] parts = ip.split( "\\." );
if ( parts.length != 4 ) {
return false;
}
for ( String s : parts ) {
int i = Integer.parseInt( s );
if ( (i < 0) || (i > 255) ) {
return false;
}
}
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
}
}
You're assuming that read() fills the buffer. It isn't specified to do that. See the Javadoc. If you want to fill the buffer you must use readFully().
NB isConnected() cannot possibly be false at the point you're testing it.

How to read continuously connected bluetooth device?

I am trying to connect my App to the bluetooth device and after that i am doing the functionality Read and Write. I am able to do connectivity and Write command to device. But i am not able to do Read functionality.
The "Read" functionality is like, as soon as device socket will get connect to the App, it should send the device information continuously. But i my case, read() method is calling but every time i am getting the length on InputStream in 0.
Below is the code what i am writing. Please check it where i am going wrong and please help me.
public class ConnectionThread implements Runnable {
private static final String CLASSTAG = ConnectionThread.class.getSimpleName();
public static BluetoothSocket mSocket;
private InputStream inStream;
private OutputStream outStream;
private boolean canceled = false;
private Context mContext;
public ConnectionThread(Context context, BluetoothDevice device) {
this.mContext = context;
try {
mSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(Constants.mUUID));
} catch (IOException e) {
Log.e(CLASSTAG, "createRfcommSocketToServiceRecord", e);
}
}
#SuppressLint("NewApi")
#Override
public void run() {
// Cancel discovery because it will slow down the connection
if(BluetoothAdapter.getDefaultAdapter().isDiscovering()) {
BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
}
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mSocket.connect();
} catch (IOException e) {
Log.e(CLASSTAG, "connect failed", e);
// Unable to connect; close the socket and get out
disconnect();
return;
}
// Get the input and output streams, using temp objects because
// member streams are final
try {
inStream = mSocket.getInputStream();
outStream = mSocket.getOutputStream();
} catch (IOException e) {
Log.e(CLASSTAG, "IO streams init failed", e);
disconnect();
return;
}
while (!canceled) {
read();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Log.e(CLASSTAG, "sleep failed", e);
}
}
}
#SuppressLint("NewApi")
public void applyCommand(String configCommand) throws IOException {
byte[] bytes = null;
bytes = new BigInteger(configCommand,16).toByteArray();
if (mSocket.isConnected()) {
//write(bytes);
outStream.write(bytes);
Log.d(CLASSTAG, "Command apply: " + bytes + " (" + configCommand + ")");
}
Toast.makeText(mContext, mContext.getString(R.string.cmd_updated), Toast.LENGTH_LONG).show();
}
private void read() {
byte[] buffer = new byte[128];
// Keep listening to the InputStream while connected
try {
// Read from the InputStream
if (inStream.available() > 0) {
int bytes = inStream.read(buffer);
if (bytes > 0) {
Log.d(CLASSTAG, "Response: " + new String(buffer, 0, bytes));
}
}
// Send the obtained bytes to the UI Activity
// mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
Log.e(CLASSTAG, "reading from input stream failed", e);
disconnect();
}
}
public void disconnect() {
try {
if (outStream != null) {
outStream.close();
}
if (inStream != null) {
inStream.close();
}
mSocket.close();
canceled = true;
} catch (IOException e) {
Log.e(CLASSTAG, "close socket", e);
}
}
}

Why on double click android client received data?

When I click the first time on login button, data send to server and server received data in return on first click data not show on android client screen. When I pressed login button again it again send data and then it show data on client screen... plz help me. Why data is received on secind click i want my data recived on my first click?
Here is the code :
Client tcpip code...
public class SockProg {
private Socket socket;
DataOutputStream dataOutputStream;
DataInputStream dataInputStream;
String data;
String serverip = "192.168.1.7";
int serverport = 4444;
public void connetToServer(){
try {
socket = new Socket(serverip, serverport);
Log.i("AsyncTank", "doInBackgoung: Created Socket");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (socket.isConnected()) {
try {
dataOutputStream = new DataOutputStream(
socket.getOutputStream());
dataInputStream = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void writeToStream(String message) {
try {
if (socket.isConnected()){
dataOutputStream.writeUTF(message.toString());
} else {
Log.i("AsynkTask", "writeToStream : Cannot write to stream, Socket is closed");
}
} catch (Exception e) {
Log.i("AsynkTask", "writeToStream : Writing failed");
}
}
public String readFromStream() {
String ret = null;
try {
if (socket.isConnected()) {
Log.i("AsynkTask", "readFromStream : Reading message");
ret=dataInputStream.readUTF();
Log.i("AsynkTask", "readFromStream : read "+ret);
} else {
Log.i("AsynkTask", "readFromStream : Cannot Read, Socket is closed");
}
} catch (Exception e) {
Log.i("AsynkTask", "readFromStream : Reading failed"+e.getClass());
}
return ret;
}
public void CloseSockets(){
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (dataOutputStream != null) {
try {
dataOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (dataInputStream != null) {
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
here is the code of sychronized thread
public class TCP implements Runnable {
String data;
SockProg sp;
Thread thh;
private static String rdata;
public TCP(SockProg spr, String val) {
sp = spr;
data = val;
thh = new Thread(this);
thh.start();
}
#Override
public void run() {
synchronized(sp) { // synchronized block
//rdata= sp.DataSendRecive(data);
sp.connetToServer();
sp.writeToStream(data);
rdata=sp.readFromStream();
sp.CloseSockets();
}
}
public static String getData(){
return rdata;
}
}
here is code of Login Activity...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
msg = (TextView) findViewById(R.id.msg_log);
login = (Button) findViewById(R.id.btn_login);
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// try{
txtph = (EditText) findViewById(R.id.txt_phnum);
txtpass = (EditText) findViewById(R.id.txt_pass);
ph = txtph.getText().toString();
pass = txtpass.getText().toString();
int ch = 0;
if (ph.equals("") || ph == null) {
msg.setText("Please Enter Mobile Number....\n");
ch++;
}
if (pass.equals("") || pass == null) {
if (ch == 0) {
msg.setText("Please Enter your Password....\n");
} else {
msg.append("Please Enter your Password....\n");
}
ch++;
}
if (ch == 0) {
ArrayList<String> ph_pass = new ArrayList<String>();
ph_pass.add(0, "LoginAccount");
ph_pass.add(1, ph);
ph_pass.add(2, pass);
SockProg sp=new SockProg();
TCP t=new TCP(sp, ph_pass.toString());
data=t.getData();
msg.setText(data);
}
}
});
}
This looks like a classic case of asynchronous coding delay. The TCP class is a runnable and therefor when it is called the first time (the first click on the login button) it starts running, but the Thread does not have enough time to finish
rdata=sp.readFromStream();
in the run() method, therefor data=t.getData(); does not return anything useful. The second click, provides the runnable with enough time populate the rdata with some data and therefor your program works.
When working with asynchrounous code, you need a better way to wait for code to complete what it is doing.
Why is rdata a static type? Make it non-static and then change the getData() method like this:
public synchronized String getData()

ObjectOutputStream method writeObject hangs on android

I write some client-server communication.
My server:
public class Server {
public synchronized static void sendPacket(Packet packet,
ObjectOutputStream server) {
try {
server.writeObject(packet);
server.flush();
} catch (IOException e) {
Log.d(TAG, "Error while sending a packet. Output stream is unaviable.");
}
}
public synchronized static Packet readPacket(ObjectInputStream sourceStream) {
Packet recivedPacket = null;
try {
recivedPacket = (Packet) sourceStream.readObject();
} catch (StreamCorruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return recivedPacket;
}
/** Register user on the server */
private User registerUser(Socket socket) {
ClientUserLoginPacket newUserPacket = null;
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
try {
Log.i(TAG, "Opening output stream...");
oos = new ObjectOutputStream(socket.getOutputStream());
if (oos != null)
Log.d(TAG, "Output stream opened");
Log.i(TAG, "Opening input stream...");
ois = new ObjectInputStream(socket.getInputStream());
if (ois != null)
Log.d(TAG, "Input stream opened");
} catch (StreamCorruptedException e1) {
Log.e(TAG, "Error while opening stream");
} catch (IOException e1) {
e1.printStackTrace();
}
// First packet MUST be register request
try {
Log.d(TAG, "Waiting for login packet from client...");
newUserPacket = (ClientUserLoginPacket) readPacket(ois);
Log.d(TAG, "Login packet from recived...");
} catch (Exception e) {
Log.e(TAG, "Can't recive login packet.");
}
User newUserInstance = null;
// TODO check if exists. or to map in the future
if (newUserPacket != null) {
newUserInstance = new User(socket, ois, oos, newUserPacket.nick);
users.add(newUserInstance);
Log.d(TAG, "User " + newUserPacket.nick + " registered.");
Server.sendPacket(new ServerLoginAcceptedPacket(), oos);
Log.d(TAG, "User accept confirmation sent.");
}
return newUserInstance;
}
#Override
public void run() {
Log.i(TAG, "Starting server...");
ServerSocket server;
try {
server = new ServerSocket(PORT);
Log.i(TAG, "Server started.");
server.setSoTimeout(0);
while (true) {
Log.i(TAG, "Waiting for players...");
final Socket socket = server.accept();
Log.i(TAG, "New player connected.");
new Thread(new Runnable() {
#Override
public void run() {
Log.i(TAG, "Try to register new player.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
User user = registerUser(socket);
while (true) {
Log.i(TAG, "Waiting for packets from " + user.nick+"...");
Packet packet = readPacket(user.ois);
Log.i(TAG, "Packet from " + user.nick + " recived.");
if (packet instanceof ...) {
...
}
}
}
}).start();
}
} catch (IOException e) {
Log.i(TAG, "Port is busy.");
}
}
private class User {
public Socket connection;
public ObjectInputStream ois;
public ObjectOutputStream oos;
public String nick;
public boolean inGame;
public User(Socket socket, ObjectInputStream ois,
ObjectOutputStream oos, String nick) {
this.connection = socket;
this.ois = ois;
this.oos = oos;
this.nick = nick;
}
// ...
}
My client:
public class Client {
callbackHandler = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Log.e(TAG, "Waiting for incomeing packets...");
Packet packet = (Packet) Server.readPacket(serverInput);
Log.e(TAG, "Packet recived.");
if (packet instanceof ServerLoginAcceptedPacket) {
Log.e(TAG, "Recived packet is "
+ packet.getClass().toString());
Intent intent = new Intent(MyActivity.this,
MainMenuActivity.class);
MyActivity.this.startActivity(intent);
}
}
}
});
public void connectToServer() {
SocketAddress sockaddr = new InetSocketAddress(mEditTextIp.getText()
.toString(), Server.PORT);
server = new Socket();
try {
server.setSoTimeout(1000);
Log.d(TAG, "Connecting to server.");
server.connect(sockaddr, Server.PORT);
Log.d(TAG, "Connected to server.");
} catch (IOException e) {
Log.e(TAG, "Can't connect to server.");
server = null;
}
if (server != null)
try {
server.setSoTimeout(0);
Log.d(TAG, "Opening output stream...");
serverOutput = new ObjectOutputStream(server.getOutputStream());
if (serverOutput != null)
Log.d(TAG, "Output stream opened");
else
Log.e(TAG, "Error while opening output stream");
} catch (IOException e) {
Log.e(TAG, "Server socket probably closed");
}
}
public void requestLogin() {
new Thread(new Runnable() {
#Override
public void run() {
Log.e(TAG, "Sending login packet...");
Server.sendPacket(new ClientUserLoginPacket(mEditTextLogin
.getText().toString(), ""), serverOutput); // TODO send
// pass and
// email
Log.e(TAG, "Login packet send");
}
}).start();
}
public void authenticate(View v) {
if (server == null)
connectToServer();
if (server != null) {
requestLogin();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
serverInput = new ObjectInputStream(server.getInputStream());
} catch (StreamCorruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (serverInput != null) {
Log.e(TAG, "Start reciving callbacks...");
callbackHandler.start();
} else {
Log.d(TAG, "Can't open input stream to server.");
}
}
}
public void runServer(View v) {
new Thread(new Server()).start();
Toast.makeText(this, "Server running...", 1000).show();
}
}
Where runServer() and authenticate() functions are triggered with button.
Problem is that after server recive ClientLoginPacket, all subsequent sentPacket functions hangs on oos.writeObject().
I think the order of reading/writing from/to streams may be wrong.
What should be correct order of opening streams and writing objects to them?
Do I have to write something to ObjectOutputStream before opening ObjectInputStream?
After few hours I found that keywords synchronized before my methods readPacket() and sendPacket() were problem. ;)

Java ServerSocket and Android LocalServerSocket

I have implemented my own android service as follows
public class MyService extends Service {
private static final String TAG = "MyService";
private Server mServer;
private LocalServerSocket server;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Log.d(TAG, "onCreate");
mServer = new Server();
mServer.start();
}
#Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
if(server != null){
try {
server.close();
} catch (IOException e) {
Log.d(TAG, "exception in server close");
e.printStackTrace();
}
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStart");
return START_STICKY;
}
class Server extends Thread {
#Override
public void run() {
try {
server = new LocalServerSocket("my.socket");
while (true) {
LocalSocket receiver;
try{
receiver = server.accept();
}catch(SocketException e){
Log.d(TAG, "SocketException");
break;
}
catch(IOException e){
Log.d(TAG, "IOException");
break;
}
if (receiver != null) {
Log.d(TAG, "Got Data in receiver");
}
receiver.close();
}
} catch (IOException e) {
Log.d(TAG, "one more");
e.printStackTrace();
}
}
}
}
The problem I am facing is that, if my LocalServerSocket is blocking in accept(), then a call to server.close() in OnDestroy() will not throw a SocketException. Hence, next time I start the service, I get "address already in use exception". If instead of LocalServerSocket, I use java.net.Socket, then i get the required behavior. I would like to know why LocalServerSocket behaves differently from Java Sockets. In my case, how do I come out of the while loop.
I had the same problem and "solved" it this way. The thread run() method is checking for "!isInterrupted()". The method "stopSocketServer()" which I added to my Listener-Thread marks the thread for interrupt() and then made a connect request to itself to trigger the accept() method.
/**
* Executed if thread is started.
*/
public void run() {
try {
// leave while loop if thread is marked for interrupt.
while (!isInterrupted()) {
LocalSocket clientSocket = serverSocket.accept();
if (!isInterrupted()) {
threadPool.execute(new ClientProcessor(clientSocket));
}
}
} catch (IOException e) {
if (!isInterrupted()) {
Log.e(TAG, "socket listener terminated", e);
}
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
if (threadPool != null) {
threadPool.shutdownNow();
}
Log.i(TAG, "socket listener stopped");
} catch (IOException e) {
}
}
}
public void stopSocketServer() {
if (serverSocket != null) {
try {
// mark thread as interrupted
interrupt();
// now send connect request to myself to trigger leaving accept()
LocalSocket ls = new LocalSocket();
ls.connect(serverSocket.getLocalSocketAddress());
ls.close();
} catch (IOException e) {
Log.e(TAG, "stopSocketServer failed", e);
}
}
}
From the code you've posted it looks like your LocalServerSocket server will remain null forever and onDestroy() won't close it. Also, closing the socket normally shouldn't throw IOException - but the accept() will do so if the socket is closed concurrently.

Categories