I have 2 sockets and I am using BufferedReader around it's InputStreams. What I am trying to do is take all input from the first socket and send it to the other socket (and visa versa).
The problem is that if the first one does not send a message, it will still block on the first readLine() even though the 2nd socket has already sent some data and is ready. I would like to continue with this simple approach of using no additional threads.
Here's some code that I wrote up, as you can see I have 2 BufferedReaders (in0 and in1) , the program gets stuck at in0.readLine() (blocking).
private void network()
{
PrintWriter out0 = null, out1 = null;
BufferedReader in0 = null,in1 = null;
try{
//clients[] is an array of Socket[2]
in0 = new BufferedReader(new InputStreamReader(clients[0].getInputStream()));
out0 = new PrintWriter(clients[0].getOutputStream(), true);
in1 = new BufferedReader(new InputStreamReader(clients[1].getInputStream()));
out1 = new PrintWriter(clients[1].getOutputStream(), true);
} catch (IOException e) {
System.out.println("Accept failed: 4445");
System.exit(-1);
}
int count = 1;
while(true)
{
System.out.println("network check loop # " + count);
++count;
String nextMessage = null;
try {
if( (nextMessage = in0.readLine()) != null)
{
this.relayMessage(nextMessage,out1);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Middle of network check loop");
nextMessage = null;
try {
if((nextMessage = in1.readLine()) != null)
{
this.relayMessage(nextMessage,out0);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
How can I just skip that statement if in0 is not ready to give me some data? I have seen BufferedReader's ready() method and have attempted to use in0.ready() && readLine() but this causes an infinite loop as neither of the bufferedreaders appear to ever be 'ready'. As well, I am certain that the messages being sent over the socket end in newline characters so readLine() should process correctly!
Any ideas?
Try to use setSoTimeout to put a timeout on your read(), then you just need to catch the SocketTimeoutException if the timer has expired.
Here break and continue keywords are your friends.
The simplest approach is to use two threads. This way you don't have to write your own scheduling code to determine which thread should be running. BTW: The code to copy from one socket to another is the same in each thread, reducing duplication.
To manage your threads I would use an ExecutorService which will make shutting downt eh threads easier.
Related
I am trying to create an application where my client program reads the message from echo server. I'm trying to use Future to read the message from the server that will have a larger size than my allocated bytebuffer. My thought is to read into a outputstream until end-of-stream. However I think the code will stuck at readBytes = socket.read(buffer).get() at the last try becuase there will be nothing left to read from the socketchannel and Future will be blocked here.
Please let me know how to fix this or another way around.
public String receiveMessage(){
String message = "";
if (socket.isOpen()) {
try {
ByteBuffer buffer = ByteBuffer.allocate(2);
Future<Integer> readResult = socket.read(buffer);
int readBytes = readResult.get();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
while (readBytes != -1) {
outputStream.write(buffer.array());
buffer.clear();
readBytes = socket.read(buffer).get();//stuck at here
}
byte result[] = outputStream.toByteArray();
System.out.println(result);
message = new String(result, Charset.defaultCharset()).trim();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return message;
}
'''
As this is an assignment, I believe I am not supposed to provide a completely functional answer, but here are some hints to guide you:
Oracle has many great Java tutorials including the one on sockets.
For asynchronous execution, I recommend creating a new java.lang.Thread object. Threads and Concurrency (unsurprisingly) also has a tutorial by Oracle. You may have something like the following, which I found useful when experimenting with Java sockets.
// write to server
Socket socket = //...
String message = //...
try (PrintWriter writer = new PrintWriter(socket.getOutputStream(), false)) {
writer.println(message);
// will auto-flush on '\n' (newline character) if 'false' in constructor is changed to
// true or omitted (look at PrintWriter documentation)
writer.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// read from server
Socket socket = //...
try (BufferedReader reader = new BufferedReader(new InputReader(socket.getInputStream()))) {
// TODO
} catch (IOException ioe) {
ioe.printStackTrace();
}
// pipe input stream to output stream
// Perhaps you want what comes from the server to go directly into stdout
Socket socket = //...
new Thread() {
#Override
public void run() {
try {
socket.getInputStream().transferTo(System.out);
// socket input stream is at end of stream, but not necessarily closed
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
Note that using InputStream#transferTo(OutputStream) will not terminate until the InputStream is closed, which is why you might want to execute it in its own thread.
Also, be careful about the above code segment: if you send a message through a socket using a PrintWriter then immediately close the PrintWriter, the PrintWriter will try to close the underlying OutputStream. Once that closes, it will generally try to close the Socket (whose OutputStream was being written to by the PrintWriter), and no more communication can be done through that socket (which will lead to a BrokenPipeException on attempted further use). So, perhaps try to send a message using newline characters as delimiters or something similar, which would be convenient for using a BufferedReader.
I have a buffered reader reading from a socket line by line. So this works fine. However this looks pretty low level to me and a telnet client is not able to close the connection sending a ctrl + c. So I am wondering if there is some nicer implementation of a stream reader? I.e. the whole tread and loop could easily be abstracted away and just call lambda functions on read, close and error. Or what is the best practice here?
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
new Thread(() -> {
while (true) {
try {
String readLine = bufferedReader.readLine();
// if readline is null then the client just closed connection
// if there is something in the buffer and the clients close the connection
// raadLine returns with anything left in the buffer up until the client left
// and returns a second time with null
if (readLine == null) {
logger.info("client closed connection");
socket.close();
disconnectAll();
break;
} else {
for (Listener listener : listeners) {
listener.messageReceived(this, readLine);
}
}
} catch (IOException | IllegalAccessException | InvocationTargetException e) {
logger.error(e.getMessage(), e);
try {
socket.close();
} catch (IOException e1) {
logger.error(e1.getMessage(), e1);
} finally {
disconnectAll();
break;
}
}
}
}).start();
I think your base problem is that sending Ctrl-C doesn't close a stream, Ctrl-D does. Edit: Ctrl-Z on Windows.
The remainder of the question really belongs to https://codereview.stackexchange.com/, but here goes.
Firstly, you are closing a socket that was opened outside.
Secondly, you shouldn't create Thread objects, but use an ExecutorService.
Third, I'd recommend using try-with-resource to ensure everything closes automatically.
Fourth, you can use the read line in your while statement instead of using while(true) - break.
This gives you something like
ExecutorService readerExecutor = Executors.newSingleThreadExecutor();
public startReadingSocket(Supplier<Socket> createSocket, Consumer<String> lineHandler, Consumer<Exception> excHandler, Runnable cleanUp) {
readerExecutor.submit(() -> {
String readLine;
try (Socket s = createSocket.get();
InputStreamReader isReader = new InputStreamReader(s.getInputStream());
BufferedReader reader = new BufferedReader(isReader)) {
while (readLine = reader.readLine() != null) {
lineHandler.accept(readLine);
}
System.out.println("client closed connection.");
} catch (Exception e) {
excHandler.accept(e);
} finally {
cleanUp.run();
}
}
}
And you can run that via
startReadingSocket(() -> new Socket(host, port),
line -> listeners.forEach(l -> l.messageReceived(this, line)),
ex -> logger.error(ex.getMessage, ex),
this::disconnectAll);
Now this is Java 8, with previous versions you'd need to create interfaces and anonymous classes for the lambdas.
I am running another jar with this code:
(I am updating a gui in some parts , so dont feel confused.)I get an IO Exception (Stream Closed) here:
if((line = readr.readLine()) != null){
Thats the full code:
if(!data.serverStarted()){
try{
data.updateConsole("Starting server!");
String fileDir = data.dir + File.separator + "craftbukkit.jar";
Process proc = Runtime.getRuntime().exec("java -Xmx2048M -jar "+"craftbukkit.jar"+" -o true --nojline");
data.setOutputStream(proc.getOutputStream());
InputStream is = proc.getErrorStream();
}catch(IOException ex){
ex.printStackTrace();
}
BufferedReader readr = new BufferedReader(new InputStreamReader(is));
data.setServerStarted(true);
String line;
while(data.serverStarted()){
try {
if((line = readr.readLine()) != null){
data.updateConsole(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
readr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}else{
data.updateConsole("You have already started your server!");
}
You have a while loop that closes readr on every pass. The next time it gets to the try block, readr is closed. Perhaps you intended to put the try/catch block around the while loop?
You are closing the reader inside the loop that reads from it. You need to close it outside of the loop:
try {
String line;
while (data.serverStarted() && ((line = readr.readLine()) != null)) {
try {
data.updateConsole(line);
} catch (IOException e) {
e.printStackTrace();
}
}
} finally {
try {
readr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
I am surprised that this code even compiles.
You declare the actual InputStream is inside the try/catch at the beginning, but that makes it only visible inside that block. So whatever you give to the BufferedReader a few lines below is something else and most likely not what you think it is.
In addition your while(data.serverStarted()) does not check if the stream is still open, and later you only use a single if check (again with no check if the stream is open), so you'll only read one single line at best.
I have a feeling that you had a bad OutOfCoffeeException while writing this code. ;)
I'm having a little trouble with a simple Java server, client application.
Basically the topics says it all: when I do a writeUTF on the server side it only sends every 2nd time it's being executed.
For an example:
Server:
public class Server {
/**
* #param args
*/
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(7777);
Socket client = server.accept();
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(client.getOutputStream()));
DataInputStream in = new DataInputStream(new BufferedInputStream(client.getInputStream()));
while(true) {
for(int i = 0; i < 100; i++) {
out.writeUTF("Test" + i);
out.flush();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
And client:
public class Client {
/**
* #param args
*/
public static void main(String[] args) {
try {
Socket client = new Socket("localhost", 7777);
DataInputStream in = new DataInputStream(new BufferedInputStream(client.getInputStream()));
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(client.getOutputStream()));
while(in.readUTF() != null) {
System.out.println(in.readUTF());
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
And the output in the console looks like this:
Test1
Test3
Test5
Test7
Test9
Test11
What is causing this behavior?
It's because you're discarding data on your client.
When you check while(in.readUTF() != null), you're reading an entry from the stream every time to see if it's non-null. Then you discard this value, and read a new one within the loop. (As well as losing values, this has the bug that with an odd number of total values, the while condition will evaluate to true but the next call to readUTF() would return null within the loop.)
To fix this, you should read the value to a variable, and test this before using it - something like the following:
String value;
while((value = in.readUTF()) != null) {
System.out.println(value);
}
If you don't like the assignment and nullity check as a single expression, you can instead assign value = in.readUTF() initially and at the end of every loop, but personally I find this more error-prone.
This loop
while(in.readUTF() != null) {
System.out.println(in.readUTF());
}
throws away every second string it reads. You read one string while testing for end of file, discard it, then read another one and print it.
You are consuming the data in the while loop evaluation here:
while(in.readUTF() != null) {
System.out.println(in.readUTF());
}
Try this instead:
String line;
while( ((line = in.readUTF()) != null) {
System.out.println(line);
}
If you want your socket in UTF-8
you only need to add "UTF-8" in BufferedWriter and BufferedReader's contstructor
here is the sample
this.basicReader = this.Client.getInputStream();
this.basicWriter = this.Client.getOutputStream();
this.Reader= new BufferedReader(new InputStreamReader(basicReader,"UTF-8"));
this.Writer= new BufferedWriter(new OutputStreamWriter(basicWriter,"UTF-8"));
how to send data
this.Writer.write("data"+"\r\n");
this.Writer.flush();
and your main problem is your client didnt respond a msg when your client receive a msg
server to client
client to server
server to client
like this, try it.
I got a socket listener which keep listening for data. The problem now is that the client which send data will finally close the connection by itself. Based on my codes below I am wondering do I still need to perform this part of the codes where it does writeBuffer.close();?
Should I remove the final part and just put the socket closing the catch?
public void run()
{
BufferedWriter writeBuffer = null;
BufferedReader readBuffer = null;
String message="";
try {
writeBuffer = new BufferedWriter(new OutputStreamWriter(receivedSocketConn1.getOutputStream()));
readBuffer = new BufferedReader(new InputStreamReader(receivedSocketConn1.getInputStream()));
int m = 0, count=0;
int nextChar=0;
while ((nextChar=readBuffer.read()) != -1)
{
message += (char) nextChar;
if (nextChar == '#')
{
System.out.println("\n\nSending PA : "+message);
writeBuffer.write("$PA\r\n");
writeBuffer.flush();
message="";
}
}
}
catch (Exception ex)
{
System.out.println("MyError:Exception has been caught in in the main first try");
ex.printStackTrace(System.out);
}
/*finally
{
try
{
if ( writeBuffer != null )
{
writeBuffer.close();
}
else
{
System.out.println("MyError:writeBuffer is null in finally close");
}
}
catch(IOException ex)
{
ex.printStackTrace(System.out);
}
}*/
}
It's always a good idea to explicitly close the connections you're using. Think about it, it might be possible that the client never closes the connection (of course, then you'd have to implement some kind of timeout mechanism that closes the connection on the server side after a certain amount of time, but that's a different matter).
My point is - it never hurts to be careful, and manage your resources in a conservative fashion.