I've been searching for four hours and this is driving me nuts. I'm going to try keeping this short, if you need more information/code ask and I'll edit.
So I have an Android client that connects to a server using PrintWriter and BufferedReader. The way it works is it starts a new ASyncTask() to load the connection. When the connection is made, it sends a "Join" message to the server, and then loads a listen thread that has a while loop waiting for UserInput.readLine() != null, and once broken it returns a string that runs a process function that takes the string and does it's action, and reloads the listen task.
//Listener thread
class listen extends AsyncTask<Integer, Integer, Void> {
#Override
protected Void doInBackground(Integer... params) {
//Disconnect variable that's only turned true on backpress
if (!disconnect) {
try {
message = Connection.listen(); //socket object
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
// async task finished
if (!disconnect) {
say("INCOMMING"); //easy-made function for Toast
input(message);
}
}
}
and in that Connection:
public String listen() throws IOException {
String userInput;
while ((userInput = in.readLine()) != null) {
}
return userInput;
}
Now in my server java app, I have a thread that loads up other connection threads into an ArrayList and acts as a headquarters to dispatch messages to all child clients
In my connection:
while ((inputLine = in.readLine()) != null) {
//Tells HQ to process string, with id being who it's coming from
hq.Process(id, inputLine);
if (!connected)
break;
}
in HQ object:
public void Process(int id, String str) {
String[] msg = str.split(","); //split message
String send = " "; //string I print to console
if (msg[0].equals("join")) {
send = msg[1] + " has joined!";
parent.seats[cnew.get(id).seat] = id;
cnew.get(id).sendData();
System.out.println(id);
}
And after join, the HQ tells that connection to send that player's information to the phone
public void sendData() {
out.println("chips," + chips); // Update chip count
//give player his cards
out.println("card," + hq.parent.gameCards.getCard(10) + ","
+ hq.parent.gameCards.getCard(11));
//a cry for help to get some output on the phone
out.println("error,SAY THIS");
// out.flush(); //commented out because it didn't help at all
System.out.println("sending id " + id); //debug checker (ignore this)
}
My problem is, it worked when I connected four phones and they all sent toasts to each other.
But as soon as I changed it to send back data as soon as the player joins, I'm not getting a response in Android at all.
I can't figure out why it's not working. On server side, it's going through everything (Checked with system.prints). The connection IS made, and when I click buttons on the phone the Server is outputting it's responses. But the phone is not receiving anything -- I still don't know if the server is failing to send or the phone is failing to read. Can you see anything in the code that may be causing this? Need to see more? Or have any tips on how to debug the connection status? The listen() task is never finishing it's execution anymore.
UPDATE: So I figured out it's probably to do with my while() loop on android side, doh, probably never breaking. Stupid mistake. But I tried to add this as a tester, and still nothing:
public String listen() throws IOException {
String userInput;
while ((userInput = in.readLine()) != null) {
if (userInput.length() > 2)
break;
}
return userInput;
}
UPDATE: Next desperate update -
When I hit "back" (which sends quit msg to server that closes connection, and calls out.close and the rest.close) then I get a never ending loop of "MSG" Toast's -- The Toast that I put when an input is recognized. Is a out.close causing a block?
So it turns out, println on the server's side wasn't printing a new line -- adding + "\n" at the end of the server's messages made it go through. Why? I don't know..
Related
I modified an open-source project on github for a school project to fit my needs
it had a broadcast() method to send messages and it was called in the run() method in a while loop but the problem is that broadcast() sends a message to all users in a userList<>i wanted to add the ability to send a private message to one of the users by writing #username.
Here is the code for broadcast method:
private synchronized void broadcast(String msg) {
for (int i = 0; i < clientList.size(); i++) {
clientList.get(i).write(msg);
}
System.out.println("Log: Message broadcast --> " + msg);
}
and here is the run() method
public void run() {
System.out.println("Log: Got input/output streams for connected client.");
/** Get the first message from the client, attempt communication */
String clientMsg = null;
boolean accepted = false;
/** Allow client to create an account, login, or quit */
do {
clientMsg = client.read();
if (clientMsg.equals("QUIT")) {
System.out.println("Log: Client disconnected without signing in.");
client.disconnect();
return;
}
else if (clientMsg.startsWith("NEWUSER: ")) {
createUser(clientMsg);
}
else if (clientMsg.startsWith("LOGIN: ")) {
accepted = authenticate(clientMsg);
}
else
{
System.out.println("Log: Unexpected client message -> " + clientMsg);
client.disconnect();
return;
}
} while(!accepted);
/** Run main chat loop. Will read from the client, and broadcast each read
* until the client disconnects. */
while (true) {
int i=0;
String username= clientList.get(i).getUsername();
String line = client.read();
if (line == null) break;
else if(line.startsWith("#"+username)){
broadcastp(line,username);
}
else {
broadcast(line);
}
i++;
}
/** The only way for the client to exit the above loop is to disconnect.
* Therefore, call the handler's exit routine */
exit();
}
Here is the broadcastp() method that i tried to implement this feature with, but it doesn't work. It compiles and runs perfectly though just without the private chat feature.
private synchronized void broadcastp(String msg,String username) {
for (int i = 0; i < clientList.size(); i++) {
username = clientList.get(i).getUsername();
if(msg.startsWith("#"+username))
{clientList.get(i).write(msg);}
else {
continue;
}}
System.out.println("Log: Message broadcast --> " + msg);}
I do not have the full picture of how your program works, but you say the program runs perfectly, but does not do the private messaging part.
If I look at your code, in the while loop you always take the first username from the clientList (i = 0) and only call broadcastp if the line starts with that name.
First of all.. is broadcastp ever invoked? In broadcastp you have another loop, but that will always match on i == 0 given the way you invoke it (with the line and username from the while loop).
The problem seems to be be there. So something like this within the while loop might work for you (remove the i variable, and no need for broadcastp):
boolean isPrivate = false;
String line = client.read();
for (User user : clientList) {
if (line.startsWith("#" + user.getUsername())) {
user.write(line);
isPrivate = true;
break;
}
}
if (!isPrivate) {
broadcast(line);
}
I have built a Spring CLI app which communicates with a server in an async fashion. The server was given, I did not create it, basically my app is required to open a TCP socket and send a JSON through it, then it sends back a JSON. It is mandatory not to use CLI parameters, but instead in the callback of the request I want to show the user a set of options for which he needs to select by inserting the corresponding number on the CLI. Most probably I'm not doing right something, because after entering the command, I see spring> on the console (this is an expected behavior) and it will block the async callback unless I press something (nothing is printed to the CLI when I receive the callback unless I press a bunch of enters - this is unexpected). To read from the console so far I used JLine's command line, what I would like to achieve is that when I get the response from the server and the callback is served, the console is given to the thread on which the callback is running (I instantly print the contents of the callback to the console, and I'm able to read the input without any tricks).
Some code:
public void runReceiver(){
receiverThread = new Thread(() -> {
byte[] digit = null;
int nb;
Iterator<CommandListener> it;
CommandListener listener;
String message;
List<CommandListener> listenersToRemove = new ArrayList<>();
while (true) {
try {
nb = communicatorInput.readInt();
digit = new byte[nb];
communicatorInput.readFully(digit);
} catch (IOException e) {
e.printStackTrace();
}
it = listeners.iterator();
while (it.hasNext()){
listener = it.next();
if (digit != null && digit.length > 0) {
message = new String(digit);
// the message was not acknowledged
if(message.contains("NACK")){
try {
listener.onError(message);
if (listener.isDone()) {
listenersToRemove.add(listener);
}
} catch (Exception e){
e.printStackTrace();
}
} else try {
listener.onCompleted(message);
} catch (InvalidObjectException e){
Main.logger.debug(String.format("Response could not be parsed as %s", listener.getCommandType()));
} catch (Exception e){
e.printStackTrace();
}
if (listener.isDone()) {
listenersToRemove.add(listener);
}
}
}
listeners.removeAll(listenersToRemove);
}
}, "receiverThread");
receiverThread.setDaemon(true);
receiverThread.start();
Then a CLI command (it expects no input here):
#CliCommand(value="start", help = "Starts stuff")
public void start() throws IOException, InterruptedException {
// this method is passed to the thread with the listener
getAvailabilities().updateAvailabilities("all", "all", "all", someListener);
}
And the callback for that listener:
someListener = new CommandListener() {
private String source = "Start some listener";
#Override
public void onCompleted(String r) throws IOException {
System.out.println("Which would you like to start?");
getAvailabilities().printAvailableBrands();
String brandNumber = "";
while(Objects.equals(brandNumber, "")){
System.out.println("Please enter the number of the Brand: ");
//when the callback arrives here I still only see ">spring:" and I get nothing printed on the console
brandNumber = cr.readLine();
if(!isInputAllowed(brandNumber, getAvailabilities().AvailableBrands.size())){
brandNumber = "";
}
}
BrandName = getAvailabilities().AvailableBrands.get(Integer.parseInt(brandNumber) - 1);
//updating the availabilities narrows down the things I list to the console, so I send an update after every selection
getAvailabilities().updateAvailabilities("all", BrandName, "all", getInterfaceListener);
done = true;
}
This might slightly connect to the issue that sometimes while debugging the CLI in Idea, it gets whacky inputs, eg. when I insert start it says No such command as ar, and if I press enter again, it'll say (some of) the rest: No such command as stt.
The problem is here:
if (listener.isDone()) {
listenersToRemove.add(listener);
}
If you want your listeners to be executed asynchronously you should not check their completion right away on the same thread as it will most likely return false.
The issue you might be having is that your listeners schedule some task but have no time to finish it as you immediately remove them after the loop:
listeners.removeAll(listenersToRemove);
It is very hard to tell what your logic is but I guess in the next while iteration your list is empty.
I've created a client-server connection, something like a chat system. Previously I was using a while loop on the client side, and it was waiting to read a message from the console every time (of course server has a while loop as well to serve forever). But now, I'm trying to first create a connection at the beginning of the session, and then occasionally send a message during the session, so to maintain a permanent and persistent connection.
Currently, without the while loop, the client closes the connection and I don't know how to find a workaround.
Here is the client code:
import java.net.*;
import java.io.*;
public class ControlClientTest {
private Socket socket = null;
// private BufferedReader console = null;
private DataOutputStream streamOut = null;
public static void main(String args[]) throws InterruptedException {
ControlClientTest client = null;
String IP="127.0.0.1";
client = new ControlClientTest(IP, 5555);
}
public ControlClientTest(String serverName, int serverPort) throws InterruptedException {
System.out.println("Establishing connection. Please wait ...");
try {
socket = new Socket(serverName, serverPort);
System.out.println("Connected: " + socket);
start();
} catch (UnknownHostException uhe) {
System.out.println("Host unknown: " + uhe.getMessage());
} catch (IOException ioe) {
System.out.println("Unexpected exception: " + ioe.getMessage());
}
String line = "";
// while (!line.equals(".bye")) {
try {
Thread.sleep(1000);
//TODO get data from input
// line = console.readLine();
line="1";
if(line.equals("1"))
line="1,123";
streamOut.writeUTF(line);
streamOut.flush();
} catch (IOException ioe) {
System.out.println("Sending error: " + ioe.getMessage());
}
// }
}
public void start() throws IOException {
// console = new BufferedReader(new InputStreamReader(System.in));
streamOut = new DataOutputStream(socket.getOutputStream());
}
}
And here is the Server code:
import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ControlServer {
private Socket socket = null;
private ServerSocket server = null;
private DataInputStream streamIn = null;
public static void main(String args[]) {
ControlServer server = null;
server = new ControlServer(5555);
}
public ControlServer(int port) {
try {
System.out
.println("Binding to port " + port + ", please wait ...");
server = new ServerSocket(port);
System.out.println("Server started: " + server);
System.out.println("Waiting for a client ...");
socket = server.accept();
System.out.println("Client accepted: " + socket);
open();
boolean done = false;
while (!done) {
try {
String line = streamIn.readUTF();
// TODO get the data and do something
System.out.println(line);
done = line.equals(".bye");
} catch (IOException ioe) {
done = true;
}
}
close();
} catch (IOException ioe) {
System.out.println(ioe);
}
}
public void open() throws IOException {
streamIn = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
}
public void close() throws IOException {
if (socket != null)
socket.close();
if (streamIn != null)
streamIn.close();
}
}
I would like to summarize some good practices regarding the stability of TCP/IP connections which I apply on a daily basis.
Good practice 1 : Built-in Keep-Alive
socket.setKeepAlive(true);
It automatically sends a signal after a period of inactivity and checks for a reply. The keep-alive interval is operating system dependent though, and has some shortcomings. But all by all, it could improve the stability of your connection.
Good practice 2 : SoTimeout
Whenver you perform a read (or readUTF in your case), your thread will actually block forever. In my experience this is bad practice for the following reasons: It's difficult to close your application. Just calling socket.close() is dirty.
A clean solution, is a simple read time-out (e.g. 200ms). You can do this with the setSoTimeoutmethod. When the read() method timeouts it will throw a SocketTimeoutException. (which is a subclass of IOException).
socket.setSoTimeout(timeoutInterval);
Here is an example to implement the loop. Please note the shutdown condition. Just set it to true, and your thread will die peacefully.
while (!shutdown)
{
try
{
// some method that calls your read and parses the message.
code = readData();
if (code == null) continue;
}
catch (SocketTimeoutException ste)
{
// A SocketTimeoutExc. is a simple read timeout, just ignore it.
// other IOExceptions will not be stopped here.
}
}
Good practice 3 : Tcp No-Delay
Use the following setting when you are often interfacing small commands that need to be handled quickly.
try
{
socket.setTcpNoDelay(true);
}
catch (SocketException e)
{
}
Good practice 4 : A heartbeat
Actually there are a lot of side scenario's that are not covered yet.
One of them for example are server applications that are designed to only communicate with 1 client at a time. Sometimes they accept connections and even accept messages, but never reply to them.
Another one: sometimes when you lose your connection it actually can take a long time before your OS notices this. Possibly due to the shortcomings described in good practice 3, but also in more complex network situations (e.g. using RS232-To-Ethernet converters, VMware servers, etc) this happens often.
The solution here is to create a thread that sends a message every x seconds and then waits for a reply. (e.g. every 15 seconds). For this you need to create a second thread that just sends a message every 15 seconds. Secondly, you need to expand the code of good practice 2 a little bit.
try
{
code = readData();
if (code == null) continue;
lastRead = System.currentTimeMillis();
// whenever you receive the heart beat reply, just ignore it.
if (MSG_HEARTBEAT.equals(code)) continue;
// todo: handle other messages
}
catch (SocketTimeoutException ste)
{
// in a typical situation the soTimeout is about 200ms
// the heartbeat interval is usually a couple of seconds.
// and the heartbeat timeout interval a couple of seconds more.
if ((heartbeatTimeoutInterval > 0) &&
((System.currentTimeMillis() - lastRead) > heartbeatTimeoutInterval))
{
// no reply to heartbeat received.
// end the loop and perform a reconnect.
break;
}
}
You need to decide if your client or server should send the message. That decision is not so important. But e.g. if your client sends the message, then your client will need an additional thread to send the message. Your server should send a reply when it receives the message. When your client receives the answer, it should just continue (i.e. see code above). And both parties should check: "how long has it been?" in a very similar way.
You could wrap a thread around the connection and have it periodically send a status to keep the line open, say every 30 seconds or whatever. Then, when it actually has data to send it would reset the keep alive to be 30 seconds after the last transmission. The status could be helpful to see if the client is still alive anyway, so at least it can be a useful ping.
Also, you should change your server code, you appear to only handle one connection at the moment. You should loop and when a socket connection comes in spawn a thread to handle the client request and go back to listening. I may be reading to much into what may just be your test code, though.
Make the client socket connection wrapped around a thread. Use a blocking queue to wait for messages. There should only be a single sender queue throughout your application, so use a singleton pattern.
e.g.
QueueSingleton queue = QueueSingleton.getSenderQueue();
Message message = queue.take() // blocks thread
send(message); //send message to server
When you need to send a message to the server, you can use the blocking queue to send the message.
QueueSingleton queue = QueueSingleton.getSenderQueue();
queue.put(message)
The client thread will wake up and process the message.
For maintaining the connection, use a timer task. This is special type of thread that calls a run method repetitively at specified periods. You can use this to post a message, a ping message, every so often.
For processing the received message, you could have another thread, waiting for messages on another blocking queue (receiver queue). The client thread will put the received message on this queue.
My project involves reading the status of an LED on Arduino, from java. It will move on to reading temperature from Arduino, but I got stuck. So here it is:
I send the "Turn on/off!" message from my java program, and I want it to show if the LED is on and off.
So when I send "192.168.0.100/ON", the LED turns on and I shoud get the "ON" message in my program.
The code on Arduino:
byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x2F, 0xD4 };
IPAddress ip(192,168,0,100);
EthernetServer server(80);
String message = String(30);
void setup()
{
pinMode(2, OUTPUT);
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
Serial.begin(9600);
}
void loop()
{
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
// an http request ends with a blank line
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (message.length() < 30) {
message += c;
}
Serial.print(message);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n') {
if (message.indexOf("ON") > 0) {
digitalWrite(2, HIGH);
client.print("ON");
}
if (message.indexOf("OFF") > 0) {
digitalWrite(2, LOW);
client.print("OFF");
}
message = "";
client.stop();
}
}
}
// give the web browser time to receive the data
delay(1);
}
}
The code in java:
public class TestClient {
public static void main(String[] args) {
HttpURLConnection connection = null;
BufferedReader serverResponse = null;
try {
// OPEN CONNECTION
connection = (HttpURLConnection) new URL("http://192.168.0.100/ON")
.openConnection();
connection.connect();
// RESPONSE STREAM
serverResponse = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
// READ THE RESPOSNE
String line;
while ((line = serverResponse.readLine()) != null) {
System.out.println(line);
}
} catch (MalformedURLException mue) {
mue.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (connection != null)
connection.disconnect();
if (serverResponse != null) {
try {
serverResponse.close();
} catch (Exception ex) {
}
}
}
}
}
What happens: The LED turns on, but I get this error in java:
java.net.SocketException: Unexpected end of file from server at TestClient.main(TestClient.java:23) -> connection.getInputStream();
What I want: After sending the "ON" message, it should be printed in console.
Mention: if I send 192.168.0.100/ON from my browser, the LED turns on and the message appears in the web page.
There are two issues here:
If the exception is thrown when getting the InputStream, then it's happening because the connection has been closed at that point, and this happens because the Arduino sends the message then immediately "closes" the client, effectively terminating the connection. You can do three things:
a. Try and creating the input stream before calling connect(), but this most likely will fail due to the connection not existing at that point.
b. Put a delay before calling client.stop();
c. (recommended) Let the client close the connection, don't do it on the server.
Try adding a \n after ON and OFF in client.print() method, in the Arduino code.
client.print("ON\n");
...
client.print("OFF\n");
readLine() will read until the first end-of-line char which never comes.
Try reverse the order in your finally block. Close the input stream before you close the socket.
As this guy here says, the Arduino can serve HTML pages, so I guessed my HttpURLConnection must know about this. Here, it says that "The version of an HTTP message is indicated by an HTTP-Version field in the first line of the message."
So I simply added the following code into the Arduino sketch, right after I check (c == "\n"):
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
I didn't get to this very quickly, but after reading others` code and the resources I mentioned above, I came to this conclusion. The explanation could be as well wrong for all I know, but it worked, and my project runs.
I'm making a simple chat server and just made it so each connection runs on a new thread.
The old version started a single thread for the server, it did a while loop, which would stop when a stop message was sent then close the socket.
The new version loops forever and create a new thread for each new connection. Now I cannot close the socket connection.
If you press a key and the main thread stops, the socket stays open. Thus when I run the program again I need to change the socket number.
code of server
while(true)
{
///////////////////////////////////////////////////
// get a new connection
///////////////////////////////////////////////////
System.out.println("Aceepting connections on port 1030 \r");
try{
// Get New Connection
// wait for ever on accepting new connections
server.setSoTimeout(0);
connection=server.accept();
cConnection thread = new cConnection("thread3", connection);
} catch(IOException ec)
{
System.out.println(ec.getMessage());
}
}
code that starts server
Now each message comes in on a new thread, so I cannot tell it to stop and close the socket.
You need to provide a flag that must be globally accesible, so when some client wants to stop the server then change the variable ans stops the bucle. By example:
class YourServer {
private static boolean execute = true;
public static synchronized void stop() {
execute = false;
}
public void yourMethod() {
while(execute) {
// implement your server here
}
}
}
When a client send the command STOP you must be do
YourServer.stop();
If you want a stop command to stop the server you can call System.exit() to force the program to store or just closing server is likely to be all you need.
Looking into your problem, I understood one thing, that since you are putting
while (true), so your control always gets stuck at connection=server.accept(); listening for a new connection. So in order to stop the sockets you need to first find a way to stop looping in that while loop. Either you can set a Variable, like (int clientsConnected) to check the number of Clients, when that comes to zero stop that while loop. So you can stop your sockets.
Below is my sample code for clients which is doing the same thing for closing the Sockets.
Hopefully this solves your problem.
class GetNamesFromServer implements Runnable
{
private Socket sForName, sForId;
private BufferedReader in, inForName, inForId;
private PrintWriter outForName, outForId;
private static String clientNames;
public GetNamesFromServer(Socket s1, Socket s2)
{
sForName = s1;
sForId = s2;
}
public void run()
{
try
{
outForName = new PrintWriter(sForName.getOutputStream(), true);
outForName.println(Client.clientName);
System.out.println("Send Name : " + Client.clientName);
outForName.flush();
}
catch(IOException e)
{
System.err.println("Error sending Name to the Server.");
}
try
{
inForId = new BufferedReader(new InputStreamReader(sForId.getInputStream()));
Client.clientId = (inForId.readLine()).trim();
System.out.println("Client ID is : " + Client.clientId);
}
catch(IOException e)
{
System.err.println("Error Receiving ID from Server.");
}
try
{
inForName = new BufferedReader(new InputStreamReader(sForName.getInputStream()));
while (true)
{
clientNames = inForName.readLine();
if (clientNames != null && clientNames != "")
{
clientNames = clientNames.substring(1, clientNames.length() - 1);
System.out.println("Names Received : " + clientNames);
String[] names = clientNames.split(", ");
Client.nameClients.clear();
for (String element: names)
Client.nameClients.add(element);
Client.nPane.setText("");
int size = Client.nameClients.size();
System.out.println("Size of list : " + size);
for (int i = 0; i < size; i++)
{
String name = Client.nameClients.get(i);
String colour = Character.toString(name.charAt(0));
name = name.substring(1, name.length()) + "\n";
appendToNamePane(name, ReceiveMessages.getColour(Integer.parseInt(colour)), "Lucida Console");
}
System.out.println("Clients Online : " + Client.nameClients);
}
int index = Client.nameClients.indexOf(Client.clientId + Client.clientName);
**if (index == -1)
{
sForName.close();
break;
}**
}
}
catch(IOException e)
{
System.err.println("Error Receiving Names of Clients from Server");
}
}
NEW EDITION :
You can add a cap to maximum number of clients that can connect, once that reaches your while loop will not go to connection = server.accept(); and hence when they are done chatting (after some time) i.e. totalClients = 0, you can stop your sockets as well, to stop the program.
if (totalClients == 0)
{
socket.close();
serverSocket.close();
}
Regards