Using ServerSocket and BufferedReader.readLine() the input is only read once the line is terminated on the client side, is there anyway to read the input, and send an output before the line is terminated?
ServerSocket SRVSOCK = new ServerSocket(35553);
while(true){
Socket SOCK = SRVSOCK.accept();
InputStreamReader IR = new InputStreamReader(SOCK.getInputStream());
BufferedReader BR = new BufferedReader(IR);
String MESSAGE = BR.readLine();
PrintStream PS = new PrintStream(SOCK.getOutputStream());
System.out.println(MESSAGE);
PS.print("Returned Successfully");
}
You could go with bufferedReader.read() which will return the values one character at a time
StringBuffer response = "";
while ((c = bufferedReader.read()) != -1)
{
//Since c is an integer, cast it to a char. If it isn't -1, it will be in the correct range of char.
response.append( (char)c ) ;
}
If you go with this method, it's up to you to figure out when you can start parsing the result and send a reply.
Have a look at this answer.
You can read input into a buffer whose size is under your control.
(Apologies if linking to self answers is considered bad. Felt like reusing it. We are devs after all.)
Related
I am attempting to retrieve the byte values from an InputStream which is being sent to the socket. I have used many ways but it always prints me the address of the byte array instead of its contents.
Below is my code for Client and Server. When a packet is sent from the client to the server, the server instantiates a new Thread to handle the connection. So slaveSocket is the socket I want to use for this.
public class TCPClient {
public static void main(String[] args) throws IOException{
Socket socket;
String address;
int port;
String userInput;
String serverResponse;
PrintWriter out;
BufferedReader in;
//read characters from user
BufferedReader stdIn;
if (args.length != 2) {
System.err.println("Usage: java EchoClient <address> <port>");
System.exit(1);
}
byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
address = args[0];
port = Integer.parseInt(args[1]);
try{
//connect socket to server
socket = new Socket(address, port);
//Construct PrintWriter to write objects to the socket
out = new PrintWriter(socket.getOutputStream(), true);
//Construct BufferedReader to read input from the socket
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//Another reader to read characters typed by the user
stdIn = new BufferedReader(new InputStreamReader(System.in));
//scanner for menu option
Scanner scanner = new Scanner(System.in);
int menuOption;
System.out.println("Press 1 to read from file or 2 to write to file");
menuOption = scanner.nextInt();
if (menuOption == 1){
String filename = "";
String text = "";
System.out.println("Enter file name");
filename = scanner.next();
byte[] packet = new byte[512];
//Constructing the RRQ Packet
//Ading the OPCODE
packet[0] = 1;
//adding the filename
filename.getBytes(Charset.forName("UTF-8"));
byte[] filenameB = filename.getBytes(Charset.forName("UTF-8"));
System.arraycopy(filenameB,0,packet,1, filenameB.length);
//adding a 0
packet[filenameB.length +1] = 0;
//adding the mode
System.arraycopy(mode,0,packet,1+filenameB.length+1,mode.length);
//adding the last 0
packet[1+filenameB.length+1+mode.length+1] = 0;
out.println(packet);
}else if(menuOption == 2){
}
socket.close();
}catch(UnknownHostException e){
System.err.println("Dont know about host" + address);
System.exit(1);
}catch(IOException e){
System.err.println("Couldnt get I/O for the connection to " + address);
System.exit(1);
}
}
}
public class TCPServer {
public static void main(String[] args) throws IOException{
//port of the server
int port = 10000;
//Socket objects
ServerSocket masterSocket;
Socket slaveSocket;
//instantiate the server socket
masterSocket = new ServerSocket(port);
System.out.println("Server Started");
boolean flag1 = true;
while(true){
slaveSocket = masterSocket.accept();
System.out.println("Accepted TCP connection from: " +
slaveSocket.getInetAddress() + ", " + slaveSocket.getPort() + "...");
System.out.println("Initialising new Thread...");
new TCPServerThread(slaveSocket).start();
}
}
}
public class TCPServerThread extends Thread{
private Socket slaveSocket = null;
public TCPServerThread(Socket socket){
super("TCPServerThread");
this.slaveSocket = socket;
}
public void run(){
byte[] ClientPacket = new byte[512];
PrintWriter socketOutput;
InputStream socketInput;
try{
//send packet to client
socketOutput = new PrintWriter((slaveSocket.getOutputStream()), true);
//read packet from client
socketInput = new DataInputStream(slaveSocket.getInputStream());
ClientPacket = socketInput.readAllBytes();
System.out.println(new String(ClientPacket, StandardCharsets.UTF_8));
}catch (IOException e){
System.err.println(e);
}
}
}
You've hopelessly overengineered this.
Writer and Reader do character input and output. InputStream and OutputStream do byte input and output.
You turn byte-based stuff (and in the end, network ports are byte based, not character based) into character based stuff in dangerous ways and then are attempting to read and write bytes into and out of the char-based things.
The solution is simple. Just stop doing that. You have byte-based stuff, there is absolutely no need to involve Reader and Writer.
A bunch of lines that cause problems:
out.println(packet);
PrintStreams are debug aids. You can't use them for any of this. For example, this line will print newlines (definitely not something you'd want in a byte based stream system!), and will print 'objects' - it does that by invoking the .toString() method, and the toString method of arrays are mostly useless. That explains why you see what you see. This is not how you send bytes. You cannot send bytes to a PrintStream (which is a confused mess, as it tries to let you send characters to a byte based system. As I said, you use it for debugging and nothing else. You should not be using it here at all).
new InputStreamReader(socket.getInputStream())
This is dangerous. You're turning a byte based system (InputStream) into a char-based one (Reader) and this always means somebody is making an explicit, 'out of band' (not based on the data in that stream) decision about charset encoding. In this case, as per the docs of InputStreamReader, you get the 'platform default'. Starting with JDK18, it's guaranteed to be UTF-8 fortunately, but before that, who knows what it is. You never want to call this constructor to avoid the confusion. new InputStreamReader(socket.getInputStream, StandardCharsets.UTF_8).
Mostly, though, don't make a reader in the first place. You have no interest whatsoever in reading streams of characters, you just want bytes.
If you have smallish strings and the information about where they 'end' is done 'out of band' (example: The size in bytes (not characters) is sent first, then X bytes that are the string, UTF_8 encoded), you can just read that in as bytes, and then make a string off of that, bypassing any need for Readers and Writers. Reader and Writer is useful only if the entire stream is all character based, or if you have huge strings (hundreds of megabytes) where their end can only be surmised by interpreting the data as characters first. (Mostly, those are horrible protocols that shouldn't be used).
//Construct PrintWriter to write objects to the socket
No, you can't write objects to sockets. Objects aren't bytes. You can write bytes to a socket; some objects will let themselves be turned into bytestreams but this is decidedly not a trivial job, and PrintWriter can't do it at all.
catch (IOException e) { System.err.println(e);
Most code has no reasonable route to 'deal' with them, but the solution to that is to throw them onwards. Not to catch the exception, print a note of despair, and just keep going on like nothing happened. Doing it right is also less code, so, win-win.
stdIn = new BufferedReader(new InputStreamReader(System.in));
//scanner for menu option
Scanner scanner = new Scanner(System.in);
You're making 2 different ways to read standard input. That makes no sense. Pick one.
I tried to fix it for you:
public class TCPClient {
public static void main(String[] args) throws Exception { // always throw Exception from `main`.
if (args.length != 2) {
System.err.println("Usage: java EchoClient <address> <port>");
System.exit(1);
return; // Always return after System.exit.
}
byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
String address = args[0];
int port = Integer.parseInt(args[1]);
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\\R"); // split on newlines, not spaces. So much more logical.
// resources need to be safe-closed - use try-with!
try (var socket = new Socket(address, port);
var out = new BufferedOutputStream(socket.getOutputStream());
var in = socket.getInputStream()) {
System.out.println("Press 1 to read from file or 2 to write to file");
int menuOption = scanner.nextInt();
if (menuOption == 1) {
System.out.println("Enter file name");
String filename = scanner.next();
//Constructing the RRQ Packet
//Adding the OPCODE
out.write(1);
out.write(filename.getBytes(StandardCharsets.UTF_8));
out.write(0);
// The above is dangerous; NUL (0) is actually a valid char.
// A proper way to send strings is to send length in bytes
// first. I'll leave it to you to fix your protocol.
// If it can't be fixed, scan for `\0` chars and get rid of em.
//adding the mode
out.write(mode);
out.write(0);
}else if (menuOption == 2) {
}
}
}
Sending bytes one at a time can be slow (as it ends up sending an entire packet) but can also be useful - the data is just sent, instead of waiting perhaps for a long time for more data. In your case, you send it all in one go, so sending it all off very quickly is not a good idea. Hence, why the outputstream is wrapped in a BufferedOutputStream, which fixes that. You can always use flush() to force sending now, in case you want to keep the connection open (close(), naturally, also flushes).
It's fine if you want to use a byte[] packet instead, but it seems convoluted and unneccessary here. out.write(someByteArray), where out is an OutputStream of some sort, works fine. out.println(byteArray), where out is a Writer of some sort, or a PrintStream - doesn't work at all. (It would take the array, call toString() on it which isn't useful, then convert those bytes using some unknown charset and send that, and none of that is what you want).
You'll need to similarly eliminate PrintStream and the like from your server code.
I have developed a small java client that suppose to communicate with a tool installed on unix-server.
I'm working with Socket first time so could do something wrong. I am also limited to Java 6.
In brief code looks like this
I use Socket to establish connection.
Socket socket = new Socket();
SocketAddress socketAddress = new InetSocketAddress(endpoint, port);
socket.connect(socketAddress, 5000);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Here is how I send message
out.println("Hello");
out.flush();
And here is how I read response
String res = "";
int letter;
while(letter = in.read() != -1) {
char c = (char) letter;
res += c;
}
F.x. If I send a message "Hello", I will receive answers with 2 lines (see example below)
> Hi there
> My name is Robot
The things stuck when I read next character after "Robot\n", I expected that in.read() != -1 will be true and thus it will stop itself, but that is not a case and instead everything just stuck.
What could be the reason to this and how to solve? Thanks.
Please let me know if I need to provide more information.
I had to close my output writer before reading from it, otherwise it blocks.
if (!socket.isOutputShutdown()) {
socket.shutdownOutput();
}
The answer came from a person who later deleted own answer :-/
I am using following code
OutputStream out = socket.getOutputStream();
PrintWriter output = new PrintWriter(out);
#SuppressWarnings("unused")
byte[] b = message.getBytes("US-ASCII");
String convertedString = Normalizer.normalize(message, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
Log.d("ASCII", convertedString);
output.println(convertedString);
Log.e("TCP Message sent", convertedString);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//read line(s)
String st = input.readLine();
Log.e("TCP Message received", st);
But I am stuck at String st = input.readLine(); It just hangs there and never proceed to next line. Is this correct way of sending message using TCLP And then get response?
You are getting stuck at readLine() because the connection is still open - android is still waiting for more data to come in, and so can't move on.
This is what I did, but whether this is viable may depend on the protocol you are adhering to.
StringBuilder builder = new StringBuilder();
String line;
while ((line = input.readLine()) != ""){
builder.append(line);
}
Log.i("data", builder.toString());
What you need is flushing your output stream after sending message by:
output.flush();
or use auto flush by passing true while creating Output Stream like this:
PrintWriter output = new PrintWriter(out, true);
Hope this helps
I have a ServerSocket in Java:
ServerSocket serverSocket = new ServerSocket(1000);
which accepts a clientSocket:
Socket clientSocket;
clientSocket = serverSocket.accept();
Up until now I was reading the input like this:
BufferedReader clientSocketInputStream = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
while ((inputLine = clientSocketInputStream.readLine()) != null){
String message = inputLine;
// Hack the computer connecting to this one after here
However if the text sent is something like
String stringToBeSent = "Hello\nHowareyou";
then I am in trouble. Because I need this text as it is. 2 different Strings do not help me.
How can I read it as it is?
Thanks.
Two options:
Put the multiple strings back together.
StringBuilder sb = new StringBuilder()
while ((inputLine = clientSocketInputStream.readLine()) != null)
{
String message = inputLine;
sb.append(message);
sb.append('\n');
}
String message = sb.toString();
Read an array of bytes instead of String, using a BufferedInputStream instead of a BufferedReader. Then smash the whole byte array into the String constructor with a valid charset. This will require you to know how many bytes the String will be.
You could simply read character by character. http://docs.oracle.com/javase/7/docs/api/java/io/BufferedReader.html#read()
instead of read line.
BufferedReader reads characters until it gets "\n", "\r" or "\r\n". You can read character by character but it does not change anything anyway as how would you determine which new line characater shows the real new line?
I have a client which is connecting to a server. The server and the client exchange datas in string format. The problem is that, the server does not take '\n' character at the end of the message and because of this the client blocked in readLine() method. Unfortunately the server-side can't be changed. How can read from stream that kind of message which does not have '\n' at the end?
My client code:
public class json
{
private static Socket socket;
public static void main(String args[])
{
String sendMessage = "";
Gson gson = new Gson();
JSON_package authentication = new JSON_package();
authentication.setType("Identifying");
authentication.setSource("exampleClient");
Package_Parser pp = new Package_Parser();
sendMessage = gson.toJson(authentication);
sendMessage = authentication.buildPackage(sendMessage);
try
{
String host = "host_address";
int port = port_number;
InetAddress address = InetAddress.getByName(host);
System.out.println("Connecting.");
socket = new Socket(address, port);
System.out.println("Connected.");
//Send the message to the server
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
bw.write(sendMessage);
bw.flush();
System.out.println("Message sent to the server : "+sendMessage);
//Get the return message from the server
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
StringBuffer buffer = new StringBuffer();
String message = br.readLine();
message = pp.Parser(message);
System.out.println("Message received from the server : " +message);
}
catch (Exception exception)
{
exception.printStackTrace();
}
finally
{
//Closing the socket
try
{
socket.close();
System.out.println("Closed.");
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
You can try to use ready and read(char c) methods.
Here is one example:
StringBuffer sb = new StringBuffer();
while (br.ready()) {
char[] c = new char[] { 1024 };
br.read(c);
sb.append(c);
}
The easiest solution is to read the message character per character, but the main problem here is to know when the message is complete. In a line-oriented protocol this is simple, the newline that was sent is the "separator" between messages. Without, there are two possible situations where this problem is easy to solve:
Case 1: the message always has a fixed character at the end, that can't occur in the message
// let's pretend ! is the end of message marker
final char endMarker = '!';
// or of course StringBuffer if you need to be treadsafe
StringBuilder messageBuffer = new StringBuilder();
// reads to the end of the stream or till end of message
while((value = br.read()) != -1) {
char c = (char)value;
// end? jump out
if (c == endMarker) {
break;
}
// else, add to buffer
messageBuffer.append(c);
}
// message is complete!
String message = messageBuffer.toString();
Case 2: the message has a fixed length
// let's pretend message is always 80 long
int messageLength = 80;
StringBuilder messageBuffer = new StringBuilder();
int charactersRead = 0;
// reads to the end of the stream or till end of message
while((value = br.read()) != -1) {
char c = (char)value;
// end? jump out
if (++charactersRead >= messageLength) {
break;
}
// else, add to buffer
messageBuffer.append(c);
}
// message is complete!
String message = messageBuffer.toString();
In both cases you'll have to add some code to check the sanity of what you received, you may have received EOF during read.
If there is no obvious message separator and message have a variable length it will be a lot harder.
The point of readLine() is to read data where it really is guaranteed that the input will end with a newline. Generally, when parsing input, there has to be some token - some character or combination of characters in the input, which you can use to decide whether to
Wait for more input
Do something with the information you've gotten already
And possibly decide whether to go back to waiting for more input afterwards
If you cannot guarantee that a newline will be sent, then readLine() is the wrong tool for the job. Use something like the character-array read method of InputStreamReader instead. You will have to iterate the array of characters you read in, and figure out when you have enough input to work with. You could also use the one-character-at-a-time read() method of InputStreamReader which will result in simpler but probably less efficient code.
If you use the character-array version of read(), and if you go back to collecting input after parsing some, don't forget to put whatever is left over when you do get enough to parse back into the queue to handle on the next round.