Java server program maxing out heap, burning CPU - java

I've got a little network game I'm making to learn networking in Java, and I'm in need of a little insight into what my program is having issues with. My server program maxes out the heap and burns 100% of the CPU. I'm sure I've got major newbie gotchas in the code, and I'm wondering if anyone would be so kind as to point them out to me, and perhaps detail why it is such a horrible practice.
Basically, the Server class's job is to wait in socket.accept() to deal with new clients. Each client from there gets its own ConnectionThread (which deals with input) and an attached OutputStream (which handles output). I know this may be wasteful for large applications, but with the server running along with just three clients (which are set to skip rendering and only send data through the socket every ~20ms for both input and output) it cooks the CPU and the server overflows the stack.
I've got a Packet class which converts the data into a string for sending, and the receiver decodes it back into a Packet. I suspect I have some Packets laying around too long, but I don't see where. If it isn't the packets, I'm fairly certain I have SOME sort of uncontrolled exponential object growth.
Here are some snippets of relevant code. I'm happy to provide more if the problem is elsewhere.
Just for reference, here is the full code: https://github.com/taylorrobert/ProjectM2O
Server:
public Server() {
network = new NetworkManager(this);
network.setConnectionCounter(0);
entityManager = new EntityManager(this);
setListenState(true);
try {
serverSocket = new ServerSocket(PORT);
} catch (IOException e) {
System.out.println("Error in server constructor.");
System.exit(1);
}
}
public void listen() {
System.out.println("Current connectionCounter: " + network.getConnectionCounter());
while (shouldListen) {
ConnectionThread conn = null;
try {
conn = new ConnectionThread(serverSocket, this);
}
catch (Exception e) {
System.out.println("____Error constructing ConnectionThread. Could there be another instance of the server running?");
e.printStackTrace();
System.exit(1);
}
(new Thread(conn)).start();
System.out.println("Connection count: " + network.getConnectionCounter());
}
}
ConnectionThread:
public ConnectionThread(ServerSocket s, Server ser) {
resetTimer();
setActiveState(false);
server = ser;
//This UUID becomes the client's controllable player ID
//and the ID of this ConnectionThread.
connectionID = String.valueOf(UUID.randomUUID());
try {
socket = s.accept();
System.out.println("Socket ID " + connectionID + " established on: " + socket);
} catch (IOException e) {
System.out.println("Error in ConnectionThread. Is there a server already running on this port?");
}
init();
}
public void init() {
try {
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
catch (IOException e) {
System.out.println("Error in intializing I/O streams.");
System.exit(1);
}
//Create the output thread
OutputStream outputHandler = new OutputStream(this);
new Thread(outputHandler).start();
//Get the client up to date on all relevant data
server.getEntityManager().addPlayerEntity(getConnectionID());
server.getNetwork().pushClientInitState(getConnectionID(), this);
server.getNetwork().addConnection(this);
server.getNetwork().notifyClientsAboutNewPlayer(getConnectionID());
int s = server.getNetwork().getConnections().size();
server.getNetwork().sendConsoleMessage("Players online: " + s, this);
}
public void run() {
setActiveState(true);
System.out.println("Running ConnectionThread...");
while (isActive()) {
//System.out.println("Entity size: " + server.getEntityManager().getEntities().size());
String op = readInputStream();
if (op.equals("")) continue;
Packet packet = Packet.populateNewPacketFromString(op);
try {
incomingOpQueue.put(packet);
} catch (InterruptedException e) {
System.out.println("Server failed to add packet to outgoing queue!");
}
//Take all packets off the incoming queue and execute them in order
while (incomingOpQueue.size() > 0) {
Packet p = incomingOpQueue.poll();
PacketExecutor.executePacket(server, p, this);
}
}
}
public String readInputStream() {
String msg = "";
try {
msg = in.readLine();
msg = msg.replace("\n", "");
msg = msg.trim();
} catch (IOException e) {
return "";
}
return msg;
}
OutputStream:
public void output() {
while (parentCT.isActive()) {
UnitTester.updateAllEntityLocationsToAllClients(parentCT, parentCT.server.getEntityManager().getEntities());
while (parentCT.getOutgoingOpQueue().size() > 0) {
String packet = (parentCT.getOutgoingOpQueue().poll().getString());
if (packet.equals("")) continue;
//System.out.println("Sending " + packet + " to " + parentCT.getConnectionID());
parentCT.getOutput().println(packet);
}
}
}

You probably need to make shouldListen volatile. Otherwise, there's every chance that its value will be cached, and setting it to false in some other thread will make no difference. You are setting it to false right, so that the main loop just doesn't make lots and lots of threads until it maxes out the heap and burns up the CPU?

Related

Java Datagram send and receive not staying in sync

I have a device that replies to commands using UDP datagrams. I'm trying to come up with a way to guarantee the response is from a particular request. When I run the following code I get the first two, and sometimes third request, then it just hangs and times out the receive. I've tried a few different methods to resolve it (hence the synchronize and iQueuePointer receive timeout). Here is an example of a run:
Listening for X-Air responses
Sending X-Air requests
Servicing request 6
Send loop - bWait=true request.size=6 iQueuePointer=6
/info,ssssV0.04XR12-24-00-6EXR121.10 from 6
Removed 6
Servicing request 5
Send loop - bWait=true request.size=5 iQueuePointer=5
/ch/01/config/name,sPreach Mic from 5
Removed 5
Here's the code:
public static final Object socketLock = new Object();
public static DatagramSocket socket;
public static ArrayList<String> request = new ArrayList<>();
public static int iQueuePointer;
public static boolean bWait;
public static void main(String[] args) {
new Main();
}
private Main() {
try {
socket = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
}
new Thread(receiveRunnable).start();
new Thread(sendRunnable).start();
iQueuePointer++;
request.add("/info");
iQueuePointer++;
request.add("/ch/01/config/name");
iQueuePointer++;
request.add("/ch/02/config/name");
iQueuePointer++;
request.add("/ch/03/config/name");
iQueuePointer++;
request.add("/ch/04/config/name");
iQueuePointer++;
request.add("/ch/05/config/name");
iQueuePointer++;
}
private Runnable sendRunnable = new Runnable() {
#Override
public void run() {
System.out.println("Sending requests");
while(socket != null) {
if(!bWait && request.size() > 0 && request.size() < iQueuePointer) {
iQueuePointer--;
bWait = true;
System.out.println("Servicing request " + iQueuePointer);
//synchronized (socketLock) {
try {
socket.send(new DatagramPacket(request.get(0).getBytes(),
request.get(0).getBytes().length,
InetAddress.getByName("192.168.0.180"), 10024));
} catch (IOException e) {
e.printStackTrace();
}
//}
}
}
System.out.println("sendRunnable ended");
}
};
private Runnable receiveRunnable = new Runnable() {
#Override
public void run() {
System.out.println("Listening for responses");
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
while(socket != null) {
//synchronized (socketLock) {
try {
//socket.setSoTimeout(5000);
socket.receive(packet);
if (packet.getLength() > 0 && socket != null) {
System.out.println(new String(Arrays.copyOf(packet.getData(), packet.getLength()))
+ " from " + iQueuePointer);
request.remove(0);
System.out.println("Removed " + iQueuePointer);
bWait = false;
}
} catch (Exception e) {
if (!e.toString().contains("Receive timed out")) {
e.printStackTrace();
} else {
System.out.println("Receive loop - bWait=" + bWait + " request.size=" + request.size()
+ " iQueuePointer=" + iQueuePointer);
}
}
//}
}
System.out.println("receiveRunnable ended");
}
};
In UDP you can't assume that the responses arrive in the same order as the requests were sent, or indeed at all, and you also can't assume they only arrive once.
You will have to use sequence numbers in both, to match them up, or else not have any pending requests, and keep issuing a request until you get its response, and then you still have to deal with duplicates somehow.
NB You need to either recreate the DatagramPacket or at least reset its length every time around the read loop: otherwise it can keep shrinking to the size of the smallest datagram received so far.

Java RMI server side threading

I'm just getting started with RMI and I'm trying to write a simple program that simulates a train booking system. I have the basics set up - Server, Client, and a Remote object exported. It works fine with one Client connection. However when more than 1 Client connects, the Clients seem to be executing in the same thread. This is the case when I run multiple Clients on the same machine or when I connect a Client from another laptop.
I was under the impression that RMI handled threading on the server side? If not, how do I go about handling multiple Client connections given the code below?
Here are the classes of interest.
Server.....
public class Server {
public Server() {
try {
Booking stub = (Booking) UnicastRemoteObject.exportObject(new BookingProcess(), 0);
Registry registry = LocateRegistry.getRegistry();
registry.bind("Booking", stub);
System.err.println("Server Ready");
} catch (RemoteException e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
} catch (AlreadyBoundException e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
}
}
BookingProcess.....(I've left out the private methods that processInput(String input) uses)
public class BookingProcess implements Booking {
private static Journey dublinGalway = new Journey("Dublin to Galway");
private static Journey dublinLimerick = new Journey("Dublin to Limerick");
private static Journey dublinCork = new Journey("Dublin to Cork");
private Journey currentJourney;
private enum State {
INITIAL, JOURNEYS_DISPLAYED, JOURNEY_CHOSEN, ANOTHER_BOOKING_OFFERED, SOLD_OUT;
}
private State currentState = State.INITIAL;
public synchronized String processInput(String input) {
String output = "";
if(currentState == State.INITIAL) {
if(bookedOut()) {
output = "Sorry, there are no seats remaining on any route. Get the bus.";
currentState = State.SOLD_OUT;
}
else {
output = "Please choose a journey to book: " + "1: " + dublinGalway.getDescription() + ", 2: " + dublinLimerick.getDescription() + ", 3: " + dublinCork.getDescription();
currentState = State.JOURNEYS_DISPLAYED;
}
}
else if(currentState == State.JOURNEYS_DISPLAYED) {
output = this.processJourneyChoice(input);
}
else if(currentState == State.JOURNEY_CHOSEN) {
output = "Do you wish to confirm this booking? (y/n)";
if(input.equalsIgnoreCase("y")) {
if(bookingConfirmed()) {
output = "Thank you. Your journey from " + currentJourney.getDescription() + " is confirmed. Hit return to continue.";
//currentState = State.ANOTHER_BOOKING_OFFERED;
}
else {
output = "Sorry, but the last seat on the " + currentJourney.getDescription() + " route has just been booked by another user.";
//currentState = State.ANOTHER_BOOKING_OFFERED;
}
currentState = State.ANOTHER_BOOKING_OFFERED;
}
else if(input.equalsIgnoreCase("n")) {
output = "You have cancelled this booking. Hit return to continue.";
currentState = State.ANOTHER_BOOKING_OFFERED;
}
}
else if(currentState == State.ANOTHER_BOOKING_OFFERED) {
output = "Would you like to make another booking? (y/n)";
if(input.equalsIgnoreCase("y")) {
output = "Hit Return to continue.";
currentState = State.INITIAL;
}
else if(input.equalsIgnoreCase("n")){
output = "Goodbye.";
try {
Thread.currentThread().join(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
currentState = State.INITIAL;
}
}
else if(currentState == State.SOLD_OUT) {
output = "Goodbye.";
}
return output;
}
And finally Client......
public class Client {
public static void main(String[] args) {
Client client = new Client();
client.runClient();
}
public void runClient() {
try {
BufferedReader consoleInput = new BufferedReader(new InputStreamReader(System.in));
Registry registry = LocateRegistry.getRegistry("localhost");
Booking stub = (Booking) registry.lookup("Booking");
String serverResponse = stub.processInput("begin");
System.out.println("Server: " + serverResponse);
while((serverResponse = stub.processInput(consoleInput.readLine())) != null) {
System.out.println(serverResponse);
if(serverResponse.equals("Goodbye.")) {
break;
}
}
} catch (Exception e) {
System.err.println("Client exception " + e.toString());
e.printStackTrace();
}
}
}
As for as RMI server threads, the answer is that it may or may not run in a separate thread. See the documentation here:
http://docs.oracle.com/javase/6/docs/platform/rmi/spec/rmi-arch3.html
3.2 Thread Usage in Remote Method Invocations
A method dispatched by the RMI runtime to a remote object implementation may or may not execute in a separate thread. The RMI runtime makes no guarantees with respect to mapping remote object invocations to threads. Since remote method invocation on the same remote object may execute concurrently, a remote object implementation needs to make sure its implementation is thread-safe.
You can take server side thread dumps and you would see that the RMI TCP Connection threads IDs keep changing, however as #jtahlborn noticed the server side method is synchronized so it would execute serially, not necessarily in a single thread though.
Your server side processInput() method is synchronized, so, yes, the calls will be handled serially. what does that have to do with RMI?
UPDATE:
if you want to have separate currentState and currentJourney values for each client session, then you need to use the RMI remote session pattern, see this answer for details.

Full-duplex server socket implementaion, seperate read and write thread?

I want to read and write(randomly from server to client) on same server socket (java application). My client to server write and read work fine in a loop. At server with response write properly.
But if i am trying to write at server randomly some command. i do not have solution, first of all my question is :
is it possible at server side to write command to client ramdonly on same socket?
if possible, any suggestion or pointer how to do it?
please give me some pointer where I can read the material about this scenario ?
thanks in advance.
public class ContentServerSocket extends ServerSocket {
private final static int PORT = 4444;
protected static boolean XYZGONE = false;
public static Content content;
public ContentServerSocket(xyzService service) throws IOException {
super(PORT);
while (true) {
Log.d(TAG, "Waiting for new request from client(content) ....");
new HandleRequest(accept(), service).start();
}
}
public static void xyzRunAway() {
Log.d(TAG," Content Serv er 1 ");
XYZGONE = true;
}
}
class HandleRequest extends Thread {
private final static String TAG = "ContentServerSocket:Thread for a request:";
private Socket client;
private xyzService service;
private static Context context;
HandleRequest(Socket client, SuggestionService service) {
this.client = client;
this.service = service;
context = xyzService.serviceContext();
}
public void run() {
while (true) {
try {
Log.d(TAG, " Step 1: client: Received request MSG for Check... ");
PrintWriter out = new PrintWriter(client.getOutputStream(),
true);
BufferedReader in = new BufferedReader(new InputStreamReader(
client.getInputStream(), "utf-8"));
String request = "";
String tmpLine = null;
Log.d(TAG, " Step Xyz waiting data from the client ... ");
while ((tmpLine = in.readLine()) != null) {
if (tmpLine.length() > 0) {
request += tmpLine;
//if (tmpLine.toLowerCase().contains("</contentInfo>")) {
if (tmpLine.contains("</contentInfo>")) {
Log.d(TAG, " Server : broke because of </contentInfo>");
break;
}
} else {
Log.d(TAG, " Step NULL : ");
request = "";
}
}
Log.d("Robin", " Step 2: Actual request received from the client : : " + request);
if (request.length() == 0) {
Log.d("Robin",
" client got 0 length request, thread stop!");
throw new Exception();
}
//XMLParser xmlParser = new XMLParser(new ByteArrayInputStream(
// request.getBytes("UTF-8")));
Log.d(TAG, " Step 3 : ");
RequestParser readxmlrequest = new RequestParser(request);
String requestType = readxmlrequest.parsingXmlRequestFromContent();
Log.d(TAG, " Step 4 requestType : " + requestType);
//TODO : need to get the result and pas to the out.println..
//String result = processXML(xmlParser);
String result = responseToContentRequest(readxmlrequest,requestType);//null; //TODO need to complete.
Log.d(TAG, " Step 5 result : "+result);
(((((((((())))))))))";
if (result != null && result.length() > 0) {
//oos.writeObject(result);
Log.d("Robin", " Writing response to socket ... ");
out.println(result + "\n");
out.flush();
Log.d("Robin", " Writing flush completed ");
}
if(ContentServerSocket.XYZGONE) {
Log.d(TAG," XYZGONE >>>>>>>> ");
ContentServerSocket.XYZGONE = false;
String tmp = "<ssr> OK Done .......</ssr>";
out.println(tmp + "\n");
Log.d("Content Server Socket ", "xyz:" + tmp);
out.flush();
}
} catch (IOException ioException) {
Log.d("Robin", " IOException on socket listen: " + ioException);
}
catch (Exception e) {
Log.d("Robin", " outer exception: " + e.toString());
break;
}
finally {
if (client == null || client.isClosed()
|| !client.isConnected()) {
Log.d(" Robin ", " client is null");
break;
}
}
//break;
}
Log.d("Robin", " thread stop... ");
}
So , I fixed it . I just need to maintain two different thread.
1) read.
2)write.
In the above code i just started one more thread for write .
insert the code in Run function of above code.
====================================================
Runnable r1 = new Runnable() {
public void run() {
try {
while (true) {
System.out.println("Hello, world!");
if(ContentServerSocket.XYZGONE) {
Log.d(TAG," XYZGONEY >>>>>>>> ");
ContentServerSocket.XYZGONE = false;
String tmp = "<ssr> OK Done .......</ssr>";
out.println(tmp + "\n");
Log.d("Content Server Socket ", "XYZGONE :" + tmp);
out.flush();
}
Thread.sleep(1000L);
}
} catch (InterruptedException iex) {}
}
};
Thread thr1 = new Thread(r1);
==================================
Then Start the thread in the wile loop of read.
with the following code with a check.
====================================
if(!thr1.isAlive())thr1.start();
Thanks everyone, who respond my question..
Yes it is possible to write data from multiple threads on a server or on a client to an existing socket. However you have to make sure the requests do not overlap, and the receiving side actually knows what is written from who.
If you use a line based protocol you can define each message is a single line. In that case you should synchronize multiple threads in a way that only one is writing parts of that line at any given moment.
Your code is a bit too big to understand where your problem is, sorry.
Maybe this tutorial helps? There are quite many out there:
http://www.javaworld.com/javaworld/jw-12-1996/jw-12-sockets.html

Java - Human playable Server/Client Multiplayer

I'm currently trying to implement the game of Nim using Java, I want to be able to have one player act as the server and another as the player.
I'm fairly new to Java networking and have only had experience using basic TCP/IP where the human client connects to a computer host.
The trouble I'm having is that I need to be able to differentiate between the different players whilst implementing the protocol for the game (The protocol being the logic for the game).
As it stands I can let one player (Client) interact with the Server. All that happens is the Client can play the game but there is no oppostion (The Server merely tracks the state of the game e.g. How many sticks left, valid input etc..).
How would I go about adding a second player to take the place of the host?
Edit:
The Client and Server code has been posted, it is the code I have used and I'm quite comfortable with, the question I am asking is would it be a suitable base to implement a multi-player game or would I need to do something completely different?
My Nim Protocol: (Untested)
public class NimLogic
{
private static final int WAITING = 0;
private static final int EVALUATING = 1;
private static final int ANOTHER = 2;
private int currentState = WAITING;
private int theInput = 0;
private int totalSticks = 10;
String processInput(String input) {
String theOutput = null;
try
{
theInput = Integer.parseInt(input);
}
catch (Exception e)
{
// Ignore conversion error
}
switch (currentState)
{
case WAITING:
theOutput = "Take how many sticks?";
currentState = EVALUATING;
break;
case EVALUATING:
if(theInput == 1 | theInput == 2 | theInput == 3)
{
if (theInput < totalSticks)
{
totalSticks -= theInput;
theOutput = "There are" + totalSticks + " left.";
}
else if (theInput > totalSticks)
{
theOutput = "Error: You cannot take more sticks than that are available";
currentState = EVALUATING;
}
}
if(totalSticks == 1)
{
theOutput = "Game Over! Play again? (Yes = 1, No = 0)...";
currentState = ANOTHER;
}
break;
case ANOTHER:
if (theInput == 1)
{
totalSticks = 10;
currentState = EVALUATING;
theOutput = "Take how many sticks?";
}
else
{
theOutput = "Bye.";
}
}
return theOutput;
}
}
Thanks for all the help!
Edit:
Client
public class Client
{
#SuppressWarnings("static-access")
public static void main(String machine[])
{
Socket kkSocket = null;
PrintStream os = null;
DataInputStream is = null;
try
{
kkSocket = new Socket(machine[0], 4444);
os = new PrintStream(kkSocket.getOutputStream());
is = new DataInputStream(kkSocket.getInputStream());
}
catch(UnknownHostException e)
{
System.err.println("Socket Connect failed on " + machine[0]);
}
catch (IOException e)
{
System.err.println("Streams failed on " + machine[0]);
}
if (kkSocket != null && os != null && is != null )
{
try
{
String fromServer, fromClient;
while((fromServer = is.readLine()) != null && !fromServer.equals("Bye."))
{
fromClient = JOptionPane.showInputDialog(fromServer);
os.println(fromClient);
}
JOptionPane.showMessageDialog(null, "Goodbye, keep smiling.");
os.close();
is.close();
kkSocket.close();
}
catch (UnknownHostException e)
{
System.err.println("Can't connect to " + machine[0] + e);
}
catch (IOException e)
{
e.printStackTrace();
System.err.println("I/O failed on " +machine[0]);
}
}
}
}
Server
public class Server
{
public static void main(String arg[])
{
ServerSocket serverSocket = null;
try
{
serverSocket = new ServerSocket(4444);
}
catch (IOException e)
{
System.err.println("Can't listen on 4444 -> " + e);
System.exit(1);
}
Socket clientSocket = null;
try // allow the client to connect
{
clientSocket = serverSocket.accept();
}
catch (IOException e)
{
System.err.println("Failed accept on 4444 -> " + e);
System.exit(1);
}
try
{
DataInputStream is =
new DataInputStream(new BufferedInputStream
(clientSocket.getInputStream()));
PrintStream os =
new PrintStream(new BufferedOutputStream
(clientSocket.getOutputStream(), 1024), false);
GuessState kks = new GuessState();
String inputLine, outputLine;
outputLine = kks.processInput(null);
os.println(outputLine);
os.flush();
while((inputLine = is.readLine()) != null
&& !outputLine.equals("Bye."))
{
outputLine = kks.processInput(inputLine);
os.println(outputLine);
os.flush();
}
os.close();
is.close();
clientSocket.close();
serverSocket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
I'm not quite sure if I'm answering your question here, so apologies if I'm not. Also, it's been a little while since I did any Java networking code, so there might be a few wrinkles here which hopefully others can sort out.
The following is a bit of a brain dump of the changes I'd probably make, for better or worse...
Rework the networking code to accept multiple connections. Normally you'd do this by handing off the socket returned by ServerSocket.accept to a thread to process. If you were dealing with a lot of connections, you could do it using NIO instead, but that's probably too far to fast for now.
Separate the game state from the client conversation code. To keep things simple, embed the client conversation code in the thread object. The game state needs to be in an object that's shared between all the threads servicing the socket.
I'd recommend making the game state a proper 'domain object' rather than having it parsing strings etc. It should have operations like 'take(clientID, int)' rather than 'processInput'.
Consider using the observer pattern to distribute events from the domain object to the socket threads. Examples of events might be 'turnTaken' or 'gameComplete'.
Embed the notion of 'turns' into the game object, and have the server broadcast an event to the socket threads announcing whose turn it is.
Hope that gives you a starter for ten?
The server/client aspect should have no bearing on the communication of the two players. You should be able to spawn two instances of the Nim game, one that listen for an incoming connection on some port(Server), and one that connects to it (Client). Once the connection is established, you can pass Objects between the two instances over your connection that represent game information. Each instance of your Nim game is responsible for parsing that game data and running the Nim logic on it.
In essence, each instance of the game can run as a server or a client. Here's some code I wrote for Chess that should be applicable. Read through it. Elsewhere I instance a Server or Client and store it in a reference of type NetworkIdenitity.
private abstract class NetworkEntity
extends Thread {
ObjectOutputStream outputStream;
ObjectInputStream inputStream;
Socket connectionHandle;
Object messageToSend;
Object receivedMessage;
public NetworkEntity(final String name) {
super(name);
}
#Override
public abstract void run();
public void getStreams()
throws IOException {
this.outputStream = new ObjectOutputStream(this.connectionHandle.getOutputStream());
this.outputStream.flush();
this.inputStream = new ObjectInputStream(this.connectionHandle.getInputStream());
}
public void closeConnection() {
try {
if (this.outputStream != null) {
this.outputStream.close();
}
if (this.inputStream != null) {
this.inputStream.close();
}
if (this.connectionHandle != null) {
this.connectionHandle.close();
chatPanel.writeToDisplay("Connection closed with "
+ this.connectionHandle.getInetAddress().getHostName());
}
}
catch (final IOException e) {
JOptionPane.showMessageDialog(thisFrame, "Problems experienced when closing connection",
"Notification", JOptionPane.ERROR_MESSAGE);
}
}
public void processIncomingData()
throws IOException {
do {
try {
this.receivedMessage = this.inputStream.readObject();
}
catch (final ClassNotFoundException e) {
JOptionPane.showMessageDialog(thisFrame, "read() error: message from "
+ this.connectionHandle.getInetAddress().getHostName() + " not received", "Notification",
JOptionPane.ERROR_MESSAGE);
}
if (this.receivedMessage instanceof Move) {
final Move m = (Move) this.receivedMessage;
System.out.println(getName() + " got move" + m);
requestMove(Table.this.chessBoard, Table.this.currentPlayer, Table.this.currentOpponent, m, false);
repaint();
}
else if (this.receivedMessage instanceof Board) {
final Board b = (Board) this.receivedMessage;
System.out.println(getName() + " received this board:");
b.printCurrentBoardState();
// System.out.println("local copy looked like this: " );
// chessBoard.printCurrentBoardState();
// chessBoard.setGameBoard(b.getGameBoard());
// switchCurrentPlayer();
// chessBoard.updateBoardState(currentPlayer,
// currentOpponent);
repaint();
}
else if (this.receivedMessage instanceof String) {
chatPanel.writeToDisplay((String) this.receivedMessage);
}
} while (/* !message.equals("SERVER>>> TERMINATE") */true);
}
public void sendData(final Object obj_to_send) {
try {
this.outputStream.writeObject(obj_to_send);
this.outputStream.flush();
}
catch (final IOException e) {
}
}
}
private final class Client
extends NetworkEntity {
private final String hostName;
private final int serverPort;
public Client(final String host, final int port) {
super("CLIENT");
this.hostName = host;
this.serverPort = port;
}
#Override
public void run() {
try {
connectToServer();
getStreams();
processIncomingData();
}
catch (final EOFException eof) {
}
catch (final IOException ioe) {
}
catch (final NullPointerException npe) {
}
finally {
closeConnection();
}
}
private void connectToServer()
throws IOException {
try {
this.connectionHandle = new Socket(InetAddress.getByName(this.hostName), this.serverPort);
connectionEstablished = true;
chatPanel.writeToDisplay("Successfully connected to "
+ this.connectionHandle.getInetAddress().getHostName());
}
catch (final IOException e) {
chatPanel.writeToDisplay("Failed to connect to: " + this.hostName);
}
}
}
private final class Server
extends NetworkEntity {
private ServerSocket server;
private final int listenPort;
public Server(final int listen_port) {
super("SERVER");
this.listenPort = listen_port;
}
#Override
public void run() {
try {
this.server = new ServerSocket(this.listenPort, 1);
chatPanel.writeToDisplay("Listening on port " + this.listenPort);
try {
waitForConnection();
getStreams();
processIncomingData();
}
catch (final EOFException eof) {
// System.out.println(getName() + "exception: " +eof);
// eof.printStackTrace();
}
catch (final IOException ioe) {
// System.out.println(getName() + "exception: " +ioe);
// ioe.printStackTrace();
}
finally {
closeConnection();
}
}
catch (final IOException e) {
JOptionPane.showMessageDialog(thisFrame, "Network Error: " + e, "Notification",
JOptionPane.ERROR_MESSAGE);
}
}
private void waitForConnection()
throws IOException {
this.connectionHandle = this.server.accept();
connectionEstablished = true;
chatPanel.writeToDisplay("Connection received from:" + this.connectionHandle.getInetAddress().getHostName());
}
#Override
public void closeConnection() {
super.closeConnection();
try {
this.server.close();
}
catch (final IOException e) {
chatPanel.writeToDisplay(getName() + "failed to disconnect from the network");
}
}

Java: Multiple threads vs. sockets

I've written a simple application in Java where there are two nodes, each with a ServerSocket open to a port listening for incoming connections. The nodes run two threads each, sending 1000 messages to the other node through a persistent TCP socket created when sending the first message. However, the nodes do not receive all 1000 messages. One may receive 850 while the other only receives 650. This number tends to stay constant over multiple runs.
The sending code is as follows:
public void SendMsg(String dest, Message myMsg) {
Socket sendsock = null;
PrintWriter printwr = null;
try {
if(printwr == null) {
sendsock = new Socket(dest, Main.rcvport);
printwr = new PrintWriter(sendsock.getOutputStream(), true);
}
String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n";
printwr.print(msgtosend);
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
Performance seems to improve if I use
buffwr = new BufferedWriter(printwr)
as well and use buffwr.write(...) instead of printwr.print(...), though it doesn't seem to be a complete solution for the data loss. There are no exceptions to show that packets weren't delivered, so according to the sender, they were all sent successfully.
On the receiving end, the accepted connection is treated as follows:
BufferedReader inbuff = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
while(running) {
String rcvedln = inbuff.readLine();
if(rcvedln != null) {
count++;
System.out.println(count);
}
}
Is there an problem with how the readers and writers have been used that could be causing the problem? Thanks.
SendMsg() is creating a new socket every call, so you aren't using a persistent TCP connection. The method isn't closing the socket, either, so you have a lot of open collections. You may be reaching a limit to the number of connections the process can make (the sockets may not be closed when the objects are garbage collected).
Finally, as kd304 pointed out, the Javadoc for PrintWriter states this about the autoFlush parameter of the PrintWriter constructor: "if true, the println, printf, or format methods will flush the output buffer". Your code wasn't calling a method that did a flush.
Try this:
public class MessageSender implements Closeable {
private final Socket socket;
private final PrintWriter writer;
public MessageSender(String dest, int port) {
socket = new Socket(dest, port);
writer = new PrintWriter(socket.getOutputStream(), true);
}
public void sendMessage(Message message) {
try {
writer.println(message.toString());
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
#Override
public void close() throws IOException {
writer.close();
socket.close();
}
Note I modified the code so that sendMessage() calls Message.toString() to get the formatted message. It doesn't seem right for sendMessage() to reference fields in Message in order to format the message. Instead of using toString() you could create a method in Message specifically for this purpose.
Here's the server side code:
public class Server implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService executor;
private volatile boolean running = true;
public Server(int port, ExecutorService executor) throws IOException {
serverSocket = new ServerSocket(port);
this.executor = executor;
}
#Override
public void run() throws IOExeption {
while (running) {
Socket socket = serverSocket.accept();
executor.execute(new ConnectionHandler(socket));
}
}
public boolean stop(long timeout, TimeUnit unit) {
running = false;
executor.shutdown();
return executor.awaitTermination(timeout, unit);
}
}
You can use Executors to create an ExecutorService to run the tasks. Note that ConnectionHandler needs to close the socket it is given.
Are you closing out the PrintWriter to flush the stream?
} finally {
printwr.close();
sendsock.close();
}
Ah, sorry. I accidentally removed the commenting from the code. It's actually like this:
public void SendMsg(String dest, Message myMsg) {
Socket sendsock = null;
try {
if(printwr == null) {
sendsock = new Socket(dest, Main.rcvport);
printwr = new PrintWriter(sendsock.getOutputStream(), true);
}
String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n";
printwr.print(msgtosend);
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
printrw is declared and stored outside the function, so once it's set up, there is no need for sendsock or for reinitializing printrw. In the actual application, I'm storing the PrintWriter for every connection in a HashMap and retrieving it at the start of the SendMsg(...) function.
Since the connections are persistent, every time one is accepted, a new thread is lunch that runs a while loop to check it continuously for data. These threads and connections are only closed once the application is terminated. In addition to my previous question, is there a more efficient way of doing this?
Earlier, I'd implemented this code without the "\n" and using println(...) instead and I still had the issue of some messages not being received, so I'm not sure what is causing the problem. The messages are sent like so:
public class SendPortal2 implements Runnable {
String dest = null;
SendPortal2 (String dest) {
this.dest = dest;
}
public void run() {
for(int i=1; i<1000; i+=2) {
Message myMsg = new Message("Message", Main.myaddy + " " + String.valueOf(i));
Main.myCommMgr.SendMsg(dest, myMsg);
}
}
}
There are two such threads running. When I ran the code again just now, one side got 999 packets whereas the other one only got 500, leading me to believe sometimes the data from an entire thread could be blocked out. Is that likely?
Thanks for the replies!
If I put a Thread.sleep(2) inside the for loop where the SendMsg function is called, more messages are received properly, but it's not always 1000. Could it be possible that the system's resources are being hogged by two threads running while loops continuously?

Categories

Resources