bwc
bwc

Reputation: 1

Java - Listen to localhost and port

Sorry in advance for a noob question, but I am learning java and playing with some little projects I find useful and interesting to improve my learning.

The current project that I'm stuck on is that I'm trying to create a small desktop app in Java that will "listen" to the text sent to localhost from another application. Basically, if I input an IP and Port, I want the app to listen to any text-type traffic that is sent to that IP and port.

I tried using some code I found and started adjusting it to meet my needs.

package localHostApp;

import java.io.*;  
import java.net.*;  

public class Server2 {  

    public static void main(String[] args){  

        try{  
            ServerSocket ss=new ServerSocket(1234);  
            Socket s=ss.accept();//establishes connection   
            DataInputStream dis=new DataInputStream(s.getInputStream());  
            String  str=(String)dis.readUTF();  
            System.out.println("message= "+str);  

            ss.close();  

        }catch(Exception e){System.out.println(e);}  

    }  
}  

However, this code only seems to work if you also have a "client" app that is also using Java socket to connect like this:

import java.io.*;  
import java.net.*;  
public class MyClient {  
public static void main(String[] args) {  
try{      
Socket s=new Socket("localhost",1234);  
DataOutputStream dout=new DataOutputStream(s.getOutputStream());  
dout.writeUTF("Hello Server");  
dout.flush();  
dout.close();  
s.close();  
}catch(Exception e){System.out.println(e);}  
}  
}  

When I run the server code and the client code, the client sends traffic to the server which displays in the console just fine. However, if I try to connect to the server with another app (like PUTTY) to just send raw text to the server, it doesn't display. It shows that it connects, but doesn't receive the text.

What am I missing here? Should a socket connection in Java just be able to read and display whatever information is sent to that IP and port?

Again, sorry for the noob question.

--- UPDATED ---

So I incorporated the new approach like this:

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class Server {

    public static void main(String[] args){

        try{
            ServerSocket ss=new ServerSocket();
            Socket s=ss.accept();//establishes connection
            var rawIn = ss.getInputStream();
            var in = new BufferedReader(new InputStreamReader(rawIn, StandardCharsets.UTF_8)); {

        while (true){
            String cmd = in.readLine().trim();
            if (cmd == null) break; //client is hung up
            if (cmd.isEmpty()) continue; //empty line was sent
            System.out.println("Client sent: " + cmd);
        }
            }

        }catch(Exception e){System.out.println(e);}

    }
}  

On the var rawIn = ss.getInputStream, I am getting an error "Cannot resolve method 'getInputStream' in 'ServerSocket'

Did I incorporate the suggestion below incorrectly?

----- UPDATE 2 ----

Found an error in how I incorporated it previously. (used ss.getInputStream instead of s.getInputStream).

Now I can compile and run without error, but it immediately says socket is not bound yet and exits. Do I need to specifically bind the IP and Port for it to sit and listen?

---- UPDATE 3 -----

I think I am getting dangerously close. Here is the code I have in place currently.

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class Server {

    public static void main(String[] args){

        try{
            ServerSocket ss=new ServerSocket(1234, 1, 127.0.0.1);
            Socket s=ss.accept();//establishes connection
            var rawIn = s.getInputStream();
            var in = new BufferedReader(new InputStreamReader(rawIn, StandardCharsets.UTF_8)); {

        while (true){
            String cmd = in.readLine().trim();
            if (cmd == null) break; //client is hung up
            if (cmd.isEmpty()) continue; //empty line was sent
            System.out.println("Client sent: " + cmd);
        }
            }

        }catch(Exception e){System.out.println(e);}

    }
}  

I am getting an error for my bind address that it is looking for a ')' or ';'. I am baffled why that would be the case.

As I understand it from looking at the Java API, my ServerSocket constructor should be saying to listen to port 1234, allow one socket connection at a time, and listen on 127.0.0.1.

What am I missing?

Upvotes: 0

Views: 3169

Answers (2)

bwc
bwc

Reputation: 1

Thank you everyone for the help. I actually got it to work. Here is the finished code I ended up with that listens and displays anything I type into a "client" like PUTTY or whatever so long as I am sending to the appropriate IP and Port.

I think my next steps will be to experiment with Swing or JavaFx to put a UI around this and create an executable jar file to make a little desktop app that does this.

Thanks again for the help!

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class Server {

    public static void main(String[] args){

        try{
            ServerSocket ss=new ServerSocket(1234, 1, InetAddress.getByName("127.0.0.1"));
            Socket s=ss.accept();//establishes connection
            var rawIn = s.getInputStream();
            var in = new BufferedReader(new InputStreamReader(rawIn, StandardCharsets.US_ASCII)); {

        while (true){
            String cmd = in.readLine().trim();
            if (cmd == null) break; //client is hung up
            if (cmd.isEmpty()) continue; //empty line was sent
            System.out.println("Client sent: " + cmd);
        }
            }

        }catch(Exception e){System.out.println(e);}

    }
} 

Upvotes: 0

rzwitserloot
rzwitserloot

Reputation: 102804

TCP/IP connections are just streams of bytes. That is all they are. So, if you run readUTF, that is impossible. How the heck would I know when the string ends?

Oh, after a newline you say. You made that up. The TCP/IP protocol doesn't state anywhere that there's such a thing as a 'message' that is terminated. You should be making this stuff up: Protocols are a neccessary part of communication. However, the key is, you need to 'speak the same protocol' on both sides. That's a decision that the TCP/IP spec doesn't include; you make that on a per-protocol basis. That's how readUTF exists: It made up arbitrary rules. Of course, writeUTF from DataOutputStream uses the same made-up rules. If you're playing some ballgame and everybody makes up identical rulesets, you're actually playing a game. If one person thinks it's golf and the other thinks its baseball, that's not gonna work.

The readUTF protocol evidently disagrees with PuTTY on how messages are terminated. readUTF is in fact entirely unsuitable for everything. Well, unsuitable for everything except, specifically, reading what DataOutputStream.writeUTF sends. So, if you have any interest in reading anything else, you need to delete that part of your code entirely. From the relevant javadoc:

  • It reads modified UTF-8. We lost the game on the spot already. UTF-8 is fine. Great, even, very obvious. But modified UTF-8? Oh no.
  • This protocol works by first sending in big-endian 16-bit (so, 2 bytes) the size of the string to be sent, in terms of how many bytes it turns into when converted with java's "modified UTF-8" format.

PuTTY's RAW mode doesn't 'talk' this protocol (in fact, nothing does, except DataOutputStream).

Here's what I think you're doing:

  • You connect with PuTTY in raw mode.
  • The connection appears to get established.
  • You type a sentence using the keys on your keyboard. Then you hit enter.

Voila. You just made up a protocol. You 'spoke a language'.

I think I know enough of PuTTY to know what this looks like (but make sure you set that up in RAW mode!):

  • PuTTY just starts tossing the text that appears as you hit keys through a UTF-8 encoder and starts sending those bytes across the line as you type.
  • When you hit enter, PuTTY encodes either the character \n or the characters \r\n as UTF-8 sends it (That's 0x0D, 0x0A in bytes).
  • PuTTY does not close the connection until you close the window and hit OK on the popup that shows up.

There. That's your protocol. It's simple. readUTF is not the same protocol, and is not configurable, therefore cannot be used to read this protocol.

This will read it fine, though:

try (Socket s = socketServer.accept();
  var rawIn = s.getInputStream();
  var in = new BufferedReader(new InputStreamReader(rawIn, StandardCharsets.UTF_8))) {

  while (true) {
    String cmd = in.readLine().trim();
    if (cmd == null) break; // client hung up
    if (cmd.isEmpty()) continue; // sent an empty line
    System.out.println("Client sent: " + cmd);
  }
}

Give that a shot with PuTTY in raw mode, and I bet your server will start echoing exactly what you're typing. Even if you're typing unicode snowmen, Müller, or emojis. Try that: Type this stuff: ß, é, and ☃ and 😀. See what falls out. Note that if the wrong stuff falls out, your java app is also speaking in protocols to many layers before it turns into pixels and beams into your eyeballs; it may be possible your java app is really reading a unicode snowman properly and sending it properly, but a misconfigured terminal is messing it up. Something to keep in mind.

Upvotes: 1

Related Questions