I'm currently writing a client-server application where data should be transferred to the server to process the data. For building the connection I use ServerSocket and Socket and for sending the data I use OutputStream + ObjectOutputStream on the client-side and InputStream + ObjectInputStream on the server-side. The connection is currently running on localhost.
The object I try to transfer is a serializable class that only contains String parameters.
The problem I'm facing now is that readObject() immediately throws an EOFException as soon as the OutputStreams of the client are initialized (which leads to an initialization of the InputStreams of the server at the same time) instead of waiting for input from the client.
I send the data from the client using this code:
public void send(DataSet dataSet) {
if (!clientStreamsEstablished) {
initiateClientStreams();
}
try {
out.writeObject(dataSet);
} catch (IOException e) {
e.printStackTrace();
}
}
This method is only called when I hit the "submit"-button in the UI so it will not be executed on start of the application.
The data is currently (already tried a ton of other approaches with and without while() loop etc., etc.) read on the server using this method:
private void waitForInput(ObjectInputStream in, InputStream listeningPort) {
boolean dataReceived = false;
DataSet dataSet = null;
System.out.println("waiting ...");
while (!dataReceived) {
try {
Object temp = in.readObject(); // <-- EOFException is thrown here
boolean test = false;
if (temp instanceof DataSet) {
dataSet = (DataSet) temp;
}
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
break;
}
}
System.out.println("Test 2: " + dataSet.toString());
if (dataReceived) {
waitForInput(in, listeningPort);
}
}
As soon as the client thread on the server reaches this line (see code-comment above) I get this stacktrace:
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2626)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1321)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
at com.labdashboardserver.ClientThread.waitForInput(ClientThread.java:53)
at com.labdashboardserver.ClientThread.run(ClientThread.java:43)
Exception in thread "Thread-2" java.lang.NullPointerException
at com.labdashboardserver.ClientThread.waitForInput(ClientThread.java:65)
at com.labdashboardserver.ClientThread.run(ClientThread.java:43)
The reason for the second part of the stacktrace containing the NullPointerException is apparent as due to the EOFExcpetion the dataSet never is initialized.
However from my point of view readObject() should block and wait for the client to send ANY data before starting to read it and throw an EOF. I feel like I read through half of Stack Overflow and other forums searching for an answer but the articles I found only discuss reading files or only immediate temporary streams which are closed afterwards.
Edit
I initialize the connection before calling the UI in my main method:
public static void main(String[] args) {
connector = new LabConnector();
connector.run();
if (connector.getConnectionEstablished()) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
frame = new LabUI();
frame.setVisible(true);
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"Error Message:\n" + e.getMessage() + "\nProgram shutting down!", "Critical Error", 0);
}
}
});
}
While in the LabConnector class I initialize the streams and connection like this:
public void run() {
try {
establishConnection();
} catch (UnknownHostException e) {
retryConnection(e);
} catch (IOException e) {
retryConnection(e);
}
if (connectionEstablished) {
initiateClientStreams();
}
}
private void establishConnection() throws UnknownHostException, IOException {
client = new Socket(HOST_IP_ADRESS, HOST_PORT);
JOptionPane.showMessageDialog(null, "Connected to Server!");
connectionEstablished = true;
}
private void initiateClientStreams() {
try {
sendingPort = client.getOutputStream();
out = new ObjectOutputStream(sendingPort);
clientStreamsEstablished = true;
} catch (IOException e) {
e.printStackTrace();
}
}
Related
an app in PC using JAVA io.socket which will sends json to a server device ESP8266on TCP on LAN network
when you are connected and when disconnect sequence is executed from java it self everything is ok .
java is client and device is server , when device cuts the connection (here lets use Hercules on localhost) the java program will not being noticed and when i try to write with outputstreamwriter it dose not trig an exception , exception will be executed after at least two writes to socket after the server is being disconnected and the last two writes which was not being received by server will return success! in java. i have read other programmers use a byte send to see if connection is still alive . the same problem is there too . if i send two write each 20 seconds time in between its going to be 60 seconds before java realize server is disconnected and if i send every 1 second is going to be a lot of ATcommand interrupts for nothing .
here is my code:
public boolean Write(String data){
System.out.println("StartSending");
if(TESocket.Connected)
{
Thread write = new Thread(new Runnable() {
#Override
public void run() {
try
{
outputStreamWriter.write(data);
outputStreamWriter.flush();
}
catch (IOException e)
{
TESocket.Connected=false;
System.out.println("Faild");
System.out.println(e.getCause());
}
}
});
write.start();
return true;
}
else
{
System.out.println("Not Connected");
return false;
}
}
The TESocket is class which handles Socket using Runnable and Connected is a static boolean since there is just one socket at a time here is the connect method
public boolean Connect(){
Thread connect = new Thread(new Runnable() {
#Override
public void run() {
try {
socket= new Socket("127.0.0.1",Integer.parseInt(port));
if(socket.isConnected())
{
inputStreamReader = new InputStreamReader(socket.getInputStream());
outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());
TESocket.Connected=true;
System.out.println("Connected");
}
} catch (IOException e) {
System.out.println("Faild to Connect");
e.printStackTrace();
}
}
});
connect.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(TESocket.Connected)
return true;
else
return false;
}
how can i be noticed if server is out of reach with immediately after sending the write? or is there eny event for noticing that ? maybe some king of asynchronous socket? like it was in QT (Signal Slot for Disconnect)
well By reading method and setting a timeout solved this problem , thanks to reading some post
in read if i am still connected and not receiving any data time out will be thrown which bring me back to the first lie of the while loop
if i receive data i will be bigger than zero which reader will read data and flag the rec=true so the disconnect sequence dose not take in place
if i don't receive any data and timeout dose not occurs the rec=false and exception will not be thrown so the program will o to disconnect routine
setting the timeout to 1 millisecond makes it real-time (proportional to my work) in deadline detection
public boolean Read()
{
if (TESocket.Connected)
{
try {
socket.setSoTimeout(1000);
} catch (SocketException e) {
System.out.println("Problem Timeout");
e.printStackTrace();
}
Thread read = new Thread(new Runnable() {
#Override
public void run() {
int i;
boolean rec=false;
while (true)
{
char[] reader = new char[250];
try {
//while(!inputStreamReader.ready());
i=inputStreamReader.read(reader);
if(i>0) {
System.out.println(reader);
rec=true;
}
// Thread.sleep(2500);
if(!rec)
{
System.out.println("Disconnected");
TESocket.Connected=false;
inputStreamReader.close();
outputStreamWriter.close();
socket.close();
break;
}
rec=false;
}
catch (IOException e)
{
System.out.println("Connected");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
read.start();
}
return true;
}
I am getting EOFException from the following code:
if (!(in.read() == -1))
{
CANDataInfo canData = (CANDataInfo) in.readObject();
System.out.println(canData.toString());
bw.write(canData.toString());
}
else
{
System.out.println("in.read() == -1 "+in.readObject());
jLab0x28.setText("No more bytes to read ");
}
I am doing an Socket programming where server is sending continuos data to client at some interval. The Data whichis passed from server to client in via socket is of type CANDataInfo object which i have developed. At client side while i am printing the data i am getting exception. Since object's read is always -1 i am not able to log the data on some file.
Server Side Code:
private ServerSocket server = null;
private Socket client = null;
private ObjectOutputStream out;
public static final String TAG = "APP1";
private void structureData(CANDataInfo canDataInfo)
{
try
{
if(server == null)
{
server = new ServerSocket(38301);
server.setSoTimeout(0);
}
client = server.accept();
Log.e("Server ", ""+client.isConnected());
Log.e("Data ", ""+canDataInfo.toString());
if(!client.isConnected())
{
Log.e("Server ", "client.isConnected() "+client.isConnected());
server.close();
}
out = new ObjectOutputStream(client.getOutputStream());
out.writeObject(canDataInfo);
out.close();
}
catch (Exception ex)
{
Log.e(CANManagerSetUp.TAG, "" + ex);
}
}
Client Side Code {Not a clean solution, Refer Answer from EJP}
package com.cnh.socket.client;
import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import javax.swing.JLabel;
import cantest.setup.CANDataInfo;
public class ThreadListener
{
Socket client = null;
ObjectInputStream in = null;
ListenFor0X28 runnableListenFor0X28 = null;
boolean continueMe;
public class ListenFor0X28 implements Runnable
{
JLabel jLab0x28;
public ListenFor0X28(){}
public ListenFor0X28(boolean stop, JLabel jLab0x28)
{
continueMe = stop;
this.jLab0x28 = jLab0x28;
}
public void run()
{
while(continueMe)
{
try
{
client = new Socket("localhost", 38301);
in = new ObjectInputStream(client.getInputStream());
if(client.isConnected())
{
jLab0x28.setText("Connected to Server");
appendFile(continueMe, jLab0x28, client);
}
else
{
System.out.println("Client is trying to connect");
jLab0x28.setText("Client is trying to connect");
}
}
catch(Exception ex)
{
ex.printStackTrace();
System.err.println("Before Append "+ex.toString());
}
}
}
}
BufferedWriter file = getFile("C:\\ISSUE124_Resolved.txt");
private void appendFile(boolean continueMe, JLabel jLab0x28, Socket client)
{
try
{
if(!client.isClosed())
{
try
{
CANDataInfo canData = (CANDataInfo) in.readObject();
System.out.println(canData.toString());
file.write(canData.toString());
file.flush();
}
catch (EOFException exp)
{
continueMe = true;
System.out.println("A Stream has finished "+exp.toString()+"\n");
}
catch (ClassNotFoundException exp)
{
exp.printStackTrace();
System.err.println(exp.toString());
continueMe = false;
}
}
if(!continueMe)
{
file.close();
client.close();
in.close();
jLab0x28.setText("Socket is closed "+client.isClosed());
}
}
catch(IOException exp)
{
exp.printStackTrace();
System.err.println("Exception "+exp.toString());
jLab0x28.setText(exp.getMessage());
continueMe = false;
}
}
public BufferedWriter getFile(String path)
{
try
{
File file = new File(path);
if (!file.exists())
{
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
return new BufferedWriter(fw);
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
}
Exception Stack: {Before Resolving}
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.cnh.socket.client.ThreadListener.appendFile(ThreadListener.java:73)
at com.cnh.socket.client.ThreadListener.access$0(ThreadListener.java:65)
at com.cnh.socket.client.ThreadListener$ListenFor0X28.run(ThreadListener.java:48)
at java.lang.Thread.run(Unknown Source)
Data received in unknown format java.io.EOFException
In the client
if (!(in.read() == -1))
{
CANDataInfo canData = (CANDataInfo) in.readObject();
System.out.println(canData.toString());
bw.write(canData.toString());
}
The first line reads one byte from the input stream. This is actually the first byte of the object that was written by the server. Thus the stream is no longer aligned correctly so the following readObject() fails.
You should remove the pointless and erroneous read() call, which is getting your object streams out of sync.
While you're at it, you can also remove all the redundant calls to isConnected(). They aren't doing anything. You seem to have a mania for calling extra methods which mostly don't do anything, or which try to predict the future. Try to taper off.
EDIT As requested I am critiquing not only your client but your server code.
Server:
private void structureData(CANDataInfo canDataInfo)
{
try
{
if(server == null)
The ServerSocket should have been created and configured in the constructor.
{
server = new ServerSocket(38301);
server.setSoTimeout(0);
Zero is the default. Don't assert defaults. Remove.
}
client = server.accept();
Log.e("Server ", ""+client.isConnected());
Logging isConnected() is redundant. Remove. This will always print true. The socket is connected. You just accepted it. If you want to log something useful, log the client socket's remote address.
Log.e("Data ", ""+canDataInfo.toString());
How can there be any data when you haven't read any yet? If this is invariant server-side data, why log it on every accept?
if(!client.isConnected())
{
Log.e("Server ", "client.isConnected() "+client.isConnected());
server.close();
}
This test can never pass, and the code block can never be entered, and if by some miracle it was entered, closing the server socket is a ridiculous response. Remove all this.
out = new ObjectOutputStream(client.getOutputStream());
out.writeObject(canDataInfo);
out.close();
}
catch (Exception ex)
Don't catch Exception. Catch IOException.
{
Log.e(CANManagerSetUp.TAG, "" + ex);
You should log the exception class, its message, and the stack trace. ""+ex does not accomplish that.
}
}
Client:
public class ThreadListener
{
Socket client = null;
ObjectInputStream in = null;
ListenFor0X28 runnableListenFor0X28 = null;
boolean continueMe;
public class ListenFor0X28 implements Runnable
{
JLabel jLab0x28;
public ListenFor0X28(){}
public ListenFor0X28(boolean stop, JLabel jLab0x28)
{
continueMe = stop;
this.jLab0x28 = jLab0x28;
}
public void run()
{
while(continueMe)
{
try
{
client = new Socket("localhost", 38301);
in = new ObjectInputStream(client.getInputStream());
if(client.isConnected())
The client is connected. You just connected it, when you constructed the Socket. And if by some miracle it wasn't connected, calling getInputStream() would already have failed with a SocketException. Remove this test. In general there is far too much testing of things that can't be true or can't be false in your code.
{
jLab0x28.setText("Connected to Server");
appendFile(continueMe, jLab0x28, client);
}
else
{
System.out.println("Client is trying to connect");
jLab0x28.setText("Client is trying to connect");
}
}
The else block is unreachable, and the log message 'Client is trying to connect' is incorrect. Remove the entire block and the else.
catch(Exception ex)
See above. Don't catch Exception. Catch the exceptions the compiler tells you to catch: in this case IOException and the DNS-related ones.
{
ex.printStackTrace();
System.err.println("Before Append "+ex.toString());
See above about how to log exceptions.
}
}
}
}
BufferedWriter file = getFile("C:\\ISSUE124_Resolved.txt");
private void appendFile(boolean continueMe, JLabel jLab0x28, Socket client)
{
try
{
if(!client.isClosed())
{
try
{
CANDataInfo canData = (CANDataInfo) in.readObject();
System.out.println(canData.toString());
file.write(canData.toString());
file.flush();
}
catch (EOFException exp)
{
continueMe = true;
System.out.println("A Stream has finished "+exp.toString()+"\n");
}
catch (ClassNotFoundException exp)
{
exp.printStackTrace();
System.err.println(exp.toString());
continueMe = false;
}
}
if(!continueMe)
{
file.close();
client.close();
in.close();
You don't need to close both the input stream and the socket. Either will do. General practice is to close the outermost writer/output stream if there is one, otherwise the input stream.
jLab0x28.setText("Socket is closed "+client.isClosed());
}
}
catch(IOException exp)
{
exp.printStackTrace();
System.err.println("Exception "+exp.toString());
jLab0x28.setText(exp.getMessage());
continueMe = false;
}
}
public BufferedWriter getFile(String path)
{
try
{
File file = new File(path);
if (!file.exists())
{
file.createNewFile();
}
Here you are (1) testing for file existence and (2) creating a new file.
FileWriter fw = new FileWriter(file.getAbsoluteFile());
Here the operating system will create a new file regardless of what you did above. The exists()/createNewFile() part is therefore a complete waste of time: two system calls that accomplish precisely nothing. Remove them.
return new BufferedWriter(fw);
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
Poor practice. You should let this method throw IOException and not catch it internally, or return null. At present, if this method fails, you will get an instrutable NullPointerException when you go to use its return value.
}
}
I have been working with TCP server/client stuff for a while. I am actully good at UDP programming when it comes to connecting more than one user that is multiple clients. I tried to do the same on a TCP server that i made using Threads but whenever the Thread gets to this piece of code
String reader = (String)in.readObject();
an error is generated and the thread stops executing the code but the thread still runs the program keeping it alive.
Anyway here is the entire source code :
public class TestServer implements Runnable {
private Thread run, streams, connect, receive, send;
private ServerSocket socket;
private Socket conn;
private ObjectInputStream in;
private ObjectOutputStream out;
private boolean running, incomingMessage = false;
private int port;
public TestServer(int port) throws IOException {
this.port = port;
socket = new ServerSocket(port);
console("Server stated on : " + InetAddress.getLocalHost() + " : " + port);
run = new Thread(this, "Run");
run.start();
}
public void run() {
running = true;
connect();
receive();
}
private void connect() {
connect = new Thread("Connect") {
public void run() {
while(running) {
try {
conn = socket.accept();
} catch (IOException e) {
e.printStackTrace();
}
console("You are now connected" + conn.getInetAddress().toString() + " : " + conn.getPort());
try {
setupStreams();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}; connect.start();
}
private void setupStreams() throws IOException {
streams = new Thread("Streams") {
public void run() {
try {
console("Setting up Streams");
out = new ObjectOutputStream(conn.getOutputStream());
out.flush();
in = new ObjectInputStream(conn.getInputStream());
console("Streams are now setup");
incomingMessage = true;
receive.start();
} catch(IOException e) {
e.printStackTrace();
}
}
}; streams.start();
}
private void receive() {
receive = new Thread("Receive") {
public void run() {
while(incomingMessage) {
String message = "";
try {
message = (String) in.readObject();
//This is the only flaw the program
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
console("Client : " + message);
}
}
};
}
private void console(String message) {
System.out.println(message);
}
public static void main(String[] args) {
try {
new TestServer(1234);
} catch (IOException e) {
e.printStackTrace();
}
}
}
FYI am not new to this. The error is caused because the server starts receiving packets even when there are no packets to be received. But because the thread forces it to receive it, i generates the error in the thread and dont know any other way to counter this. So please help. Thanks in Advance.
You shouldn't need 2 threads per connection. One thread is all that's required. After the connection is accepted, pass it to a worker thread to start reading. This can be done in a while loop in the worker thread.
Even though the socket's input stream can be read, the ObjectInputStream() class is more sensitive. If there is any error, its state is corrupted and it can't be used.
while (true) {
try {
Object input = in.readObject();
message = (String) input;
} catch (IOException e) {
e.printStackTrace();
break; //unrecoverable
} catch (ClassNotFoundException e) {
e.printStackTrace();
break; //unrecoverable
}
console("Client : " + message);
}
It's a better design to use a specific message protocol instead of sending serialized Java objects. For example if you are sending Strings like your sample, an InputStreamReader can be used to convert bytes to characters more easily and with less error handling.
These resources would be helpful to you:
https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html#later
Java - Listening to a socket with ObjectInputStream
ObjectInputStream(socket.getInputStream()); does not work
For a chat server project I use netty as server, with the following code in my handler :
public class PacketHandler extends ChannelInboundHandlerAdapter{
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
try {
AbstractClientPacket packet = ClientPacketHandler.handle(in);
if(packet != null && packet.read()){
packet.run();
ctx.write(msg+"\r\n");
}
} finally {
ReferenceCountUtil.release(msg);
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
So, my packet is correctly handled and it works fine, but then, i do ctx.write(msg+"\r\n"); to send back the message to my client, acting like an echo server.
Here is the Client's code :
public class ChatClient {
static Socket socket;
static DataOutputStream out;
static BufferedReader in;
public static void main(String[] args){
try {
initSocket();
String test = "Salut 1";
TestPacket packet = new TestPacket(0x18,test.getBytes());
sendPacket(packet);
while(true){
try {
String message = in.readLine();
System.out.println(message);
} catch (IOException e) {
e.printStackTrace();
}
}
//TEST
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static void initSocket(){
try {
socket = new Socket("localhost",58008);
out = new DataOutputStream(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader((socket.getInputStream())));
} catch (IOException e) {
e.printStackTrace();
}
}
private static void sendPacket(TestPacket p) throws IOException{
out.write(p.getRawData());
out.flush();
}
}
The packet is correctly sent, but i get nothing as reply, and when i stop my server, the client is spamming null because of my while(true), but i don't get my message back, nothing is displayed and i really don't know why.
I can't use netty for the client because this one is just for test purpose, the final client will be written in C# (Unity Engine), so i can't use netty in this one, I have to do it with native socket handling.
EDIT:
According to wireshark, The packet from client is sent but the server answer is not, i don't see the packet From server containing "Salut 1".
You did:
ctx.write(msg+"\r\n");
msg is not a String but a ByteBuf. If you want to append \r\n to the received message, you should do the following:
in.writeByte('\r');
in.writeByte('\n');
ctx.write(in);
Also, because you reused the received message (in) as a response, you should not release it:
// Do NOT call this.
ReferenceCountUtil.release(in);
If you really intended to call ctx.write(msg + "\r\n"), please make sure that your pipeline has StringEncoder.
I started learning networking with the main networking package in JDK, it's pretty simple and easy after a few examples. But now I am interested into making multi-client applications like a chat system.
My structure idea so far is like this:
Connection handler class, which handles incoming connections, and holds the list of clients.
If new connection was found, create a new client object, start it's thread (Client object will implement runnable, so it will start it's own looping service, it will loop for new packets received), and add it to the list.
I create a new thread for each client instead of looping through all clients because the reading from client process stops the whole execution and will wait for the client to send data, which is kinda annoys me and this is my issue there.
I have created a simple console app that receives messages from the client, but now I want to detect disconnections. I read that bufferedReader .read() method returns -1 if user is not connected, so I thought I could loop and do that every number of seconds to every client, but the thing is, the client must send a packet in order to .read() it, so let's say if you do .read() it will wait & stop the whole thread until packet is received, (I think).
This is my current code which gets messages from client:
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
Basically what happens here, I am sending a integer packed from the client, I might have many things to do so therefore I can set many unique packet ID's, so if I want to receive and process a chat message, the packet id is 216, the client sends a int 216, server reads the packet, enters the switch loop of all packet ids and detects if its really 216, if yes it gets the instance of the packed class that handles messages & gets the bytes of the received message like this:
public class Chat implements Packet {
#Override
public void processPacket(Session c) {
String message = readMessage(c);
System.out.println("Message: " + message);
}
private String readMessage(Session c) {
byte[] data = c.readBytes();
String message = null;
try {
message = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return message;
}
}
And this is how I read bytes:
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
Okay my problem:
after adding the is disconnected detection, when I send my message, nothing happens. This is probably due to the .read() it stops and is waiting for a response. BUT if I write a message again, I will get the message in server.
This is my temporary, ugly client:
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 43594);
Scanner r = new Scanner(System.in);
PrintWriter out = new PrintWriter(socket.getOutputStream());
String input;
while(true) {
input = r.next();
if (input != null) {
sendMessage(input, out);
}
}
}
public static void sendMessage(String message, PrintWriter out) {
byte[] encoded = encode(message);
out.write(0);
out.println(encoded + "\n");
out.flush();
}
public static byte[] encode(String s) {
return DatatypeConverter.parseBase64Binary(s);
}
public static String decode(byte[] s) {
return DatatypeConverter.printBase64Binary(s);
}
}
My question is: What is a better way of reading data from client without making the application wait for it and actually loop everytime? OR maybe should I have a new thread for checking if user is online so it's 2 threads per 1 client?
If someone needs my session object (client object):
public class Session extends Thread implements Runnable {
private Socket session;
private Client client;
private PrintWriter out;
private BufferedReader in;
private PacketHandler packets;
private DataInputStream dataIn;
private ConnectionHandler connection;
private final int checkTime = 1600;
private final int maxTime = 22000;
private long lastCheck;
public Session(Socket session) {
this.session = session;
this.client = new Client(this);
try {
this.setStream();
} catch (IOException e) {
e.printStackTrace();
}
this.packets = new PacketHandler(this);
System.out.println("[New session created]: " + session.getRemoteSocketAddress());
}
public void setConnectionHandler(ConnectionHandler c) {
this.connection = c;
}
public void run() {
try {
this.startClientService();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setStream() throws IOException {
this.out = new PrintWriter(this.session.getOutputStream());
this.in = new BufferedReader(new InputStreamReader(this.session.getInputStream()));
this.dataIn = new DataInputStream(this.session.getInputStream());
}
public Client getClient() {
return this.client;
}
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
public String readMessage() {
try {
return this.in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
}
Thanks!
While I don't have time to look over all the code, here are two things that could help you out.
1) Use a defined message header. Define X number of bytes of each message that the client will send to the server. Use these bytes to define how long the message will be, and what type of message it is. The server knows the length and layout of this header, and uses it to process the message in a particular way. Example could be a header of one byte. A value of 1 could be a I'm connected message. 2 could be I'm about to disconnect. 3 could be I'm currently away, and 4 could be an incoming chat message.
2) There are 2 ways you can handle the input. First is to use blocking IO, and create a separate thread to receive messages from each client. I believe this is what you are currently doing. The second is to use non-blocking IO, and have a separate thread iterate over the open sockets and do a read. Non-blocking will check if there is data to read, but if there is not, the thread will continue executing.