In most Java client/server examles they use ServerSocket.accept() method to receive messages from the client. This method blocks the thread. This looks good becouse server does not do useless work and can answer immediately. But on the client side they use infinite loop like this:
BufferedReader in = new BufferedReader (new InputStreamReader (socket.getInputStream ()));
while (true)
{
String cominginText = "";
try
{
cominginText = in.readLine ();
System.out.println (cominginText);
}
catch (IOException e)
{
//error ("System: " + "Connection to server lost!");
System.exit (1);
break;
}
}
This looks bad because in my mind client does useless work.
Is there some way to receive a message on the client side without doing useless work and immediately like on server side (may be should use listeners?)
"What do you mean by useless work? – Braj 31 mins ago "
for example checking in loop is button pressed (we should use listener)
while (!button.isPressed() ) { } is bad way.
There is no 'useless work' here, as the socket is in blocking mode, but there is:
A pointless initialization of 'comingText'.
A failure to check it for null, so it will spin mindlessly at EOS.
An incorrect handling of IOExceptions: not all of them are fatal, e.g. SocketTimeoutException, and none of them should cause an immediate System.exit().
The line read is thrown away, which is an application protocol error.
So it is definitely wrong.
in.readLine() blocks. The loop wont continue until a String returns
Related
i read some of the answers here about this problem, but i wasn't satisfied with them, so i decided to ask it my self. So I know that there are similar questions, but since the answers don't really work for me, i asked myself.
I have an app that lets 2 users connect to each other (one works as a server, the other one as client). They will send files through that socket connection. I am using a Service with 2 threads inside, one to read, another one to send the file that the user chose.
Here is the problem : If a client closes the app by swiping it on the android menu (of the apps that are running), and then the server (the other guy) tries to send him something, in my opinion it should throw an IOException, since the other end of the socket streams is over. But it is not doing that and i don't know why. If i try to send something to someone that left, i want to show a Toast.
Edit: just noticed it always stops at the instruction out.reset();
Do any of you know why that exception is not being thrown?
What could be a possible solution.
PS: It is a lite app, so to send Keep Alive messages wouldn't be a good solution. Also, it already showed the toast that i have one or two times, but then i couldn't replicate that behaviour again.
Here is my code where i wan't that to happen :
ClientHandler tmp = connectedClients.get(key);
ObjectOutputStream out = tmp.getOut();
Socket s = tmp.getSocket();
if(s.isClosed()){
System.out.println("The socket of this client "+key + " is closed!");
}
if(s.isOutputShutdown()){
System.out.println("The output of this client is shutdown !");//only checks this side, the other one is the one that is shutdown
}
System.out.println("changed the culpado to : "+1);
createSendNotification();
File apkToSend;
for(int i=0;i<listOfApps.size();i++){
System.out.println("Item do be sent is : "+i);
HighwayGridViewAppItem tmpItem=listOfApps.get(i);
filePath=tmpItem.getFilePath();
appName=tmpItem.getAppName();
System.out.println("his filepath to send is : "+filePath);
System.out.println("his appname to send is : "+appName);
couldSend=false;
apkToSend=new File(filePath);
if(apkToSend.exists()){//do i reallly need this if?
apkToSendSize=apkToSend.length();
System.out.println("File size: " +apkToSendSize);
try{
out.writeObject(appName +": "+ apkToSendSize);//appName to send to have the name of the file
byte[] buffer = new byte [8192];
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(apkToSend));
int count;
totalToSend =0;
showSendProgress();
while((count=bis.read(buffer))!=-1){
out.write(buffer,0,count);
totalToSend +=count;
out.reset();
System.out.println("ServerComm send thread - already sent this ammount : "+ totalToSend);
}
out.flush();
bis.close();
}
catch ( IOException e){
System.out.println("It is throwing the input output exception");
e.printStackTrace();
connectedClients.remove(key);
if(clients.size()<=1){
h.post(new Runnable() {
#Override
public void run() {
Toast.makeText(context, "No one is in your group.", Toast.LENGTH_SHORT).show();
}
});
i=listOfApps.size()+1;
}else{
System.out.println("Has more than one");
}
}
PS: When i try to send to a "closed" client, it prints a few "ServerComm send thread - already sent this ammount : "+ totalToSend" but then just stops, which is when i think it should throw the exception, but it just stops, and doesn't give any error, the app continues its life, but i NEED to give some input to the user that some problem went down.
Also, I create that Handler in the onCreate method of this service, it is being correctly created (since it is in a Service, it needs different creation) with the main looper.
Thank you guys in advance.
EDIT: Eventually, after almost 4 minutes, it throws a SocketException, but i can't wait that long.
Just because Android disposes of an app does not mean that internally all of your open connections are closed, it is mostly likely you need to detect the Android event and then execute the code that explicitly closes the open socket rather than waiting for Android to take care of it eventually. Otherwise, you will have to wait for the socket to be closed by garbage collection calling the finalizer.
This post here has some details about Android events and the onDestroy method in particular: How to close Android application?
If you require an immediate disconnection detection then you would have to implement your own ping/keep alive mechanism which would normally mean sending packets and acknowledging them continuously to be able to catch an exception more reliably.
I got this multi-threaded server application that someone else wrote.
When it is going to accept a Socket-object with it's ServerSocket-object,
it's running trough a method called "acceptSocketSafe".
Here is a snippet of the program where I have included the parts of code needed:
public Socket acceptSocketSafe(ServerSocket x) {
boolean socketFound = false;
Socket socket = null;
do {
try {
socket = x.accept();
int i = socket.getInputStream().read();
if ((i & 0xFF) == 14) {
socketFound = true;
}
} catch (Exception e) {
}
} while (!socketFound);
return socket;
}
What I don't understand is how the method "acceptSocketSafe" makes the socket acception safer than how I would have done it (the simple, regular way). (I believe it has something with excluding connections with bad intentions (DDoS, for example)).
Thank you for any explanation of this method!
This is security by obscurity. The socket is accepted anyway, only that it checks that the client sends 0x0E (14) as the first byte. If it doesn't, it throws (without closing the accepted socket btw.).
This could still DDoS'ed by just not sending anything after connecting...
Edit: Looking at it closer, it doesn't even need to be a distributed attack. A single client just not sending any byte will block the accept loop entirely, mission accomplished. Whoever wrote it didn't know what he was doing.
It doesn't make it safer at all. It makes it worse.
This code does client I/O on the accepting thread. That means that all a malevolent client has to do to mount a DOS attack is to connect and send nothing. Then no other client can be accepted until that client either sends something or closes the connection.
As for what it does, it just rejects client connections that don't start with a 14 byte. It's a pretty weak test: 1 in 256 random attempts will pass. It would be better accomplished by proper error checking in the application protocol. You still have to do that anyway so there is no actual advantage at all.
This code also leaks rejected sockets.
Throw it away.
I have noticed that in a few pieces of example code for a TCP chat program, written in Java, both the read string from client and send string to server both occur in main.
For example, here is a tutorial where I don't see the distinction between an input thread and in output thread... http://www.cise.ufl.edu/~amyles/tutorials/tcpchat/TCPChat.java
"BufferedReader input" and "PrintWriter output" are both used with a TCP Socket from within the same thread. As a complete novice, this confuses me because previously, if I had something that waits for input, like the "getch()" get character function from C, that thing that will hold up the thread (unless it is an event or an exception). Normally, when I imagine code in a thread being executed, I imagine it being executed line by line, with occasional jumps in execution for exceptions and events. But writing to a stream and reading from a stream is neither an exception nor an event - I don't know what the main thread would do if it received an input string and was supposed to send out an output string both at the same moment. Is the stream itself actually handled by some other thread or some other program, like the terminal or the OS?
I had felt so weird about this that I split the chat program into two separate threads to make me feel more comfortable - one thread for receiving strings from the TCP socket and another thread for sending strings out through the same socket. Can someone provide an explanation as to why my act of splitting input and output into two separate threads is totally unnecessary? And before someone marks this down due to lack of research and understanding, I did my best to read online Java tutorials on Sockets and I have had experience writing to and reading from streams (terminal and plain text file).
case CONNECTED:
try {
// Send data
if (toSend.length() != 0) {
out.print(toSend); out.flush();
toSend.setLength(0);
changeStatusTS(NULL, true);
}
// Receive data
if (in.ready()) {
s = in.readLine();
if ((s != null) && (s.length() != 0)) {
// Check if it is the end of a trasmission
if (s.equals(END_CHAT_SESSION)) {
changeStatusTS(DISCONNECTING, true);
}
// Otherwise, receive what text
else {
appendToChatBox("INCOMING: " + s + "\n");
changeStatusTS(NULL, true);
}
}
}
}
catch (IOException e) {
cleanUp();
changeStatusTS(DISCONNECTED, false);
}
break;
I took this code snippet from the "main procedure" of the link you provided.
The method that would halt the thread from continuing like you were talking about is in.readLine(); The thread would wait until there was something to read from the input stream before continuing. I would like to point out the if statement " if(in.ready()) " By having that if statement there, the code checks first if the input stream has something to read. Otherwise, it skips over it and continues.
I want to listen to incoming packets on port 19132 and print them out as they come, but so far my code doesn't exactly print anything at all. There is a server forwarding the packets through port 19132 to my computer, and the port is open and enabled, but still nothing is printed.
public static void listenToPort(){
try{
ServerSocket serverSocket = new ServerSocket(19132);
Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
try{
System.out.println(in.readLine());
}
catch(IOException e){
System.out.println("Connection to server lost!");
System.exit(1);
break;
}
}
}
catch(IOException e){
System.out.println(e.getMessage());
}
}
On the server side there is info being sent, but the client program (this script) doesn't receive anything, what's the problem?
You are reading lines, not 'packets'. If the data being sent to this port doesn't contain newlines, readLine() will block forever, or until the peer closes the connection.
On the other hand when it does read something, or EOS, your code will spin forever printing null, because you aren't checking for readLine() returning null, at which point you must close the socket and exit the loop.
Not to mention, it might be entirely possible that the System.exit doesn't give enough time for the console to flush it's output (i'm not 100% sure if System.exit will or won't cause a flush in System.out and System.err).
Why don't you attach a debugger to your server process and see if it's even getting past the in.readLine()? As one of the other posters mentioned, if you're not sending a newline character, in.readLine() would block until you do.
Secondly, you shouldn't really use System.exit. It's bad form in most cases and leads to people wondering why the hell an app would just randomly quit. If you want to exit an app, you should allow the code to return back to the main() method, and from there you can do a System.exit if necessary.
I'm a real newbie to java, so please excuse me if this is a hopelessly straightforward problem.
I have the following from my java game server:
// Get input from the client
DataInputStream in = new DataInputStream (server.getInputStream());
PrintStream out = new PrintStream(server.getOutputStream());
disconnect=false;
while((line = in.readLine().trim()) != null && !line.equals(".") && !line.equals("") && !disconnect) {
System.out.println("Received "+line);
if(line.equals("h")){
out.println("h"+EOF); // Client handshake
System.out.println("Matched 1");
}else if (line.equals("<policy-file-request/>")) {
out.println("..."+EOF); // Policy file
System.out.println(server.getInetAddress()+": Policy Request");
disconnect=true;
System.out.println("Matched 2");
}else if(line.substring(0,3).equals("GET")||line.substring(0,4).equals("POST")){
out.println("HTTP/1.0 200 OK\nServer: VirtuaRoom v0.9\nContent-Type: text/html\n\n..."); // HTML status page
disconnect=true;
System.out.println("Matched 3");
} else {
System.out.println(server.getInetAddress()+": Unknown command, client disconnected.");
disconnect=true;
System.out.println("Matched else");
}
}
server.close();
First of all, the client sends an "h" packet, and expects the same back (handshake). However, I want it to disconnect the client when an unrecognised packet is received. For some reason, it responds fine to the handshake and HTML status request, but the else clause is never executed when there's an unknown packet.
Thanks
From the information added in your comments it seems that what will be happening is the client is sending a single character (e.g. 'n'). The line
line.substring(0,3).equals("GET")||line.substring(0,4).equals("POST"))
will be executed, but since line is only a single character line.substring(0,3) will throw a StringIndexOutOfBoundsException. Either this is causing your program to fail and you haven't mentioned that. Or you have some exception handling going on in another part of your code that you haven't shown and this is either supressing the error or printing a log line or something and again you haven't mentioned this (or noticed it).
Try replacing substring().equals with startsWith
You need to check for null before you trim it. The result of trim() can never be null.
You should check disconnect first, before the readLine(), otherwise you are always doing one readLine() too many.
If you are never getting to your 'else' it means one of the other conditions is always true.
There are a number of problems with your code
in.readLine().trim()
readLine do returns null and calling null.trim() will result in ... NullPointerException
Is there a reason to append EOF to every response you send.
calling substring without making sure it has at least that much elements will throw StringIndexOutOfBoundsException if it is shorter.
Are you testing with "P" for example?
It would seem highly unlikely that the else is not executing. Are you sure your loop does not exit on such packets and hence your conditions do not even run? Does your
System.out.println("Received "+line); print anything for the packet that seems to be missing the else statement?