I'm trying to make a simple text editor which can be shared accross multiple terminals at the same time. I have a Server waiting for new users, when a user enters the shared editor it just starts waiting for input characters.
public class Server {
public static final int PORT = 8080;
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(PORT);
while (true) {
Socket socket = ss.accept();
System.out.println("A new user entered the sever");
new Thread(() -> serve(socket)).start();
}
}
private static void serve(Socket socket) {
try {
while (!socket.isClosed() && !socket.isInputShutdown()) {
System.out.println("hey " + socket.isClosed() + " " + socket.isInputShutdown());
System.out.print(new String(SocketUtil.receiveBytes(socket,1)));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
When a user closes the editor, the socket is closed on client side. However, the socket on the server side does not get closed and the server starts looping infinitly in the "wait for input" loop.
The Client is a singleton containing the following methods, called at the openning and closing of the editor.
public static void init() {
try {
if (socket == null) socket = new Socket(HOST,Server.PORT);
} catch (IOException e) {
e.printStackTrace();
kill();
throw new Error(e.getMessage());
}
}
public static void kill() {
Check.notNull(socket);
try {
SocketUtil.terminateCommunication(socket);
System.out.println(socket.isClosed());
} catch (IOException e) {
e.printStackTrace();
}
}
Finally, here are utilitary methods (in SocketUtil) used in both classes :
public static void terminateCommunication(Socket socket) throws IOException {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
}
public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
char[] bytes = new char[nBytes];
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
isr.read(bytes);
return bytes;
}
Any idea of why the socket on server side is not closed after the Client gets killed ?
It is not quite clear from the Javadoc, but isClosed() only returns true when you have explicitly called close() on the socket (see the sources to confirm that). You should check for exceptions and the return value of read() instead. If you read -1 or catch an IOException while trying to read (or write, for that matter), it essentially means that the other side has closed the connection, so you should close your socket as well (better to it in a finally block) and you're done with that particular connection. You don't check for -1 in receiveBytes(), but you really should. Perhaps throw a EOFException() if you want to merge these two possibility into one, so that the code up the stack (in serve()) doesn't have to figure out what exactly happened:
public static char[] receiveBytes(Socket socket, int nBytes) throws IOException {
char[] bytes = new char[nBytes];
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
if (isr.read(bytes) == -1)
throw new EOFException();
return bytes;
}
One exception from the IOException rule (sorry for the pun) is the SocketTimeoutException. If you get this, the connection is still alive, and you may just as well retry your read(). But I believe that in order to get these, you must call Socket.setSoTimeout() somewhere, and if you haven't, then you probably shouldn't worry about SocketTimeoutException.
You should also note that read() may sometimes return partial reads (that is, less than bytes.length). If it's important that receiveBytes() reads exactly nBytes (which probably is, since you never return the number of actual characters read), then you should call it in a loop, like this:
int pos = 0;
while (pos < bytes.length) {
int l;
if ((l = isr.read(bytes, pos, bytes.length - pos)) == -1) {
throw new EOFException();
}
pos += l;
}
I know this is cumbersome, which is exactly why many developers create utility methods like your receiveBytes().
The proper way to detect that the client has closed its connection is by checking the reception of 0 bytes.
System.out.print(new String(SocketUtil.receiveBytes(socket,1)));
just check if the string is empty should do the trick.
Note that I am not that familiar with java, but I do know socket programming.
Receiving 0 bytes, checking for that, and closing the socket if you do is a good solution.
You can use exception handling too, but you'll detect that the peer closed it socket an iteration later. Receiving 0 bytes is not really an error condition it is just a signal from the peer that he has closed its end of the socket and won't send anymore data. If you ignore this, and keep using the socket, you'll receive an exception in the next iteration because there is nothing to receive anymore.
Related
Following scenario that explains my problem.
I've a PLC that acts as a server socket program. I've written a Client Java program to communicate through socket communication with the PLC.
Steps that take place in this process are:
1) For each second my Client program happen to communicate with the PLC, read the data in stream, store the data temporarily in a ByteArrayOutputStream and closing both input stream and socket. Following snippet gives the idea
try {
socket = new Socket(host, port);
is = socket.getInputStream();
outputBuffer = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int read;
if((read = is.read(buffer)) != -1) {
outputBuffer.write(buffer, 0, read);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
System.out.println("Before closing the socket");
try {
is.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("After closing the socket");
} catch (Exception e) {
e.printStackTrace();
}
}
2) Processing stored data according to my requirement is what I'm trying to do. So for every 1 second, client program connects to Server, read the data(if data is present), store the data, close socket and process it. And it has to happen for a very long run, probably till the Server program is on. And that may happen till for every few weeks.
3) Problem what I'm facing is, I'm able to run the above show for 1-2 hours, but from then, Client Program unable to fetch the data from the Server Program(PLC in this case), though both are connected through socket. I.e 128 bytes of data present, but Client program isn't able to read that data. And this started happening after program run successfully for almost 2hours
4) Please find the brief code which may help for you to look into.
public class LoggingApplication {
public static void main(String[] args) throws NumberFormatException {
if (args.length > 0 && args.length == 2) {
String ipAddress = mappingService.getIpAddress();
int portNo = (int) mappingService.getPortNo();
ScheduledExecutorService execService = Executors.newScheduledThreadPool(1);
execService.schedule(new MyTask(execService, ipAddress, portNo, mappingService), 1000, TimeUnit.MILLISECONDS);
} else {
throw new IllegalArgumentException("Please pass IPAddress and port no as arguments");
}
}
}
Runnable Code:
public class MyTask implements Runnable {
public ScheduledExecutorService execService;
private String ipAddress;
private int portNo;
private ConfigurationMappingService mappingService;
private MySocketSocketUtil mySocketSocketUtil;
public MyTask(ScheduledExecutorService execService, String ipAddress, int portNo, ConfigurationMappingService mappingService) {
this.execService = execService;
this.ipAddress = ipAddress;
this.portNo = portNo;
this.mappingService = mappingService;
}
public void run() {
MySocketSocketUtil mySocketSocketUtil = new MySocketSocketUtil(ipAddress, portNo);
execService.schedule(new MyTask(execService, ipAddress, portNo, mappingService), 1000, TimeUnit.MILLISECONDS);
mySocketSocketUtil.getData(); //It's able to fetch the data for almost 2 hours but from then, it's just getting empty data and it's keep on giving empty data from then. and so on.
/*
*
*Some code
*/
}
}
Here's where, I'm having the problem
mySocketSocketUtil.getData(); is able to fetch the data for almost 2 hours but from then, it's just getting empty data and it's keep on giving empty data from then. and so on. It's a big question I know, And I want to understand what might have gone wrong.
Edit: I'm ignoring the condition to check end of the stream and closing a socket based on it is because, I knew I'm going to read first 1024 bytes of data only always. And So, I'm closing the socket in finally block
socket = new Socket(host, port);
if(socket != null && socket.isConnected())
It is impossible for socket to be null or socket.isConnected() to be false at this point. Don't write pointless code.
if((read = is.read(buffer)) != -1) {
outputBuffer.write(buffer, 0, read);
};
Here you are ignoring a possible end of stream. If read() returns -1 you must close the socket. It will never not return -1 again. This completely explains your 'empty data':
from then, it's just getting empty data and it's keep on giving empty data from then, and so on
And you should not create a new Socket unless you have received -1 or an exception on the previous socket.
} else {
System.err.println("Socket couldn't be connected");
}
Unreachable: see above. Don't write pointless code.
You should never disconnect from the established connection. Connect once in the LoggingApplication. Once the socket is connected keep it open. Reuse the socket on the next read.
I think there are couple of points you need to fix before getting to the solution to your problem. Please try to follow the following suggestions first:
As #EJP said this code block is not needed.
if(socket != null && socket.isConnected()) {
also you are using a byte array of length 1024 and not using while or for loop to read the data stream. Are you expecting only a block of data which will never exceed 1024 bytes?
byte[] buffer = new byte[1024];
int read;
if((read = is.read(buffer)) != -1) {
This is also not needed as it is unreachable.
} else {
System.err.println("Socket couldn't be connected");
}
Can you explain the data stream behavior you are expecting?
Last but not the least is.read(buffer) is a blocking call so if there is no data to read yet, it will hold the thread execution at that point.
Please try to answer the questions I have asked.
#KishoreKumarKorada from your description in the comment section, it seems like you are monitoring the data change on server side. Socket stream works in a read-once fashion. So,
First thing is, you need to request from server every time and the server needs to RESEND the data on every request.
Second, the way you presented is more like you are operating on byte level, which is not very good way to do that unless you have any legitimate reason to do so. The good way is to wrap the data in JSON or XML format and send it over the stream. But to reduce bandwidth consumption, you may need to operate on byte stream sometimes. You need to decide on that.
Third, for monitoring the data change, the better way is to use some timestamp to compare when the data has changed on the server side and what is the timestamp stored on the client side, if they match, data has not changed. Otherwise fetch the data from the server side and update the client side.
Fourth, when there is data available that you are not able to read, can you debug the ins.read(...) statement to see if its getting executed and the execution goes inside the if block or if statement is evaluated to false? if true then examine the read value and let me know what you have found?
Thanks.
I have a client program that sends messages typed in console to the server. Following some advices, I introduced a check for a closed socket with Socket.checkError(). Nevertheless, for some reason it indicates error only after second failed attempt to send a message.
My code:
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
while (true)
try (
Socket clientSocket = new Socket(hostname, port);
PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
) {
String input;
while ((input=stdIn.readLine())!=null) {
socketOut.println(input);
if (socketOut.checkError()) {
System.out.println("Got socket error");
break;
}
}
} catch (IOException e) {
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {}
}
I shut down (manually) my server side after receiving 'message1'. Therefore, I expect to get the error while trying to send the very next message. Nevertheless it occurs only one message after:
message1
message2
message3
Got socket error
Can anyone explain this behavior and advise me a method to get notification right on the first attempt to send a message in void?
Following some advices, I introduced a check for a closed socket with Socket.checkError().
There is no such method. Clearly you are referring to PrintWriter.checkError().
Nevertheless, for some reason it indicates error only after second failed attempt to send a message.
The reason is that there is both a socket send buffer at the sender and a socket receive buffer at the receiver, and that sending is asynchronous: it therefore isn't possible for the first send to detect an error.
Can anyone explain this behavior and advise me a method to get notification right on the first attempt to send a message in void?
There isn't one. That's the nature of TCP. What you are attempting indicates an application protocol error, and the answer lies in the realm of the application protocol as well: don't have the peer close the socket while this end could still be sending data, OR don't allow this end to send data after the peer has indicated, via the application protocol, that it won't be reading any more data.
Don't use PrintWriter over the network. It suppresses the actual exception. Use BufferedWriter and the write() and newLine() methods.
In ths java library there is no method to check if connection is opened or not. Method like isConnected() and isClosed() check only one side of the connection (where you invoked the method).
From javadoc:
Note: Closing a socket doesn't clear its connection state, which means
this method will return true for a closed socket (see isClosed()) if
it was successfuly connected prior to being closed.
To check if the connection has been really closed simply invoke the read() method (or equivalent) and check if it returns -1.
Note: also if isConnected will work as you like (giving false if the other side of the socket closed the connection or if there is a network problem or similar) the sequence:
if (socket.isConnected()) {
int x = socked.read();
}
will not grant that the x has a value different from -1 or throws an IOException, because the connection could be closed after the isConnected test and before the read operation.
The following code to show how any kind of check on the socket cannot guarantee that a subsequent read will give a valid result.
// Return true because socket communication is enabled
if (myFunctionToCheckIfSocketIsOpen(socket)) {
// Here the peer closed the socket or the network shutdown
// This read will give -1 or throws IOException also if the previous check returned true
int x = socket.read();
}
From the answer of #Davide Lorenzo MARINO I got the idea of employing read(). The only problem that it is blocking. However, one can always run it in another thread, which would modify a class global variable, when read() finally returns -1:
static boolean socketIsAlive;
...
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
while (true)
try (
Socket clientSocket = new Socket(hostname, port);
PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
) {
socketIsAlive=true;
new ConnectionChecker(clientSocket).start();
String input;
while (true) {
if ((input=stdIn.readLine())!=null)
socketOut.println(input);
if (!socketIsAlive) {
System.out.println("Got socket error");
break;
}
}
} catch (IOException e) {
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {}
}
}
...
static public class ConnectionChecker extends Thread{
Socket socket;
public ConnectionChecker(Socket socket) {
this.socket=socket;
}
#Override
public void run() {
try {
if (socket.getInputStream().read()==-1)
socketIsAlive=false;
} catch (IOException e) {}
}
}
I've created a client-server connection, something like a chat system. Previously I was using a while loop on the client side, and it was waiting to read a message from the console every time (of course server has a while loop as well to serve forever). But now, I'm trying to first create a connection at the beginning of the session, and then occasionally send a message during the session, so to maintain a permanent and persistent connection.
Currently, without the while loop, the client closes the connection and I don't know how to find a workaround.
Here is the client code:
import java.net.*;
import java.io.*;
public class ControlClientTest {
private Socket socket = null;
// private BufferedReader console = null;
private DataOutputStream streamOut = null;
public static void main(String args[]) throws InterruptedException {
ControlClientTest client = null;
String IP="127.0.0.1";
client = new ControlClientTest(IP, 5555);
}
public ControlClientTest(String serverName, int serverPort) throws InterruptedException {
System.out.println("Establishing connection. Please wait ...");
try {
socket = new Socket(serverName, serverPort);
System.out.println("Connected: " + socket);
start();
} catch (UnknownHostException uhe) {
System.out.println("Host unknown: " + uhe.getMessage());
} catch (IOException ioe) {
System.out.println("Unexpected exception: " + ioe.getMessage());
}
String line = "";
// while (!line.equals(".bye")) {
try {
Thread.sleep(1000);
//TODO get data from input
// line = console.readLine();
line="1";
if(line.equals("1"))
line="1,123";
streamOut.writeUTF(line);
streamOut.flush();
} catch (IOException ioe) {
System.out.println("Sending error: " + ioe.getMessage());
}
// }
}
public void start() throws IOException {
// console = new BufferedReader(new InputStreamReader(System.in));
streamOut = new DataOutputStream(socket.getOutputStream());
}
}
And here is the Server code:
import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ControlServer {
private Socket socket = null;
private ServerSocket server = null;
private DataInputStream streamIn = null;
public static void main(String args[]) {
ControlServer server = null;
server = new ControlServer(5555);
}
public ControlServer(int port) {
try {
System.out
.println("Binding to port " + port + ", please wait ...");
server = new ServerSocket(port);
System.out.println("Server started: " + server);
System.out.println("Waiting for a client ...");
socket = server.accept();
System.out.println("Client accepted: " + socket);
open();
boolean done = false;
while (!done) {
try {
String line = streamIn.readUTF();
// TODO get the data and do something
System.out.println(line);
done = line.equals(".bye");
} catch (IOException ioe) {
done = true;
}
}
close();
} catch (IOException ioe) {
System.out.println(ioe);
}
}
public void open() throws IOException {
streamIn = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
}
public void close() throws IOException {
if (socket != null)
socket.close();
if (streamIn != null)
streamIn.close();
}
}
I would like to summarize some good practices regarding the stability of TCP/IP connections which I apply on a daily basis.
Good practice 1 : Built-in Keep-Alive
socket.setKeepAlive(true);
It automatically sends a signal after a period of inactivity and checks for a reply. The keep-alive interval is operating system dependent though, and has some shortcomings. But all by all, it could improve the stability of your connection.
Good practice 2 : SoTimeout
Whenver you perform a read (or readUTF in your case), your thread will actually block forever. In my experience this is bad practice for the following reasons: It's difficult to close your application. Just calling socket.close() is dirty.
A clean solution, is a simple read time-out (e.g. 200ms). You can do this with the setSoTimeoutmethod. When the read() method timeouts it will throw a SocketTimeoutException. (which is a subclass of IOException).
socket.setSoTimeout(timeoutInterval);
Here is an example to implement the loop. Please note the shutdown condition. Just set it to true, and your thread will die peacefully.
while (!shutdown)
{
try
{
// some method that calls your read and parses the message.
code = readData();
if (code == null) continue;
}
catch (SocketTimeoutException ste)
{
// A SocketTimeoutExc. is a simple read timeout, just ignore it.
// other IOExceptions will not be stopped here.
}
}
Good practice 3 : Tcp No-Delay
Use the following setting when you are often interfacing small commands that need to be handled quickly.
try
{
socket.setTcpNoDelay(true);
}
catch (SocketException e)
{
}
Good practice 4 : A heartbeat
Actually there are a lot of side scenario's that are not covered yet.
One of them for example are server applications that are designed to only communicate with 1 client at a time. Sometimes they accept connections and even accept messages, but never reply to them.
Another one: sometimes when you lose your connection it actually can take a long time before your OS notices this. Possibly due to the shortcomings described in good practice 3, but also in more complex network situations (e.g. using RS232-To-Ethernet converters, VMware servers, etc) this happens often.
The solution here is to create a thread that sends a message every x seconds and then waits for a reply. (e.g. every 15 seconds). For this you need to create a second thread that just sends a message every 15 seconds. Secondly, you need to expand the code of good practice 2 a little bit.
try
{
code = readData();
if (code == null) continue;
lastRead = System.currentTimeMillis();
// whenever you receive the heart beat reply, just ignore it.
if (MSG_HEARTBEAT.equals(code)) continue;
// todo: handle other messages
}
catch (SocketTimeoutException ste)
{
// in a typical situation the soTimeout is about 200ms
// the heartbeat interval is usually a couple of seconds.
// and the heartbeat timeout interval a couple of seconds more.
if ((heartbeatTimeoutInterval > 0) &&
((System.currentTimeMillis() - lastRead) > heartbeatTimeoutInterval))
{
// no reply to heartbeat received.
// end the loop and perform a reconnect.
break;
}
}
You need to decide if your client or server should send the message. That decision is not so important. But e.g. if your client sends the message, then your client will need an additional thread to send the message. Your server should send a reply when it receives the message. When your client receives the answer, it should just continue (i.e. see code above). And both parties should check: "how long has it been?" in a very similar way.
You could wrap a thread around the connection and have it periodically send a status to keep the line open, say every 30 seconds or whatever. Then, when it actually has data to send it would reset the keep alive to be 30 seconds after the last transmission. The status could be helpful to see if the client is still alive anyway, so at least it can be a useful ping.
Also, you should change your server code, you appear to only handle one connection at the moment. You should loop and when a socket connection comes in spawn a thread to handle the client request and go back to listening. I may be reading to much into what may just be your test code, though.
Make the client socket connection wrapped around a thread. Use a blocking queue to wait for messages. There should only be a single sender queue throughout your application, so use a singleton pattern.
e.g.
QueueSingleton queue = QueueSingleton.getSenderQueue();
Message message = queue.take() // blocks thread
send(message); //send message to server
When you need to send a message to the server, you can use the blocking queue to send the message.
QueueSingleton queue = QueueSingleton.getSenderQueue();
queue.put(message)
The client thread will wake up and process the message.
For maintaining the connection, use a timer task. This is special type of thread that calls a run method repetitively at specified periods. You can use this to post a message, a ping message, every so often.
For processing the received message, you could have another thread, waiting for messages on another blocking queue (receiver queue). The client thread will put the received message on this queue.
I was trying to get some networking going in my app, but i encountered some issues. It seems that I cant write to the OutputStream object. Though my server recieves the connection, it does not recieve any data. I've tried using Writer, DataOutputStream among others. none seemed to work.
My app uses asynctasks that call this object with a Socket object and a message. (The socket object has already been used to set Streams after initialisation using the setStreams method.)
can someone please try and find the problem? I will be very thankful.
public class NetworkingUtils {
private OutputStream out = null;
private InputStream in = null;
//set streams
public void setStreams(Socket sock){
if (sock.isConnected()) {
try {
this.out = (OutputStream) sock.getOutputStream();
this.in = (InputStream) sock.getInputStream();
} catch (Throwable e) {
Log.d("SOCKET", "FAILED TO SET STREAMS");
e.printStackTrace();
}
}
}
//send \n terminated messages to pre defined socket
public void sendMessage(Socket sock, String message) throws Throwable {
if (sock.isConnected()) {
try {
this.out.write(message.getBytes());
Log.d("SOCKET","WRITING COMPLETE. " + message);
} catch (Throwable e) {
throw e;
}
}
}
public String recvMessage(Socket sock) throws Throwable {
//receives \n terminated message from pre defined socket
String answer = null;
if (sock.isConnected()){
try{
answer = this.convertStreamToString(this.in);
Log.d("SOCKET","READING COMPLETE");
}
catch (Throwable e){
Log.d("socket",e.getLocalizedMessage());
throw e;
}
}
else{
Log.d("socket","is not connected!!!");
}
if (answer.length() == 0){
//empty string answer from server
throw new IOException();
}
else {
return answer;
}
}
private String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = null;
try{
s = new java.util.Scanner(is).useDelimiter("\r\n");}
catch (Throwable e){
e.printStackTrace();
}
return s.hasNext() ? s.next() : "";
}
}
I can only see one client-side that might cause this ... and I'm doubtful about it. (That is to say: try this, just in case it makes a differences, but I don't think it will.)
this.out.write(message.getBytes());
Log.d("SOCKET","WRITING COMPLETE. " + message);
The potential problem is that if out is a "buffered" stream, then a write may only result in the bytes being written to the buffer. It may be necessary to call this.out.flush() to "push" to the server.
But I am doubtful it will help, because (to my knowledge) a socket output stream isn't buffered in Java. I think it is more likely that the real problem is on the server side.
If you are stumped with figuring out which side the problem is occuring, I suggest you try using a network monitoring / packet sniffing tool (on the server side) to check if the data is reaching the server host.
While I have your attention, your exception code is really, really bad.
Don't declare methods as throws Throwable (or throws Exception). That basically says "this method may throw ANY exception, and I'm not telling you which one". When you do that, the caller code has to cope with any exception, which is basically impossible to do intelligently.
What you should do is to declare the method as throwing the checked exceptions that the code can throw. For example, in your case, IOException is probably sufficient.
It is not a good idea to catch an exception, log it, and then rethrow it. Why? Because further up the stack there are probably other methods that will see the exception. They can't know if the exception has already been logged or not. So should they log it (possibly resulting in duplicate logs events for the same problem) or not (possibly resulting in the exception going unlogged.)
Don't throw exceptions without a message:
throw new IOException();
It is lazy. You should always include a simple message that can (at least) be grep'd or googled for.
In addition, your testing of Socket.isConnected() all over the place is unnecessary. According to the javadoc:
Returns: true if the socket was successfuly connected to a server
Note: Closing a socket doesn't clear its connection state, which means
this method will return true for a closed socket (see isClosed()) if
it was successfuly connected prior to being closed.
So repeatedly testing isConnected is nugatory. If it returns true once, it will will always return true from then on.
Even the initial isConnected test in setStreams is doubtful. I'd just call getInputStream without testing, and rely on the Socket API throwing an IOException if the socket is in the wrong state.
You're effectively reading lines with that obscure Scanner usage, but you're not writing lines. So the scanner will block until a line terminator or EOS arrives.
You need to append a line terminator when sending.
Hey guys, I'm working on a server program that is meant to scale well and serve potentially thousands of clients. The thing is, I feel that Apache MINA is too heavyweight so I decided to not use it and wrote my own client listener instead. I never really performed asynchronous socket operations in Java (C# made that so much easier, but I really preferred to write this project in Java since I'm more familiar with it in everything besides socket reads), so trying to understand how to use the thread pool correctly is hard for me. I used Apache MINA documentation to get an idea of how things should be done. I got two questions:
Is the thread pool used correctly? Apache MINA's default thread size is the number of CPU cores + 1, but should I really use a 3 thread thread pool for my Core 2 Duo in order to accept thousands of clients?
I know that reallocating the buffer twice for each message received from the client (each message is two packets, one header that is a constant 4 bytes and a content packet that has its length specified in the header). Is there an easy way to use a fixed size buffer that checks for buffer overruns so that behavior is still the same but the buffer doesn't have to be constantly reallocated?
Here's how I start the listener:
ClientListener cl = new ClientListener(1234);
cl.init();
new Thread(cl).start();
Here is the relevant code for ClientListener:
private static final int THREADS = Runtime.getRuntime().availableProcessors() + 1;
private ServerSocket socket;
private ExecutorService threadPool;
private int port;
public ClientListener(int port) {
this.port = port;
threadPool = Executors.newFixedThreadPool(THREADS);
}
public void init() {
try {
socket = new ServerSocket(port);
} catch (IOException ex) {
}
}
public void run() {
while (true) {
try {
ClientSession s = new ClientSession(socket.accept());
threadPool.execute(s);
} catch (IOException ex) {
}
}
}
ClientSession relevant code:
private Socket socket;
private byte[] buffer;
private boolean isHeader;
public ClientSession(Socket socket) {
this.socket = socket;
this.buffer = new byte[4];
this.isHeader = true;
}
public void run() {
InputStream in;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (IOException ex) {
return;
}
while (!socket.isClosed()) {
try {
int read = in.read(buffer);
if (read == -1)
break;
receive(read);
} catch (IOException ex) {
break;
}
}
}
private void receive(int readBytes) {
if (isHeader) {
if (readBytes >= 4) {
buffer = new byte[getPacketLength(buffer)];
isHeader = false;
} else {
System.out.println("Not enough data received from client " + socket.getInetAddress() + " to decode packet.");
}
} else {
if (readBytes >= buffer.length) {
processMessage(new LittleEndianByteArrayReader(decryptData(buffer)), this);
buffer = new byte[4];
isHeader = true;
} else {
System.out.println("Not enough data received from client " + socket.getInetAddress() + " to decode packet (needed " + buffer.length + ", received " + readBytes + ").");
}
}
}
You don't need to know the code for getPacketLength, processMessage, decryptData, and the class LittleEndianByteArrayReader, but I'm pretty sure the purposes of those methods/classes are obvious.
The number of threads in blocking IO scenario have to be calculated by the number of clients and the time each client connection will be open.
Each connection of each user requires on thread.
With only three threads a user could simply block your server until connection timeout by just opening three TCP connections and not sending any data to your server.
Nevermind guys. I realized that Apache MINA actually uses NIO which is why I got confused. It really needs only one thread to process requests with the use of selectors. Thanks for all your answers and sorry about the confusion!