So the end result of my program is an updating game client, but what i have so far is a server that is able to accept multiple connections, and a client that connects to the server. this is the code for the client portion:
public void client() {
Socket socket = null;
ObjectInputStream in = null;
ObjectOutputStream out = null;
try {
socket = new Socket(IP, Integer.parseInt(port));
in = new ObjectInputStream(socket.getInputStream());
out = new ObjectOutputStream(socket.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
do {
// have a conversation
try {
message = (String) in.readObject();
System.out.println("\n" + message);
} catch (Exception e) {
System.out.println("\n idk wtf that user sent!");
}
} while (!message.equals("CLIENT - END")); // When the user types "END"
System.err.println("CLOSED!!!");
System.exit(0);
}
and this is the code for the server portion:
public void run() {
// where everything happens
System.out.println("server- connected");
try {
in = new ObjectInputStream(socket.getInputStream());
out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject("hi");
out.flush();
Thread.sleep(5000);
out.writeObject("close");
out.flush();
System.out.println("closed");
} catch (Exception e) {
e.printStackTrace();
}
}
now, i am running into this problem where, when my server sends the object "hi" the client appears to not receive it. i'm not totally sure if it does, but if it is getting it, it isnt printing it out like i wanted. i previously have made a chat program that does this same thing, and i pretty much copied it to this, but it isnt communicating. the most i get is the confirmation that they are connected. im not sure what is going on, but any help would be appreciated! thanks in advance!
create the ObjectOutputStreams before the ObjectInputStreams and flush them immediately after creation.
the constructor of an ObjectInputStream reads the stream header. this stream header is written by the constructor of the ObjectOutputStream (kind of an ugly implementation, but it is what it is). if you construct the OIS's first, they hang waiting for the header bytes.
Related
I have established a connection via sockets between two computers. I have created an own object called "Result" and I can successfully transfer it to the server computer from the client computer.
If I do this socket connection only on my computer then I can receive an object from the server computer as well.
The problem is when I try to receive an object from the server computer. I get error messages and I have the feeling that something is happening to my object that is being sent. If I open a saved (serializable) Result object on my own computer in notepad then I get a lot of random symbols but when I do the same on the server computer then it is only two symbols.
Here is my code, I'm using JFileChooser so I can easily access the object I want to send from the server, understandably I have access to both computers.
Code for the sending server
public static void serverSendObject() throws IOException, ClassNotFoundException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(2001,10);
}
catch (IOException ex) {
System.out.println("Can't setup server on this port
number. ");
}
Socket socket = null;
OutputStream out = null;
ObjectOutputStream objOut = null;
try {
socket = serverSocket.accept();
}
catch (IOException ex) {
System.out.println("Can't accept client connection. ");
}
try {
out = socket.getOutputStream();
}
catch (IOException ex) {
System.out.println("Can't get socket input stream. ");
}
try {
objOut = new ObjectOutputStream(out);
}
catch (FileNotFoundException ex) {
System.out.println("File not found. ");
}
JFileChooser fc = new JFileChooser();
int reValue=fc.showOpenDialog(null);
if(reValue == JFileChooser.APPROVE_OPTION) {
try(ObjectInputStream objInput = new ObjectInputStream(new FileInputStream(fc.getSelectedFile()))) {
objOut.writeObject(objInput.readObject());
}
catch(IOException e) {
}
catch(ClassNotFoundException e) {
}
}
serverSocket.close();
socket.close();
}
Code for the receiving client
public void loadExternal() throws IOException, ClassNotFoundException {
Visualizer vis = new Visualizer();
currentVis=vis;
Socket socket = null;
String host= *insert IP address*
socket= new Socket(host, 2001);
InputStream in = socket.getInputStream();
ObjectInputStream objIn = new ObjectInputStream (in);
currentRes = (Result) objIn.readObject();
objIn.close();
socket.close();
}
I keep getting
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(Unknown Source)
as an error. I have tried to put a catch on this but it doesn't help. I have tried some different methods but nothing seems to work.
Just want to point out that the exact same code works when I connect the sockets on my OWN computer and that this problem occurs when I connect two different computers AND that I'm being able to send an object to the server computer.
EDIT: I think I can confirm that something has happaned to the object I have sent. A locally (via sockets) saved object is 1131 bytes while the object I have sent to the server computer is only 4 bytes.
I use the same kind of technique when I send the objects, with ObjectOutputStream at the client and ObjectInputStream at the server.
whenever you write anything. remember to flush and then close. Hope can help.
I'm implementing a "little" client/server app, like a Cloud.
The problem is: I'm new to Java.
So I've learned it a bit fast, and same for client/server communication, database, frames, threads.
I'm pretty sure my code won't be the best one, but the fact is: I have to use Java, and I have to do this fast. I won't try to optimize it, I just need it to work.
I have already implemented a lot, so I won't give all the code here, just explain what happens before my problem:
The main prog on client side opens a login frame. The client can register or login. Registration is working well, so let's say he logs in. If authentication works, that opens another frame, with "browse" and "upload" options. The idea is, he browse a file and then upload it. When he clicks on upload, it should call the upload function, which will send a byte[2] array to the server with a DataOutputStream object. (it sends first the size of the array, and then the array)
On the server side, when authentication works, it'll give the client socket to a "Cloud" class, which - until now - is just supposed to receive a byte array (it'll do more later, but for now I can't get this byte array)
But the server receives a size of 1970302063 instead of 2. I've checked the size before the writeInt on client size, it's 2. After the readInt on the server side, it's 1970302063.
I don't understand it. Can the server receive something else somewhere and my beautiful 2 be lost in a buffer?
Server side:
public class Server {
public static void main(String[] zero) throws IOException {
ServerSocket serverSocket ;
Socket clientSocket ;
int tmp = 0; //when everything works, tmp will disappear
DataBase DB = new DataBase();
System.out.println("ouverture du server");
serverSocket = new ServerSocket(2017);
while(tmp<1) { //limit the number of connections allowed.
try {
clientSocket = serverSocket.accept();
Thread t = new Thread(new Connect(clientSocket, DB));
t.start();
//clients.printAll(); //just to see the DB.
}catch (IOException e) {
e.printStackTrace();
}
tmp++;
}
serverSocket.close();
}
}
As you can see, I'm authorising just 1 connection, because I'm still testing it and I don't want to change the port number each time I got an error / change something. With that I'll just have to restart server after each test.
public class Connect implements Runnable{
private DataBase DB;
private Socket clientSocket;
public Connect(Socket socket, DataBase DB) {
this.clientSocket = socket;
this.DB = DB;
}
public void run() {
try {
BufferedReader in;
PrintWriter out;
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream());
String mode = in.readLine();
String clientID = in.readLine();
String clientPwd = in.readLine();
if (mode.equals("auth")) {
Authentification auth = new Authentification (clientID, clientPwd, DB);
out.write(auth.getMessage()+"\r\n");
out.flush();
if (auth.getMessage().equals("Login Successfull. Welcome in SecureCloud!"))
new Cloud(clientSocket, DB); //launch Cloud.
}
else if (mode.equals("reg")) {
Registration reg= new Registration(clientID, clientPwd, DB);
out.write(reg.getMessage()+"\r\n");
out.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
this.clientSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now, here: authentication and registration will set up a connection, and the client socket is closed at the end. BUT, if the authentication is successful, then it create a new Cloud:
public class Cloud {
public Cloud(Socket clientSocket, DataBase DB) {
try {
DataInputStream dIn = new DataInputStream(clientSocket.getInputStream());
int length = dIn.readInt(); // read length of incoming message
System.out.println("byte array size: "+length);
byte[] shorthash;
if(length!=2) {
System.err.println("Incorrect size for shorthash!");
}
else {
shorthash = new byte[length];
dIn.readFully(shorthash, 0, shorthash.length); // read the message
System.out.println(shorthash);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
As you can see, for now it doesn't do much. I know I'll have to add a while(true) loop so that the client can upload more files, but for now I'll be happy if I can send just this byte array.
Now, client side. I'll skip the main, it just open the login frame. I'll also skip most of the login Frame, it's just a frame...here is what happens when client click on "login":
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==connectButton) {
Connection con = new Connection();
con.auth(username.getText(), password.getText());
if(con.getServerAnswer().equals("Login Successfull. Welcome in SecureCloud!")) {
this.setVisible(false);
con.setConnection(true);
con.setUsername(username.getText());
new Load_Frame(con.getUsername(),con.getSocket());
}
else System.out.println("erreur: " + con.getServerAnswer());
}
else if (e.getSource()==registerButton) {
this.setVisible(false);
new Registration_Frame();
}
}
So, it creates the connection and launch the authentification process (con.auth) with username and password. If it's successfull, it'll open the Load Frame with the username and socket used for this connection.
I'll skip again most of Load Frame, here are the actions:
public void actionPerformed(ActionEvent e)
{
if (e.getSource()== uploadButton) {
this.filename = uploadField.getText();
File file = new File(filename);
//TODO: change the upload function:
try {
client.upload(file);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
else if (e.getSource()== downloadButton) {
this.filename = downloadField.getText();
//TODO: change the download function
//download(filename);
}
else if (e.getSource()== browseButton) {
JFileChooser jc = new JFileChooser();
if(jc.showOpenDialog(this) != 1)
uploadField.setText(jc.getSelectedFile().getAbsolutePath());
}
}
And, last but not least, the "upload" function, called by client.upload(file):
public void upload(File originalFile) throws NoSuchAlgorithmException, NoSuchPaddingException, IOException {
//create the "FileData" object, which make the shorthash of the file.
FileData myfile = new FileData(originalFile, fileID);
//say to the server that we want to upload:
PrintWriter mode;
mode = new PrintWriter(socket.getOutputStream());
mode.println("upload");
mode.flush();
//Send shorthash to the server:
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
System.out.println("byte array size: "+myfile.shortHash.length);
out.writeInt(myfile.shortHash.length); // send the length of the byte array
//out.flush();
out.write(myfile.shortHash); //send the array
}
I've tried with and without the out.flush(), it doesn't make any difference.
With all that, here are the answers I get:
From client:
Asking for authentification...
Login Successfull. Welcome in SecureCloud!
No file uploaded
byte array size: 2
From server:
Connection to SQLite has been established.
ouverture du server
byte array size: 1970302063
Incorrect size for shorthash!
Of course, since I know the size of the byte array, I could easily tell the server "hey, the size is 2!" (I mean, directly initialize my array with new byte[2] )
But I would like to understand what happens here. Plus, if I'm not receiving the good size, maybe I won't receive the good array.
So, thanks to the people who find the problem really fast :)
It was a bit of stupid, the kind of problem you don't see even if you reread your code 5 times, but then someone else read it he see that in a minute.
The solution is to delete this part in the upload function:
//say to the server that we want to upload:
PrintWriter mode;
mode = new PrintWriter(socket.getOutputStream());
mode.println("upload");
mode.flush();
it was usefull until i changed the code on server side, and now it's just annoying ^^
I am currently learning networking, specifically client-server classes.
I have done much research and implemented various test programs but I can't figure out why/when one would need to use the flush() method.
How can there be data mistakenly left in the output stream if it is always read in by the input stream? As dictated by the client-server code.
I tried to test my basic echo client server program by omitting the flush() but I could not break it.
When testing the flush() by writing twice from the client side and only reading once for the server's reply all that happened was a backlog (I assume the stream acts like a queue?) in the server's replies.
Then I took the same code and added flush() before and after the second write and it made no difference. It's as if the flush() doesn't actually clear the stream.
So can someone please explain in what scenario with regards to client/server stream interactions would flush() be required?
Server:
public class ServerApp
{
private ServerSocket listener;
private Socket clientCon;
public ServerApp()
{
try
{
listener = new ServerSocket(1234, 10);
} catch (IOException e)
{
e.printStackTrace();
}
}
public void listen()
{
try
{
System.out.println("Server is listening!");
clientCon = listener.accept();
System.out.println("Server: Connection made with Client");
processClient();
} catch (IOException e)
{
e.printStackTrace();
}
}
public void processClient()
{
try(ObjectOutputStream out = new ObjectOutputStream(clientCon.getOutputStream()); ObjectInputStream in = new ObjectInputStream(clientCon.getInputStream()))
{
String msg;
while(!(msg = (String)in.readObject()).equalsIgnoreCase("Shutdown"))
{
out.writeObject("Server: " + msg);
out.flush();
}
out.writeObject("Server is powering down...");
out.close();
in.close();
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
public static void main (String args[])
{
ServerApp sa = new ServerApp();
sa.listen();
}
}
Client:
public class ClientApp
{
private Socket serverCon;
public ClientApp()
{
try
{
serverCon = new Socket("127.0.0.1", 1234);
} catch (IOException e)
{
e.printStackTrace();
}
}
public void communicate()
{
try (ObjectOutputStream out = new ObjectOutputStream(serverCon.getOutputStream()); ObjectInputStream in = new ObjectInputStream(serverCon.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)))
{
String response = null;
do
{
System.out.println("Enter your message for server: ");
out.writeObject(br.readLine());
out.flush();
out.writeObject("Flush not working");
out.flush();
response = (String) in.readObject();
System.out.println(response);
response = (String) in.readObject();
System.out.println(response);
} while (!response.equalsIgnoreCase("Server is powering down..."));
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
public static void main(String args[])
{
ClientApp ca = new ClientApp();
ca.communicate();
}
}
The method flush() is used to flush out any internal buffers that may be in use. For example using a BufferedOutputStream the contents are written in chunks to improve performance (it's slower to write each byte as they come).
Depending on usage, you might never have to call flush(). However let's say you send a small String (converted to byte[]) and it fits nicely in the internal buffer. The contents of the buffer won't be sent until the buffer is full or flush() is called.
Now let's say you're writing over the network, and you expect the other side to answer something to your small String. Since it's still in the buffer, the other side won't receive it and it can result in both sides waiting forever.
Object streams are another beast, and I'm a little disappointed that so many beginners are using them. There should be a warning in the class saying "Objects may be more difficult to send/receive than they appear".
ObjectOutputStream delegates the flush() call to its internal BlockDataOutputStream which has 3 buffers sized 1024, 5 and 256 for "blockdata", header data and characters respectively.
Try it with new ObjectOutputStream(new BufferedOutputStream(clientCon.getOutputStream())) and you'll see a difference with and without flush(). It causes flushing of the underlying buffered output stream. Without a buffered stream there is no buffer to flush so it does nothing.
I have many clients that are waiting for server messages. So the client make accept() and wait for server. When server have messages, open a connection to the client and send messages, after that, close the communication and the cycle restart.
I've seen usually the inverse approach, where the server do accept() and client connect to it. I've wrote this code but the client (that do accept() ) is blocked on point 3 and the server (that create the connection to the client) is blocked on point 2.
Sure i have some problems in my code (dont know where), but... this is the correct way ?
The client (that do accept() and wait for new messages)
try {
System.out.println("Waiting..");
receiver = serverSocket.accept();
System.out.println("1");
ObjectInput fromServerReader = new ObjectInputStream(receiver.getInputStream());
ObjectOutputStream toServerWriter = new ObjectOutputStream(receiver.getOutputStream());
System.out.println("2");
toServerWriter.writeObject("dummy");
toServerWriter.flush();
System.out.println("3");
ScheduledEvent scheduledEvent = (ScheduledEvent) fromServerReader.readObject();
System.out.println("4");
receiver.close();
System.out.println("5");
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
// Should never happen
}
The server (that when have new message to send to client, create the
connection)
try {
InetAddress address = InetAddress.getByName(sendToUser
.getMachineName());
socket = new Socket(address, port);
log.debug("1");
ObjectOutputStream toClientWriter = new ObjectOutputStream(
socket.getOutputStream());
ObjectInputStream fromClientReader = new ObjectInputStream(socket.getInputStream());
log.debug("2");
String read = (String)fromClientReader.readObject();
log.debug("3");
// Compose the message
ScheduledEvent scheduledEvent = new ScheduledEvent();
scheduledEvent.setSubject(event.getSubject());
scheduledEvent.setMessage(event.getText());
log.debug("4");
toClientWriter.writeObject(scheduledEvent);
toClientWriter.flush();
log.debug("5");
socket.close();
log.debug("6");
} catch (UnknownHostException e) {
// TODO handle
e.printStackTrace();
} catch (IOException | ClassNotFoundException e) {
// TODO handle
e.printStackTrace();
}
In client code, instead of using
PrintWriter writer;
Use
ObjectOutputStream writer;
And then use
writer.writeObject("dummy");
writer.flush();
Try using println instead of write toServerWriter.println("dummy");. The server may be waiting for the newline character.
I'm programming a network software with Java, but I have a real problem using my application through a "true" network.
Let a software be a host, and listening for client connexions.
Here is my Server loop :
public void run() {
while (mServerSocket != null) {
try {
Socket wClient = mServerSocket.accept();
System.out.println("Client connecté");
wClient.setSoTimeout(50);
wClient.setTcpNoDelay(false);
Client c = new Client(wClient);
synchronized(this) {
mWaitingClients.add(c);
c.start();
}
} catch(Exception ex) {
System.out.println("Server error : " + ex.getMessage());
}
}
}
When a client tried to connect to the server, I use this function :
public Client connect(InetAddress addr, int port) throws Exception {
Socket socket = new Socket(addr, port);
socket.setSoTimeout(50);
socket.setTcpNoDelay(false);
Client c = new Client(socket);
c.start();
return c;
}
And here is the client loop :
public void run() {
try {
ObjectOutputStream out = new ObjectOutputStream(mSocket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(mSocket.getInputStream());
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
}
out.flush();
mOutMessages.clear();
Thread.sleep(50);
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
Thread.sleep(50);
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
}
Some other parts of the program do Message and put them in the Output list of the Client (mOutMessages).
Some other parts of the program read Message from the mInMessages of the Client.
But something is wrong with this. It works fine locally (server and client on the same computer), but fail or is hazardous (some messages are sent but never received) using two computers (with LAN or through the Internet).
Server ever detect connexions from the clients, send "handshake" messages to the client, but the client never receives them.
I'm more a C programmer than a Java one, and I never had this kind of problem using libc Sockets, so, why my way of doing is wrong ?
Thank you !
Edit :
My Server is created using this function :
public void open(int port) throws Exception {
mServerSocket = new ServerSocket(port);
start(); // Call the run mentionned above.
}
Edit :
Here is my solution, maybe it's not perfect but it works !
public void run() {
try {
BufferedOutputStream buf_out = new BufferedOutputStream(
mSocket.getOutputStream()
);
BufferedInputStream buf_in = new BufferedInputStream(
mSocket.getInputStream()
);
ObjectOutputStream out = new ObjectOutputStream(buf_out);
out.flush();
ObjectInputStream in = new ObjectInputStream(buf_in);
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
out.flush();
}
mOutMessages.clear();
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
If I understand right, both client and server use the run method. If both client and server happen to write sufficiently large messages (not fitting in involved buffers) at the same time then you get a deadlock because neither partner advances to reading (which would drain full buffers). Due to network delays, this might only happen in the non-local scenario, i.e. there may be enough time to pile up enough messages in the mOutMessages buffer.
Note that documentation of Socket.setSoTimeout (which you used) only says that it affects read()s. (For example, in my JDK, ObjectOutputStream seems to use a BlockDataOutputStream with a buffer size of 1024 bytes).
I recommend to either use a separate thread for reading/writing or (if you know the maximum messages size) use a sufficiently large buffer (by wrapping the SocketOutputStream in a BufferedOutputStream). If you opt for larger buffers, you may also want to write one message at a time (and try to read messages after each).