I getting a message from Message class and putting it in a string ss, Now everytime i get the string HOME i'm sending a message dir, but it never sends the message
I'm not sure why but the first condition is never satisfied even though ss is HOME which is proof because tv.settext becomes HOME (I've tested this multiple times)
TextView tv = (TextView) findViewById(R.id.readField);
ss=(String) message.obj;
if(ss.equals("HOME")) {
Message msg = Message.obtain();
msg.obj = dir;
writeHandler.sendMessage(msg);
}
else {
tv.setText(ss);
}
Reading part of my code to understand the above code
/**
* Return data read from the socket, or a blank string.
*/
private String read() {
String s = "";
try {
// Check if there are bytes available
if (inStream.available() > 0) {
// Read bytes into a buffer
byte[] inBuffer = new byte[1024];
int bytesRead = inStream.read(inBuffer);
// Convert read bytes into a string
s = new String(inBuffer, "ASCII");
s = s.substring(0, bytesRead);
}
} catch (Exception e) {
Log.e(TAG, "Read failed!", e);
}
return s;
}
//Under Message class
//Message coding
private void sendToReadHandler(String s) {
Message msg = Message.obtain();
msg.obj = s;
readHandler.sendMessage(msg);
Log.i(TAG, s);
}
/**
* Send complete messages from the rx_buffer to the read handler.
*/
private void parseMessages() {
// Find the first delimiter in the buffer
int inx = rx_buffer.indexOf(DELIMITER);
// If there is none, exit
if (inx == -1)
return;
// Get the complete message
String s = rx_buffer.substring(0, inx);
// Remove the message from the buffer
rx_buffer = rx_buffer.substring(inx + 1);
// Send to read handler
sendToReadHandler(s);
// Look for more complete messages
parseMessages();
}
public void run() {
// Attempt to connect and exit the thread if it failed
try {
connect();
sendToReadHandler("CONNECTED");
} catch (Exception e) {
Log.e(TAG, "Failed to connect!", e);
sendToReadHandler("CONNECTION FAILED");
disconnect();
return;
}
// Loop continuously, reading data, until thread.interrupt() is called
while (!this.isInterrupted()) {
// Make sure things haven't gone wrong
if ((inStream == null) || (outStream == null)) {
Log.e(TAG, "Lost bluetooth connection!");
break;
}
// Read data and add it to the buffer
String s = read();
if (s.length() > 0)
rx_buffer += s;
// Look for complete messages
parseMessages();
}
// If thread is interrupted, close connections
disconnect();
sendToReadHandler("DISCONNECTED");
}
}
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.
I am trying to write a messaging application in java, but keep getting an error. When I run the client on server on the same device, I get the same message. However, when I run the client and server on the same device, the client can receive messages from the server, but is unable to send a message. If I do try to send a message from the separate machine, I get the following errors:
Server has close the connection: java.io.EOFException
java.lang.ClassNotFoundException: ChatMessage
I do not know what this error means, and do not know how to fix it. I would very much appreciate some help and precise instructions on how to fix this error, and prevent it in the future.
Server Class:
public class Server {
// a unique ID for each connection
private static int uniqueId;
// an ArrayList to keep the list of the Client
private ArrayList<ClientThread> al;
// if I am in a GUI
private ServerGUI sg;
// to display time
private SimpleDateFormat sdf;
// the port number to listen for connection
private int port;
// the boolean that will be turned of to stop the server
private boolean keepGoing;
private Socket socket;
/*
* server constructor that receive the port to listen to for connection as parameter
* in console
*/
public Server(int port) {
this(port, null);
}
public Server(int port, ServerGUI sg) {
// GUI or not
this.sg = sg;
// the port
this.port = port;
// to display hh:mm:ss
sdf = new SimpleDateFormat("HH:mm:ss");
// ArrayList for the Client list
al = new ArrayList<ClientThread>();
}
public void start() {
keepGoing = true;
/* create socket server and wait for connection requests */
try
{
// the socket used by the server
ServerSocket serverSocket = new ServerSocket(port);
// infinite loop to wait for connections
while(keepGoing)
{
// format message saying we are waiting
display("Server waiting for Clients on port " + port + ".");
Socket socket = serverSocket.accept(); // accept connection
// if I was asked to stop
if(!keepGoing)
break;
ClientThread t = new ClientThread(socket); // make a thread of it
al.add(t); // save it in the ArrayList
t.start();
}
// I was asked to stop
try {
serverSocket.close();
for(int i = 0; i < al.size(); ++i) {
ClientThread tc = al.get(i);
try {
tc.sInput.close();
tc.sOutput.close();
tc.socket.close();
}
catch(IOException ioE) {
// not much I can do
}
}
}
catch(Exception e) {
display("Exception closing the server and clients: " + e);
}
}
// something went bad
catch (IOException e) {
String msg = sdf.format(new Date()) + " Exception on new ServerSocket: " + e + "\n";
display(msg);
}
}
/*
* For the GUI to stop the server
*/
protected void stop() {
keepGoing = false;
// connect to myself as Client to exit statement
// Socket socket = serverSocket.accept();
try {
new Socket("localhost", port);
}
catch(Exception e) {
// nothing I can really do
}
}
/*
* Display an event (not a message) to the console or the GUI
*/
private void display(String msg) {
String time = sdf.format(new Date()) + " " + msg;
if(sg == null)
System.out.println(time);
else
sg.appendEvent(time + "\n");
}
/*
* to broadcast a message to all Clients
*/
private synchronized void broadcast(String message) {
// add HH:mm:ss and \n to the message
String time = sdf.format(new Date());
String messageLf = time + " " + message + "\n";
// display message on console or GUI
if(sg == null)
System.out.print(messageLf);
else
sg.appendRoom(messageLf); // append in the room window
// we loop in reverse order in case we would have to remove a Client
// because it has disconnected
for(int i = al.size(); --i >= 0;) {
ClientThread ct = al.get(i);
// try to write to the Client if it fails remove it from the list
if(!ct.writeMsg(messageLf)) {
al.remove(i);
display("Disconnected Client " + ct.username + " removed from list.");
}
}
}
// for a client who logoff using the LOGOUT message
synchronized void remove(int id) {
// scan the array list until we found the Id
for(int i = 0; i < al.size(); ++i) {
ClientThread ct = al.get(i);
// found it
if(ct.id == id) {
al.remove(i);
return;
}
}
}
/*
* To run as a console application just open a console window and:
* > java Server
* > java Server portNumber
* If the port number is not specified 1500 is used
*/
public static void main(String[] args) {
// start server on port 1500 unless a PortNumber is specified
int portNumber = 1500;
switch(args.length) {
case 1:
try {
portNumber = Integer.parseInt(args[0]);
}
catch(Exception e) {
System.out.println("Invalid port number.");
System.out.println("Usage is: > java Server [portNumber]");
return;
}
case 0:
break;
default:
System.out.println("Usage is: > java Server [portNumber]");
return;
}
// create a server object and start it
Server server = new Server(portNumber);
server.start();
}
/** One instance of this thread will run for each client */
class ClientThread extends Thread {
// the socket where to listen/talk
Socket socket;
ObjectInputStream sInput;
ObjectOutputStream sOutput;
// my unique id (easier for deconnection)
int id;
// the Username of the Client
String username;
// the only type of message a will receive
ChatMessage cm;
// the date I connect
String date;
// Constructore
ClientThread(Socket socket) {
// a unique id
id = ++uniqueId;
this.socket = socket;
/* Creating both Data Stream */
System.out.println("Thread trying to create Object Input/Output Streams");
try
{
// create output first
sOutput = new ObjectOutputStream(socket.getOutputStream());
sInput = new ObjectInputStream(socket.getInputStream());
// read the username
username = (String) sInput.readObject();
display(username + " just connected.");
}
catch (IOException e) {
display("Exception creating new Input/output Streams: " + e);
return;
}
// have to catch ClassNotFoundException
// but I read a String, I am sure it will work
catch (ClassNotFoundException e) {
}
date = new Date().toString() + "\n";
}
// what will run forever
public void run() {
// to loop until LOGOUT
boolean keepGoing = true;
while(keepGoing) {
// read a String (which is an object)
try {
cm = (ChatMessage) sInput.readObject();
}
catch (IOException e) {
display(username + " Exception reading Streams: " + e);
break;
}
catch(ClassNotFoundException e2) {
display(e2.toString());
break;
}
// the messaage part of the ChatMessage
String message = cm.getMessage();
// Switch on the type of message receive
switch(cm.getType()) {
case ChatMessage.MESSAGE:
broadcast(username + ": " + message);
break;
case ChatMessage.LOGOUT:
display(username + " disconnected with a LOGOUT message.");
keepGoing = false;
break;
case ChatMessage.WHOISIN:
writeMsg("List of the users connected at " + sdf.format(new Date()) + "\n");
// scan al the users connected
for(int i = 0; i < al.size(); ++i) {
ClientThread ct = al.get(i);
writeMsg((i+1) + ") " + ct.username + " since " + ct.date);
}
break;
}
}
// remove myself from the arrayList containing the list of the
// connected Clients
remove(id);
close();
}
// try to close everything
private void close() {
// try to close the connection
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {}
try {
if(sInput != null) sInput.close();
}
catch(Exception e) {};
try {
if(socket != null) socket.close();
}
catch (Exception e) {}
}
/*
* Write a String to the Client output stream
*/
private boolean writeMsg(String msg) {
// if Client is still connected send the message to it
if(!socket.isConnected()) {
close();
return false;
}
// write the message to the stream
try {
sOutput.writeObject(msg);
}
// if an error occurs, do not abort just inform the user
catch(IOException e) {
display("Error sending message to " + username);
display(e.toString());
}
return true;
}
}
}
Client Class:
public class Client {
// for I/O
private ObjectInputStream sInput; // to read from the socket
private ObjectOutputStream sOutput; // to write on the socket
private Socket socket;
// if I use a GUI or not
private ClientGUI cg;
// the server, the port and the username
private String server, username;
private int port;
/*
* Constructor called by console mode
* server: the server address
* port: the port number
* username: the username
*/
Client(String server, int port, String username) {
// which calls the common constructor with the GUI set to null
this(server, port, username, null);
}
/*
* Constructor call when used from a GUI
* in console mode the ClienGUI parameter is null
*/
Client(String server, int port, String username, ClientGUI cg) {
this.server = server;
this.port = port;
this.username = username;
// save if we are in GUI mode or not
this.cg = cg;
}
/*
* To start the dialog
*/
public boolean start() {
// try to connect to the server
try {
socket = new Socket(server, port);
}
// if it failed not much I can so
catch(Exception ec) {
display("Error connectiong to server:" + ec);
return false;
}
String msg = "Connection accepted " + socket.getInetAddress() + ":" + socket.getPort();
display(msg);
/* Creating both Data Stream */
try
{
sInput = new ObjectInputStream(socket.getInputStream());
sOutput = new ObjectOutputStream(socket.getOutputStream());
}
catch (IOException eIO) {
display("Exception creating new Input/output Streams: " + eIO);
return false;
}
// creates the Thread to listen from the server
new ListenFromServer().start();
// Send our username to the server this is the only message that we
// will send as a String. All other messages will be ChatMessage objects
try
{
sOutput.writeObject(username);
}
catch (IOException eIO) {
display("Exception doing login : " + eIO);
disconnect();
return false;
}
// success we inform the caller that it worked
return true;
}
/*
* To send a message to the console or the GUI
*/
private void display(String msg) {
if(cg == null)
System.out.println(msg); // println in console mode
else
cg.append(msg + "\n"); // append to the ClientGUI JTextArea (or whatever)
}
/*
* To send a message to the server
*/
void sendMessage(ChatMessage msg) {
try {
sOutput.writeObject(msg);
}
catch(IOException e) {
display("Exception writing to server: " + e);
}
}
/*
* When something goes wrong
* Close the Input/Output streams and disconnect not much to do in the catch clause
*/
private void disconnect() {
try {
if(sInput != null) sInput.close();
}
catch(Exception e) {} // not much else I can do
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {} // not much else I can do
try{
if(socket != null) socket.close();
}
catch(Exception e) {} // not much else I can do
// inform the GUI
if(cg != null)
cg.connectionFailed();
}
/*
* To start the Client in console mode use one of the following command
* > java Client
* > java Client username
* > java Client username portNumber
* > java Client username portNumber serverAddress
* at the console prompt
* If the portNumber is not specified 1500 is used
* If the serverAddress is not specified "localHost" is used
* If the username is not specified "Anonymous" is used
* > java Client
* is equivalent to
* > java Client Anonymous 1500 localhost
* are eqquivalent
*
* In console mode, if an error occurs the program simply stops
* when a GUI id used, the GUI is informed of the disconnection
*/
public static void main(String[] args) {
// default values
int portNumber = 1500;
String serverAddress = "localHost";
String userName = "Anonymous";
// depending of the number of arguments provided we fall through
switch(args.length) {
// > javac Client username portNumber serverAddr
case 3:
serverAddress = args[2];
// > javac Client username portNumber
case 2:
try {
portNumber = Integer.parseInt(args[1]);
}
catch(Exception e) {
System.out.println("Invalid port number.");
System.out.println("Usage is: > java Client [username] [portNumber] [serverAddress]");
return;
}
// > javac Client username
case 1:
userName = args[0];
// > java Client
case 0:
break;
// invalid number of arguments
default:
System.out.println("Usage is: > java Client [username] [portNumber] {serverAddress]");
return;
}
// create the Client object
Client client = new Client(serverAddress, portNumber, userName);
// test if we can start the connection to the Server
// if it failed nothing we can do
if(!client.start())
return;
// wait for messages from user
Scanner scan = new Scanner(System.in);
// loop forever for message from the user
while(true) {
System.out.print("> ");
// read message from user
String msg = scan.nextLine();
// logout if message is LOGOUT
if(msg.equalsIgnoreCase("LOGOUT")) {
client.sendMessage(new ChatMessage(ChatMessage.LOGOUT, ""));
// break to do the disconnect
break;
}
// message WhoIsIn
else if(msg.equalsIgnoreCase("WHOISIN")) {
client.sendMessage(new ChatMessage(ChatMessage.WHOISIN, ""));
}
else { // default to ordinary message
client.sendMessage(new ChatMessage(ChatMessage.MESSAGE, msg));
}
}
// done disconnect
client.disconnect();
}
/*
* a class that waits for the message from the server and append them to the JTextArea
* if we have a GUI or simply System.out.println() it in console mode
*/
class ListenFromServer extends Thread {
public void run() {
while(true) {
try {
String msg = (String) sInput.readObject();
// if console mode print the message and add back the prompt
if(cg == null) {
System.out.println(msg);
System.out.print("> ");
}
else {
cg.append(msg);
}
}
catch(IOException e) {
display("Server has close the connection: " + e);
if(cg != null)
cg.connectionFailed();
break;
}
// can't happen with a String object but need the catch anyhow
catch(ClassNotFoundException e2) {
}
}
}
}
}
ChatMessage Class:
public class ChatMessage implements Serializable {
protected static final long serialVersionUID = 1112122200L;
// The different types of message sent by the Client
// WHOISIN to receive the list of the users connected
// MESSAGE an ordinary message
// LOGOUT to disconnect from the Server
static final int WHOISIN = 0, MESSAGE = 1, LOGOUT = 2;
private int type;
private String message;
// constructor
ChatMessage(int type, String message) {
this.type = type;
this.message = message;
}
// getters
int getType() {
return type;
}
String getMessage() {
return message;
}
}
Note that I am able to send messages just fine if I load both the server and client on the same machine. If I load the server on one machine, and the client on another, however, the client can receive messages from the server, but the server cannot receive messages from the client. If I attempt to send a message from the client to the Server, it gives me the errors shown above.
I'm trying to perform a programmatic telnet session in Java. I'm using commons-net TelnetClient, but I've also experimented with a direct socket. In either case I'm having the same problem.
I read up to "login :", then send the user name followed by CRLF. Then nothing, no other data is read, or written by server.
The telnet server is on an embedded device (a Star printer), so I'm wondering if there are some peculiar options required that I'm not setting, or that aren't supported by the commons-net TelnetClient class.
I can use Linux telnet without problems, and I can run my code against the telnet server in OSX and it works fine.
TelnetClient client = new TelnetClient();
client.registerNotifHandler(new TelnetNotificationHandler() {
#Override
public void receivedNegotiation(int negotiation_code, int option_code) {
ALog.i(this, "negotiation code: %d, option code: %d", negotiation_code, option_code);
}
});
try {
client.addOptionHandler(new TerminalTypeOptionHandler("VT100", false, false, true, false));
client.addOptionHandler(new SuppressGAOptionHandler(true, false, true, false));
client.addOptionHandler(new EchoOptionHandler(true, true, true, true));
} catch (InvalidTelnetOptionException e) {
e.printStackTrace();
}
try {
FileOutputStream fos = new FileOutputStream("/sdcard/spy.out");
client.registerSpyStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
InputStream in = null;
PrintWriter out = null;
String ip = getIpAddress(p);
ALog.i(this, "connecting to: %s", ip);
try {
client.connect(ip);
in = client.getInputStream();
out = new PrintWriter(client.getOutputStream(), true);
if (!expect(in, "login: ", 5000)) {
return;
}
if (!send(out, "root")) {
return;
}
if (!expect(in, "password: ", 5000)) {
return;
}
if (!send(out, "password")) {
return;
}
Here's the expect() and send() methods,
protected boolean expect(InputStream in, String s, long timeout) {
ALog.i(this, "expecting: %s", s);
final AtomicBoolean lock = new AtomicBoolean(false);
final ExpectThread t = new ExpectThread(in, s, lock, timeout);
t.start();
synchronized (lock) {
try {
lock.wait(timeout);
} catch (InterruptedException e) {
}
}
t.interrupt();
return lock.get();
}
protected boolean send(PrintWriter out, String s) {
out.println(s);
out.flush();
ALog.i(this, "sent: %s", s);
return true;
}
And here's ExpectThread,
private class ExpectThread extends Thread {
private final InputStream in;
private final String expected;
private final AtomicBoolean lock;
private final long start;
private final long timeout;
ExpectThread(InputStream in, String expected, AtomicBoolean lock, long timeout) {
this.in = in;
this.expected = expected.toLowerCase();
this.lock = lock;
this.timeout = timeout;
this.start = System.currentTimeMillis();
}
#Override
public void run() {
final StringBuilder b = new StringBuilder();
final byte[] buffer = new byte[1024];
int c;
try {
while (!isInterrupted() && System.currentTimeMillis() < start + timeout) {
ALog.i(this, "starting read ...");
while ((c = in.read(buffer)) != -1) {
String s = new String(buffer, 0, c);
b.append(s.toLowerCase());
ALog.i(this, "read string: %s, buffer: %s", s, b.toString());
if (b.toString().contains(expected)) {
ALog.i(this, "found expected");
lock.set(true);
return;
}
}
ALog.i(this, "waiting for read ...");
SystemClock.sleep(1000);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
synchronized (lock) {
lock.notifyAll();
}
}
}
}
here's a wireshark pcap of the FAILED programmatic session,
https://drive.google.com/file/d/0B5iST80rpTN9c1RsRTNFaE5GZHM/view?usp=sharing
here's a pcap of a successful terminal (linux telnet client) session,
https://drive.google.com/file/d/0B5iST80rpTN9bDZFOHhkSHlPSE0/view?usp=sharing
I see the Linux client sends a "WILL AUTHENTICATE", where my code does not. I'd try it if I could figure out how to get TelnetClient to send such commands.
You must send the \r\n explicitly, not via println(). It must be exactly that, not whatever println() does on your system.
I also suspect your expect() method may be reading ahead. It should read a byte or a char at a time to ensure this can't happen. And please try it with a socket read timeout instead of that Megillah of threads and locks and waits:
protected boolean expect(Socket socket, InputStream in, String expected, long timeout) throws IOException {
ALog.i(this, "expecting: %s", s);
long endTime = System.currentTimeMillis() + timeout;
byte[] buffer = new byte[8192];
StringBuffer b = new StringBuffer();
try
{
while (System.currentTimeMillis() < endTime)
{
socket.setSoTimeout((int)(endTime - System.currentTimeMillis()));
ALog.i(this, "starting read ...");
int c = in.read();
if (c == -1)
{
return false;
}
ALog.i(this, "read string: %s, buffer: %s", s, b.toString());
b.append(Character.valueOf((char)c));
if (b.toString().toLowerCase().contains(expected))
{
ALog.i(this, "found expected");
return true;
}
}
return false;
}
catch (SocketTimeoutException exc)
{
return false;
}
}
E&OE
As I suspected, it was an issue with the telnet negotiation with this particular server. I had to use a very specific echo option handler,
client.addOptionHandler(new EchoOptionHandler(false, false, false, true));
This means accept the DO ECHO that the server was sending. I discovered this discrepancy by comparing the telnet DO / WILL negotiation between the non-working and working sessions.
We have a java socket program where the server gets data from many devices and works fine. At times the server needs to send some command to the devices. When it sends individual commands it works fine. The problem comes when it sends multiple commands, only the first one is successful. We cant figure out why the rest fails. Below is the snippet showing how the message is sent. Should I set a delay after a message is sent?
public static void main(String[] args) {
new sServer7888();
}
sServer7888() {
try{
final ServerSocket serverSocketConn = new ServerSocket(7888);
while (true){
try{
Socket socketConn1 = serverSocketConn.accept();
new Thread(new ConnectionHandler(socketConn1)).start();
}
catch(Exception e){
e.printStackTrace(System.out);
}
}
}
catch (Exception e) {
e.printStackTrace(System.out);
}
}
class ConnectionHandler implements Runnable {
private Socket receivedSocketConn1;
ConnectionHandler(Socket receivedSocketConn1) {
this.receivedSocketConn1=receivedSocketConn1;
}
public void run() {
while ((nextChar=readIn1.read()) != -1) {
completeMessage += (char) nextChar;
if (nextChar == '*')
{
String[] splitResult = completeMessage .split(",");
String header=splitResult[0].trim().substring(0,4);
if((header.equals("$ACK")){
//update the message sent from the server as already acknowledge.
}
else{
//run query to find if there are any message to be sent out to the devices
while(rsOC1.next()){
commandText = rsOC1.getString("commandText");
writeOut1.write(commandText);
writeOut1.write("\r\n");
writeOut1.flush();
}
//now process the normal message receive from the devices.
}
completeMessage="";
}
}
}
}
If your device is sending ACK on Every message and Server is able to receive it then you can proceed in following way with your server side program.
EDIT
I have updated the code as per the requirement analysis. Let me know if any discrepancy is found after implementing it.
Thread.sleep(1000) is not the reliable solution for above case because
we are not knowing how long the device might take to execute previous
command sent by Server .
public void run()
{
int i = -1;
ArrayList<String> list = new ArrayList<String>();
while ((nextChar=readIn1.read()) != -1)
{
boolean isCompleteMessage = readMessage(nextChar);
if (isCompleteMessage)
{
String[] splitResult = completeMessage .split(",");
String header=splitResult[0].trim().substring(0,4);
if((header.equals("$ACK"))
{
String id = null;
if (i != -1)
{
id = list.get(i);
id = id.substring(0,id.indexOf("^"));
}
//update the message sent from the server as already acknowledge using id extracted above.
if ( i == 0)
{
list.remove(i);
if (list.size() == 0)
{
i = -1;
}
else
{
commandText = list.get(i);
writeOut1.write(commandText.substring((commandText.indexOf("^")) + 1));
writeOut1.write("\r\n");
writeOut1.flush();
}
}
}
else
{
//process here the normal message receive from the devices.
if (i == -1)
{
list = getRecords();
if (list.size() > 0)
{
i = 0;
commandText = list.get(i);
writeOut1.write(commandText.substring((commandText.indexOf("^")) + 1));
writeOut1.write("\r\n");
writeOut1.flush();
}
}
else
{
commandText = list.get(i);
writeOut1.write(commandText.substring((commandText.indexOf("^")) + 1));
writeOut1.write("\r\n");
writeOut1.flush();
}
}
completeMessage = "";
}
}
}
public boolean readMessage(int nextChar)
{
completeMessage += (char)nextChar;
if (((char)nextChar) == '*')
{
return true;
}
else
{
return false;
}
}
//Retreive all commands from database and returns the ArrayList containing those commands.
public ArrayList<String> getRecords()
{
ArrayList<String> list = new ArrayList<String>();
Statement stat = null;
ResultSet rsOC1 = null;
try
{
stat = con.createStatement();
rsOC1 = stat.executeQuery("Query for message retrieval from database");
while (rsOC1.next())
{
String sElement = rs0C1.getString("commandID") + "^" + rs0C1.getString("commandText");
list.add(sElement);
}
}
catch (Exception ex){}
finally
{
if (rs0C1 != null)
{
try
{
rs0C1.close();
} catch () {}
}
if (stat != null)
{
try
{
stat.close();
} catch () {}
}
return list;
}
}
I have the following code, i want to be able to restart the thread if an exception occurred while processing a request.
The following in the run method of a thread:
int status = httpConn.getResponseCode();
if (status == HttpConnection.HTTP_OK) {
// Is this html?
String contentType = httpConn
.getHeaderField(HEADER_CONTENTTYPE);
boolean htmlContent = (contentType != null && contentType
.startsWith(CONTENTTYPE_TEXTHTML));
InputStream input = s.openInputStream();
byte[] data = new byte[1000];
int len = 0;
int size = 0;
StringBuffer raw = new StringBuffer();
while (-1 != (len = input.read(data))) {
// Exit condition for the thread. An
// IOException
// is
// thrown because of the call to
// httpConn.close(),
// causing the thread to terminate.
if (_stop) {
httpConn.close();
s.close();
input.close();
}
raw.append(new String(data, 0, len));
size += len;
}
// raw.insert(0, "bytes received]\n");
// raw.insert(0, size);
// raw.insert(0, '[');
content = raw.toString();
if (htmlContent) {
content = prepareData(raw.toString());
}
input.close();
} else {
try{
httpConn.close();
}catch (Exception e) {
// TODO: handle exception
}
errorDialog(status+", status code");
retryFeed(getUrl(), "Network error. Retrying...");
}
s.close();
} else {
errorDialog("Sorry Insufficient Network Coverage.");
return;
}
} catch (IOCancelledException e) {
errorDialog(e.getMessage());
retryFeed(getUrl(), "Network error. Retrying...");
} catch (IOException e) {
errorDialog(e.getMessage());
retryFeed(getUrl(), "Network error. Retrying...");
}
What is the safest way to retry the connection if failed?
Thanks.
//New This is the Error thread. That check for errors in the connection... will this help? and is it the most efficient method? thanks..
/Error Thread - Thread to check errors/
private class ErrorThread extends Thread {
private static final int TIMEOUT = 3000; // EVERY 3 Seconds
private boolean hasException = false;
private String _theUrl;
/**
* Stops this thread from listening for messages
*/
private synchronized void stop()
{
hasException =false;
}
/**
* Listens for incoming messages until stop() is called
* #see #stop()
* #see java.lang.Runnable#run()
*/
public void run()
{
try
{
while (true) {
if((hasException==true))
{
// Synchronize here so that we don't end up creating a connection that is never closed.
errorDialog("Will Fetch new");
synchronized(this)
{
hasException=false;
if (!_connectionThread.isStarted()) {
fetchPage(_theUrl);
} else {
createNewFetch(_theUrl);
}
}
}
try {
//errorDialog("No exception.");
sleep(TIMEOUT);
} catch (InterruptedException e)
{
errorDialog("Exceptions"+e.toString()+e.getMessage());
System.exit(1);
//System.exit(0);/*Kill System*/
}
}
}
catch (Exception except)
{
}
}
public void setActive(boolean exception,String url)
{
this.hasException=exception;
this._theUrl=url;
}
}
If the connecrtion fails, typically, you want to close it, pause a small time, and retry. The purpose of the pause is to prevent your device from devoting excessive resources to trying to connect to a server that's having issues.