Hi all,

I could really use some help with a multithreaded server/client application that I'm building. I'm first experimenting with basic concepts and then am going to implement the results within a larger application. However, I find myself stuck on a particular issue. Initially, I transfer a file from the client to the server. This works fine. However, once this transfer has been completed I wish to send additional messages between server and client. This is where the problem lies as the client and server both hang on their respective sides. I really don't know how to resolve this problem. I did implement closing the socket and reopening another and this worked fine when running a single client. However, when running multiple clients I encountered problems that are self-explanatory. So I need another solution that doesn't involve closing and reopening the socket.

Here is my server code:

import java.io.*;
import java.net.*;
public class Provider implements Runnable{
	ServerSocket server;
	BufferedInputStream bis;
	BufferedOutputStream bos;
	InputStreamReader isr;
	Socket sock;
	int in;
	PrintWriter outputStream;
	int count = 0;
	public void run()
	{
		outputStream = null;
		try{
			server = new ServerSocket(1517);
			while (true)
			{
				sock = server.accept();
				ClientThread c = new ClientThread(sock,count);
				Thread t = new Thread(c);
				t.start();
			}	
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage());
		}
		finally {
			try {
				sock.close();
				server.close();
			}
			catch (Exception e)
			{
				System.out.println(e.getMessage());
			}
		}
	}

	public static void main(String args[])
	{
		Provider Server = new Provider();
		Server.run();
	}
}

Here is my ClientThread code:

import java.io.*;
import java.net.*;
public class ClientThread implements Runnable {
	ServerSocket server;
	BufferedInputStream bis;
	BufferedOutputStream bos;
	InputStreamReader isr;
	Socket sock;
	int in;
	PrintWriter outputStream;
	int ID = 0;
  
   ClientThread(Socket s, int ID){
		this.sock = s;
		this.ID = ID;
	}
	
	public void run() {
		try {
		 bis = new BufferedInputStream(sock.getInputStream());
		 bos = new BufferedOutputStream(newfileOutputStream("C:/Amanda_test/test"));
		 byte[] byteArray = new byte[6022386];
		 while ((in = bis.read(byteArray)) != -1)
		 {
		 bos.write(byteArray,0,in);
		 }
		 bos.flush();
                 BufferedReader input = new BufferedReader(
                        new InputStreamReader(
                            sock.getInputStream()));
		 PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
                 // not sure whether this is necessary
                 // just thought that it would be bad form for the client to wait
                 // for the server to write a message first. I've tried with and
                 // without the readLine(), though, and it makes no difference
                 input.readLine();
                 out.println("success"); // this is what I need to send
                 out.flush();
                 out.close(); 
                 input.close();
		 bos.close();
		 bis.close();
                 this.sock.close();
	       }
	       catch (Exception e)
	       {
		System.out.println(e.getMessage());
	       }
	}
}

and this is my Client code:

import java.io.*;
import java.net.*;
public class Requester{
	Socket requestSocket;
	BufferedOutputStream bos;
	BufferedInputStream bis;
	Socket sock;
	int in;
	Requester(){}
	void run()
	{
	   try{
		 //1. creating a socket to connect to the server
		 sock = new Socket("myserver.ac.za", 1517);
		 System.out.println("Connected to localhost in port 1517");
		 File myFile = new File("F:/Documents.txt");
		 bis = new BufferedInputStream(new FileInputStream(myFile));
		 bos = new BufferedOutputStream(sock.getOutputStream());
		 byte[] byteArray = new byte[(int)myFile.length()];
		 System.out.println((int)myFile.length());
		 while((in = bis.read(byteArray))!= -1)
		 {
		  bos.write(byteArray,0,in);
		 }
		 bos.flush();
                 BufferedReader input = new BufferedReader(
                        new InputStreamReader(
                            sock.getInputStream()));
		 PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
                 // not sure whether this is necessary
                 // just thought that it would be bad form for the client to wait
                 // for the server to write a message first. I've tried with and
                 // without the println(), though, and it makes no difference
                 out.println("nothing"); 
                 String word = input.readLine(); // this is the necessary bit
                 out.flush();
                 out.close(); 
                 input.close();
		 bos.close();
		 bis.close();
		 sock.close();
	   }
	   catch (Exception e)
	   {
	      System.out.println(e.getMessage());
	   }
     }
}

Help would be greatly appreciated!

Thanks.

Recommended Answers

All 6 Replies

IMO implementing hybrid protocols has always been a problem.

Just thinking out loud here but it seems that in your case the server has no way of knowing when the "file transfer" has completed and the user now wants to pass across a string message to the server. Also, I don't think the "while" loop at the server end would "terminate" unless the client socket output stream is closed (which would in turn trigger -1 being read thus breaking the loop).

My suggestion would be to implement a small protocol between the client and the server in which the server knows "how much" binary data the client would be sending thereby giving the server a logical condition to break out of the loop. An example of this protocol would be (all messages termiated by line breaks):
BIN:1024 (client would now initiate a binary transfer of content length 1024 bytes)
TXT:1024:UTF8 (client would now initiate a text content transfer of size 1024 byes and encoding being UTF8 -- which would be anyways default in case it is ignored)
DIE (client has decided to quit; the server can now safely close the client socket)

An alternate solution would be to use RMI in case the client/server would always be JVM bytecode compatible and the use of raw TCP sockets is not a requirement.

EDIT: As far as your code is concerned, I'm personally not a big fan of exposing variables to a greater scope than it is required. There is no need for you to have "Socket" for the server at the class level rather than being scoped inside the "run()" method of your server. The same goes with the different IO class variables declared. Also, if you want to keep track of clients, use an "AtomicInteger" which isn't subject to "race condition" i.e. two or more threads modifying the count variable with the possibility of losing updates.

Thanks for the reply ~s.o.s.~

The code for the server knowing that the file transfer has been successfully completed is written in a later version of this program. I didn't include it for simplicity sake.

I am having a problem implementing the protocol you suggested as I don't know how big the file will be from the client side. The people commissioning this project will decide that when they start using it.

I did try including a cipher - '?' - in the text file on the client side: a character that would never form part of my text data so is safe to use. I then converted each loop iteration of the byte array into a string, read each char of the string to check that it didn't reach a '?' and if it did to perform a break else write the byte array to the file.

boolean condition = false;
System.out.println((int)myFile.length());
while((in = bis.read(byteArray))> 0)
{
   String chunk = new String(byteArray,0,in);
   System.out.println(chunk);
   for (int i = 0; i < chunk.length(); i++) {
     if (chunk.charAt(i) == '?') condition = true;
   } 
   if (condition = true) break;
   else bos.write(byteArray,0,in);
}
bos.flush();

This didn't make any difference to the performance of the program. The while loop still hangs on the server side. I'm assuming because it's still waiting for the client socket to close.

So, I was wondering... wouldn't it be possible to use the exception blocks to implement a fail safe option? In other words, instead of sending a success message, write code within the client catch block and server catch block that would account for any problems with the socket. Surely, if there are problems with the socket then one can assume that the information was lost somehow in transit and steps could then be taken to remedy the situation. Then I wouldn't have to implement hybrid protocols at all.

And thanks for the heads up about atomicinteger and the scope of my variables.

I would appreciate your views on this.

I am having a problem implementing the protocol you suggested as I don't know how big the file will be from the client side

I'm not sure why it's that difficult; knowing the number of bytes present in the file is as simple as making a single method call on the File object you are anyways creating.

I'm assuming because it's still waiting for the client socket to close.

Right, because AFAIK, as long as the client socket stream is open, you'd never get a -1 when reading the stream. Also, the while loop condition should be >= 0 or != -1.

This didn't make any difference to the performance of the program

For this simple program, sure, it won't be a problem. But converting the data read into a String object and looping over it would surely take a hit when dealing with multiple clients and large files.

So, I was wondering... wouldn't it be possible to use the exception blocks to implement a fail safe option? In other words, instead of sending a success message, write code within the client catch block and server catch block that would account for any problems with the socket. Surely, if there are problems with the socket then one can assume that the information was lost somehow in transit and steps could then be taken to remedy the situation. Then I wouldn't have to implement hybrid protocols at all.

You lost me there with the "problems with socket" bit? Aren't you stuck with the basic handshaking part here?

You lost me there with the "problems with socket" bit? Aren't you stuck with the basic handshaking part here?

Okay, basically what I've done is to create code within the catch blocks to cater for any socket communication problems. It consists of some file handling operations. I now only have to write the file from the client to the server and then close the streams and sockets on both ends. This means that the -1 is reached immediately and the while loop terminated on the server side. My program now, I think, does what it needs to do provided that I'm right about the exception block being sufficient to account for any socket malfunctions.

As an aside - It's probably just me not understanding you properly but I can't see why the protocol that you suggest would work. Even if the server knows the size of the file to be sent, the client socket cannot close after the file has been sent as it is waiting for a message from the server. For this reason, my adding in of the cipher code didn't work. So, wouldn't the problem be the same where the server would hang in the while loop? As I said, it's probably just me...

Thank you for your replies. You helped me to understand what the problem was and even though I fixed it in a roundabout way, without implementing hybrid protocols, your answers helped me to see things clearer. :)

As an aside - It's probably just me not understanding you properly but I can't see why the protocol that you suggest would work. Even if the server knows the size of the file to be sent, the client socket cannot close after the file has been sent as it is waiting for a message from the server

Yes, you are right, the socket can't close after the file has been sent. *But*, the server now knows *how* much data is the "file" contents and how much data is "message" contents. So now you are saved from the infinite-loop condition since you have another condition you need to check for; the size of the data transferred. Something like this:

long fLen = /* read file length from the message */;
long totalLen = fLen;
int bytesRead = -1;
while(true) {
    if(totalLen < buf.length) {
        bytesRead = in.read(buf, 0, totalLen);
    } else {
        bytesRead = in.read(buf);
    }
    totalLen -= bytesRead;
    out.write(buf, 0, bytesRead);
    if(totalLen <= 0) break;
}

You helped me to understand what the problem was and even though I fixed it in a roundabout way, without implementing hybrid protocols

Does your existing code handle just the files sent from the client or even messages? If you are only transferring files, you really don't need a hybrid protocol. If your current code deals with both file contents and messages from client, I'd be interested in seeing how you managed to pull it off without having a demarcation between the binary and textual contents.

your answers helped me to see things cleare

You are of course welcome. :)

EDIT:

The while loop still hangs on the server side. I'm assuming because it's still waiting for the client socket to close.

You are doing a readLine() on the server which would block as long as the server doesn't read in a "line" i.e. you'd need a newline character in your message for your server to break out of that "readLine".

I am only working with files so, yes, no real need for hybrid protocols.

Thanks again for the help ;)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.