Java Socket Programming : server loop is getting executed infinitely - java

Though I do understand that
while(true){
}
generates an infinite loop, it is my understanding that
while(true){
blockingCall()
}
allows this loop to be executed x number of times (x could be between 0 and a number that reaches resource limits of given machine) due to the nature of blocking calls i.e. if there are 3 calls made to blockingCall() method and 3rd call never returned, that means the program should wait there. This is a theme of implementation, which is not working the way i expect it to work.
I am implementing a program of client/server using Java Sockets. https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html is a reference link to understand what my client is doing (it simply requests a connection to server running on a specific port and sends a msg. Server reverses that msg and sends back to client). I am trying to implement the server in a way such that there are limits to the number of connections this server allows. If number of clients requesting connection goes beyond this limit, then additional requests are queued up to a maximum limit. Once this max limit is surpassed, server simply writes a message to log stating "no more connections will be accepted". Below is my server program :
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.*;
public class MultithreadedServer {
private static BlockingQueue<Socket> queuedSockets = new ArrayBlockingQueue<>(1); //max queued connections.
private static Semaphore semaphoreForMaxConnectionsAllowed = new Semaphore(2); //max active connections being served.
private static void handleClientConnectionRequest(final Socket newSocketForClientConnection, final Semaphore maxConnectionSemaphore) {
new Thread(new Runnable() {
#Override
public void run() {
try (
BufferedReader socketReader = new BufferedReader(new InputStreamReader(newSocketForClientConnection.getInputStream()));
PrintWriter socketWriter = new PrintWriter(newSocketForClientConnection.getOutputStream(), true)
) {
maxConnectionSemaphore.acquire();
String serverMsg;
String clientMsg;
SocketAddress clientSocket = (InetSocketAddress) newSocketForClientConnection.getRemoteSocketAddress();
while ((clientMsg = socketReader.readLine()) != null) {
if (clientMsg.equalsIgnoreCase("quit")) {
maxConnectionSemaphore.release();
break;
}
System.out.println("client with socket " + clientSocket + " sent MSG : " + clientMsg);
serverMsg = reverseString(clientMsg);
socketWriter.println(serverMsg);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Closing client upon client's request.");
}
}
}).start();
}
private static String reverseString(String clientMsg) {
synchronized (clientMsg) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = clientMsg.length() - 1; i >= 0; i--) {
stringBuffer.append(clientMsg.charAt(i));
}
return stringBuffer.toString();
}
}
public static void main(String[] args) throws IOException {
boolean shouldContinue = true;
if (args.length != 1) {
System.out.println("Incorrect number of arguments at command line");
System.exit(1);
}
ServerSocket serverSocket = null;
try {
Integer portNumber = Integer.parseInt(args[0]);
serverSocket = new ServerSocket(portNumber);
int connectionNumber = 0;
System.out.println("Server listening on port# : " + args[0]);
//main thread...
while (shouldContinue) {
Socket newServerSocketForClientConnection = null;
newServerSocketForClientConnection = queuedSockets.poll();
if (newServerSocketForClientConnection == null) {
newServerSocketForClientConnection = serverSocket.accept();
connectionNumber++;
System.out.println("Created new socket upon client request. ConnectionCOunt = " + connectionNumber);
processConnection(newServerSocketForClientConnection);
} else {
//i.e. queue has a socket request pending.
System.out.println("Picking queued socket..");
processConnection(newServerSocketForClientConnection);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (serverSocket != null) {
serverSocket.close();
}
}
}
private static void processConnection(Socket newServerSocketForClientConnection) {
if (semaphoreForMaxConnectionsAllowed.availablePermits() > 0) {
handleClientConnectionRequest(newServerSocketForClientConnection, semaphoreForMaxConnectionsAllowed);
} else {
//System.out.println("Since exceeded max connection limit, adding in queue.");
if (queuedSockets.offer(newServerSocketForClientConnection)) {
System.out.println("connectionRequest queued because no more space on server. QueuedSocketList size : " + queuedSockets.size());
}else{
System.out.println("No space available for client connections. Can not be queued too.");
}
}
}
}
Output observed through this server, when number of client requests goes beyond semaphore limit (for some reason, i must use Semaphore in my program and can't use ExecutorService with FixedThreadPool) :
My question is : It appears that queuedSockets.poll() doesn't seem to be removing element from blockingQueue. That is why i'm getting this psuedo-infinite loop. Any clue why this is happening? I checked the documentation of blockingQueue and the doc says poll() will "Retrieves and removes the head of this queue", but doesn't seem happening for above program.

Let's step through this loop:
//main thread...
while (shouldContinue) {
Socket newServerSocketForClientConnection = null;
// poll for a pending connection in the queue
newServerSocketForClientConnection = queuedSockets.poll();
// if a pending connection exists, go to else...
if (newServerSocketForClientConnection == null) {
...
} else {
// queue has a socket request pending, so we process the request...
System.out.println("Picking queued socket..");
processConnection(newServerSocketForClientConnection);
}
}
And then in processConnection():
// if there are no permits available, go to else...
if (semaphoreForMaxConnectionsAllowed.availablePermits() > 0) {
handleClientConnectionRequest(newServerSocketForClientConnection, semaphoreForMaxConnectionsAllowed);
} else {
// BlockingQueue.offer() puts this connection immediately back into the queue,
// then the method exits
if (queuedSockets.offer(newServerSocketForClientConnection)) {
System.out.println("connectionRequest queued because no more space on server. QueuedSocketList size : " + queuedSockets.size());
}else{
System.out.println("No space available for client connections. Can not be queued too.");
}
}
After that, on the next iteration of the loop:
//main thread...
while (shouldContinue) {
Socket newServerSocketForClientConnection = null;
// poll immediately gets the same request that was
// removed in the previous iteration
newServerSocketForClientConnection = queuedSockets.poll();
// Once something is in the queue, this condition will
// never be met, so no new incoming connections
// can be accepted
if (newServerSocketForClientConnection == null) {
...
} else {
// process the same request again, forever, or until
// a connection is freed up. Meanwhile, all other
// incoming requests are being ignored.
System.out.println("Picking queued socket..");
processConnection(newServerSocketForClientConnection);
}
}
So it's not that the request is never being removed from the queue, it just get immediately put back afterwards due to being blocked by the Semaphore.

Related

How to stop server in Client Server multithreading

I am implementing a multi-threaded client-server application in java. I want to implement JDBC in this program and I want my server to retrieve data from the database whenever it is started. I will store that data in my collection instances, perform manipulations on data and when server completes execution, I need to store the data back to the database. The problem is that the server is in an infinite loop waiting for clients and I am not able to figure out how to make the server stop.
This is my server program:
import java.io.*;
import java.text.*;
import java.util.*;
import java.net.*;
public class Server
{
public static void main(String[] args) throws IOException
{
// server is listening on port 5056
ServerSocket ss = new ServerSocket(5056);
// running infinite loop for getting
// client request
while (true)
{
Socket s = null;
try {
// socket object to receive incoming client requests
s = ss.accept();
System.out.println("A new client is connected : " + s);
// obtaining input and out streams
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
System.out.println("Assigning new thread for this client");
// create a new thread object
Thread t = new ClientHandler(s, dis, dos);
// Invoking the start() method
t.start();
}
catch (Exception e) {
s.close();
e.printStackTrace();
}
}
}
}
// ClientHandler class
class ClientHandler extends Thread
{
DateFormat fordate = new SimpleDateFormat("yyyy/MM/dd");
DateFormat fortime = new SimpleDateFormat("hh:mm:ss");
final DataInputStream dis;
final DataOutputStream dos;
final Socket s;
// Constructor
public ClientHandler(Socket s, DataInputStream dis, DataOutputStream dos)
{
this.s = s;
this.dis = dis;
this.dos = dos;
}
#Override
public void run()
{
String received;
String toreturn;
while (true) {
try {
// Ask user what he wants
dos.writeUTF("What do you want?[Date | Time]..\n"+
"Type Exit to terminate connection.");
// receive the answer from client
received = dis.readUTF();
if(received.equals("Exit"))
{
System.out.println("Client " + this.s + " sends exit...");
System.out.println("Closing this connection.");
this.s.close();
System.out.println("Connection closed");
break;
}
// creating Date object
Date date = new Date();
// write on output stream based on the
// answer from the client
switch (received) {
case "Date" :
toreturn = fordate.format(date);
dos.writeUTF(toreturn);
break;
case "Time" :
toreturn = fortime.format(date);
dos.writeUTF(toreturn);
break;
default:
dos.writeUTF("Invalid input");
break;
}
}
catch (IOException e) {
e.printStackTrace();
}
}
try
{
// closing resources
this.dis.close();
this.dos.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
Here is my client program:
import java.io.*;
import java.net.*;
import java.util.Scanner;
// Client class
public class Client
{
public static void main(String[] args) throws IOException
{
try
{
Scanner scn = new Scanner(System.in);
// getting localhost ip
InetAddress ip = InetAddress.getByName("localhost");
// establish the connection with server port 5056
Socket s = new Socket(ip, 5056);
// obtaining input and out streams
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
// the following loop performs the exchange of
// information between client and client handler
while (true)
{
System.out.println(dis.readUTF());
String tosend = scn.nextLine();
dos.writeUTF(tosend);
// If client sends exit,close this connection
// and then break from the while loop
if(tosend.equals("Exit"))
{
System.out.println("Closing this connection : " + s);
s.close();
System.out.println("Connection closed");
break;
}
// printing date or time as requested by client
String received = dis.readUTF();
System.out.println(received);
}
// closing resources
scn.close();
dis.close();
dos.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
Overview
Great question! To reiterate what was stated in the above comments, you are looking for a server-side shutdown. There are some way of handling this situation, and I can explain it with a brief example.
ExecutorServer
I will run through a modified example based off this example. Below find the server implementation.
class NetworkService implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService pool;
private final AtomicBoolean shouldExit;
public NetworkService(int port, int poolSize) throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
shouldExit = new AtomicBoolean(false); // Thread-safe boolean
}
public void run() { // run the service
try {
// While we should not exit
while(!shouldExit.get()) {
try {
pool.execute(new ClientHandler(serverSocket.accept()));
} catch (SocketException e) {
if(shouldExit.get()) break; // Poison pill has been delivered, lets stop
// Error handling
}
}
} catch (IOException ex) {
pool.shutdown();
}
// Clean up the thread pool
shutdownAndAwaitTermination();
}
}
class ClientHandler implements Runnable {
private final Socket socket;
ClientHandler (Socket socket) { this.socket = socket; }
public void run() {
...
}
...
}
Here you will modify your current Server code to intimidate this structure. You have a similar make up currently but here we have added ExecutorService.
An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks.
By dispatching your ClientHandler to an ExecutorService, you are utilizing a ThreadPool. Although this comes with plenty of benefits, the most significant ones are that you have more control over your multi-threaded service, the ThreadPool will manage thread utilization, and the application efficiency will increase tremendously.
Below is how you would attempt to shutdown and terminate all remaining threads:
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
Now, the question remains how do we shutdown the server? The above code shows a improved structure, but still have the issue of blocking on a serverSocket.accept()!
Solution
There are two ideas that come to mind when thinking of this scenario; a CLI or a GUI. Both have the same semantics, and the decision is ultimately up to you. For purposes of explaining, I will refer to a CLI approach.
Poison Pill
If you implement a new Thread() that handled all incoming commands from the CLI, this thread would act as a poison pill. The idea is to deliver a poison pill to the target such that can wake up/execute and die. The thread will change the shouldExit atomic boolean to true and create a new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()).close(); to connect to the ServerSocket and immediately close it. In the above code, the application will no longer be blocking on the serverSocket.accept(). Instead, it will enter the try catch for SocketExceptions and test if a poison pill was utilized; If it was then lets clean up, if not lets error handle.
Timeout
You could also set a timeout on the ServerSocket such that it will throw an exception each time it cannot get a connection in that time interval with myServer.setSoTimeout(2000);. This will throw an InterruptedIOException and can be handled similarly to the poison pill where the flag is changed via a CLI command and it checks if it should exit in the catch block. If it should exit, lets clean up, if not lets error handle.
You can use pattern flag with volatile boolean variable, and you should place it in 'while' - when processing would be finished, turn it to false and the server would stop.
Another way - use thread pools and wait for them to finish in the main thread of your server.

Bi-direction multithreaded socket connection

The situation is as following:
There is a server and a client, which both can initiate a command/message to each other.
Because the server can send a message at any time, the listening on the socket is done in a separate thread (a ListenerThread). This is all fine. The client can send messages and receive at the same time, however, how would you know if a certain response belongs to the command you sent when the server can also initiate a new command/message to notify that something happened?
If I send a message to the server, and the server responds with "OK" in the listening thread. How would you know this is the actual response of the message/command you sent (keeping in mind this is another thread). What if the server received an update from another client and sends that update first.
This like a chat application, though with an actual response for every sent command.
example case:
Let us say that the protocol only consists of a move <playernum> [<x>,<y>] command which indicates that a player has done a move (server notifies client) or that a player wants to do a move (client notifies server). Also, the server responds with "OK" if the move was okay or with "ERR" if not.
Safe state:
move 1 [3,4]
client ---> server
OK
client <--- server
Unsafe state:
move 1 [3,4]
client ---> server
move 2 [1,2]
client <--- server
OK
client <--- server
The client did not expect this response... should responded with OK.
You have a protocol where the client can read one of three possible messages:
OK (The move you made was accepted)
ERR (The move you made was rejected)
move PLAYERID <co-ord1,co-ord2>
It is a reasonable assumption that the messages OK and ERR will only be sent back to the socket which requested a move. However a legal move is broadcast to all other players (perhaps excluding the player who moved).
Since you can receive unsolicited responses (the moves that other players make), you have correctly created a listener thread. You have not described the action your application takes when it receives a move message from another client, but I will assume that your listener thread handles that case. What remains is how to co-ordinate your move commands, and the response to that which will appear in the listener thread.
To synchronize the submission of your move command, and the response, a BlockingQueue (called queue) will be used , and shared between the client and listener. The form of this will be:
Client:
out.println(command); // Where out is the socket PrintWriter stream
String response = queue.take(); // Where queue is the BlockingQueue
// Process either `OK` or `ERR`
Listener Thread:
while ((command = in.readLine()) != null) {
if (command.equalsIgnoreCase("OK") || command.equalsIgnoreCase("ERR"))
queue.put(command);
else if (command.startsWith("move")) {
// Process a move
}
else
System.out.println("Unrecognized command="+command);
}
As you can see, the client simply submits a command, and blocks for the response of "OK" or "ERR". The requirement for processing other player moves has moved into the listener thread.
The listener processes all three conditions (Another player move, an "OK" or an "ERR"). The messages responses "OK" and "ERR" are sent back to the client. A move command is processed separately, and as such is not the responsibility of the client making the moves.
Below I have mocked working code which demonstrates these concepts. The server will randomly (with equal probability) respond with:
OK
ERR
A multiline response which includes OK and another player's move
Code:
public class MoveGame {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String command = "";
new Thread(new MoveServer()).start();
Socket socket = null;
PrintWriter out = null;
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);
try {
socket = new Socket("localhost", 5001);
out = new PrintWriter(socket.getOutputStream(), true);
new Thread(new ClientReader(socket, queue)).start();
while (!command.equals("quit")) {
command = scanner.nextLine();
if (command.startsWith("move")) {
out.println(command);
String response = queue.take();
System.out.println("Client got response="+response);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
scanner.close();
out.close();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
static class ClientReader implements Runnable {
private final Socket socket;
private final BlockingQueue<String> queue;
public ClientReader(Socket socket, BlockingQueue<String> queue) {
super();
this.socket = socket;
this.queue = queue;
}
#Override
public void run() {
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String command;
while ((command = in.readLine()) != null) {
if (command.equalsIgnoreCase("OK") || command.equalsIgnoreCase("ERR"))
queue.put(command);
else if (command.startsWith("move")) {
System.out.println("A player made a move: command="+command);
}
else
System.out.println("Unrecognized command="+command);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
static class MoveServer implements Runnable {
#Override
public void run() {
Random random = new Random();
Socket socket = null;
try {
ServerSocket ss = new ServerSocket(5001);
while (true) {
System.out.println("Listening for new connections");
socket = ss.accept();
System.out.println("New session has started");
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String command;
while ((command = in.readLine()) != null) {
System.out.println("Got command="+command);
int responseType = random.nextInt(3);
if (responseType == 0)
out.println("OK");
else if (responseType == 1)
out.println("ERR");
else {
out.println("move 1 [3,4]");
out.println("OK");
}
}
in.close();
out.close();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

Socket Programming - Java - Not receiving some messages

I just started working on Sockets with both client and server threads in the same Node. Below is my code structure -
I have a Node class which makes connections to different Nodes. It sends REQUEST messages to the Nodes and waits till it gets REPLY from all the nodes tracked using the outstandingreplies variable which is static and volatile, also has synchronised getters and setters.
Server Class spawns Processor thread which processes each message that it hears.
It modifies certain variables which furthers the code execution. On getting a REPLY message, it decrements outstandingreplies variable. It also sends messages to node which just sent the messages based on certain conditions.
Below is my code, I have removed certain details.
Node Class
// Creates connections to other Nodes(machines) and sends messages
public class Node {
public static volatile int timestamp = 0;
public static volatile int highestTimestamp = 0;
public static int csCounter = 0;
public static int nodeId;
public static int n;
public static String[] hosts;
public static int[] ports;
public static volatile int[] deferredNodes;
public static volatile boolean requestingCS = false;
public static volatile int outstandingReplies;
// skipping getters and setters for variables shared between server and client threads
// method to connect the node with other nodes
public static Connection connect() {
// skipping standar code to create sockets
}
public static void main(String[] args) {
// start the receiver
Server server = new Server();
server.start();
// make the connections with other nodes
Connection connections = connect();
while (counter <= 2) {
// create a message
Message message = new Message(); // skipping details to create a msg
// send requests to all other nodes
for (int i = 0; i < n; i++) {
if (i != nodeId) {
System.out.println("Sending message " + message + " to node " + i);
try {
connections.outs[i].writeObject(message);
outstandingReplies++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
// All REQUEST messages sent, WAIT for all replies
while (outstandingReplies != 0) {
// System.out.println("Waiting for replies");
}
// send REPLY msg based on some logic
if(logic is true){
// send messages to other nodes
Message replyMessage = new Message("REPLY", timestamp, nodeId);
try {
connections.outs[i].writeObject(replyMessage);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Connections Class
// Stores all the connections created by the Node Class
Server Class
// Listens to requests from nodesand spawns a Processor thread for each socket
public class Server extends Thread {
#Override
public void run() {
try {
System.out.println("Starting the node's receiver thread");
// create a serversocket to listen to requests
ServerSocket serverSocket = new ServerSocket(Node.ports[Node.nodeId]);
for (int i = 0; i < Node.n - 1; i++) {
System.out.println("Opening sockets");
Socket socket = serverSocket.accept();
// create a processor thread for each to read and process the incoming Messages
Processor processor = new Processor(socket);
System.out.println("Starting message processor");
processor.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Processor Class
// Processes the messages and sends reply when needed
public class Processor extends Thread {
Socket socket;
int[] replies = new int[10];
public Processor(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
try {
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream out = new ObjectOutputStream(
socket.getOutputStream());
while (true) {
System.out.println("Waiting for msg");
System.out.println("Socket is " + socket.isConnected());
Message message = (Message) in.readObject();
Node.timestamp = Math.max(Node.timestamp + 1, message.timestamp);
// printing the queue here
System.out.println("Processor received " + message);
// if message is a request
if (message.type.trim().equals("REQUEST")) {
if(condition is true){
// do something
} else {
// send reply to node who sent you message
Message replyMessage = new Message("REPLY", Node.timestamp,
Node.nodeId);
System.out.println(
"Send " + replyMessage + " to " + Node.hosts[message.nodeId]);
out.writeObject(replyMessage);
}
}
if (message.type.trim().equals("REPLY")) {
// do something
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
I run this code on two machines. The connections are established successfully and some messages are exchanged. However, in some cases the Processor class does not get message even when the socket is still connected.
I did go through some questions on missing messages with Java sockets which mentioned that when nodes gets two messages at once, it processes only the first message and the second message is buffered. I am not sure how I can check if that is the case with my code.
I am guessing something is wrong with I am handling sockets. Would really help if anyone could point where I am doing things incorrectly.
TIA

NIO SocketChannel saying there is no data when there is (or selector is not informing me)

I have a functioning client-server apparatus which can successfully connect and send messages to each other using NIO.
Right now my only confusion is how I'm supposed to continue reading when socketChannel.read() returns zero.
I have a protocol that sends the first 4 bytes as the number of incoming bytes to expect. Even with that amount, I'm running into a potential issue.
For testing, However, there are times where I might read something like:
5 // Read 5 bytes when calling socketChannel.read()
0 // Read 0 bytes when calling socketChannel.read() immediately after
When I hit the zero, I assumed that I was done reading and need to wait for more data to come to me.
When I do this however, OP_READ doesn't seem to be triggered when I perform selectNow() again later on. I checked the key and it has it's readyops() and interestops() set to 1 (which is OP_READ), but it does not want to recognize that it's time to read again.
I found that if I continue looping to read, I might get something like:
5 // socketChannel.read()
0 // socketChannel.read()
7 // socketChannel.read() (Done since I have all my bytes)
0
0
0
...
I'm confused here because this means one of:
There is no data there, so the 0 available is legitimate, but then when the rest of the data comes in, the selector refuses to return the key with selectNow()
The data is all there, but for some reason returns 0 on reading.
Am I supposed to re-register the channel after a selectNow() returns it as an active key? (Though I didn't have to between switching from OP_CONNECT to OP_READ... so I'm guessing not). I feel like blindly circling in a loop is dangerous and will waste processing cycles.
Am I just supposed to keep polling them? That makes me confused at when OP_READ actually fires then.
This was due to an error on my part, where I did not call .clear() on the bytebuffer that reads. This causes it to return 0 read even though the data has streamed in.
This example may also be of use to people who want to see how a simple client works (though with really bad exception handling). There is no guarantee this will work properly and may likely have issues since it was designed to be a quick and dirty test.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class Test {
public static final int PORT = 22222;
public static void main(String[] args) throws IOException {
Thread s = new Thread(new Server());
Thread c = new Thread(new Client());
s.start();
c.start();
}
}
class Client implements Runnable {
public Selector selector;
public SocketChannel sc;
public Client() throws IOException {
selector = Selector.open();
}
#Override
public void run() {
try {
sc = SocketChannel.open();
sc.socket().setTcpNoDelay(true);
sc.configureBlocking(false);
SelectionKey k = sc.register(selector, SelectionKey.OP_CONNECT);
boolean firstConnect = sc.connect(new InetSocketAddress("localhost", Test.PORT));
if (firstConnect) {
System.out.println("Connected on first connect, de-registering OP_CONNECT");
k.interestOps(SelectionKey.OP_READ);
}
while (true) {
int keys = selector.selectNow();
if (keys > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isConnectable()) {
boolean finishConnectResult = sc.finishConnect();
key.interestOps(SelectionKey.OP_READ);
System.out.println("Finished connection: " + finishConnectResult);
}
if (key.isReadable()) {
ByteBuffer bb = ByteBuffer.allocate(2);
int bytesRead = 0;
while ((bytesRead = sc.read(bb)) > 0) {
bb.flip();
System.out.println(bytesRead + " bytes read");
System.out.println(bb.get() + ", " + bb.get());
//bb.clear(); // If this is not commented, it will not be handled properly.
}
System.out.println("Last bytes read value = " + bytesRead);
System.exit(0);
}
}
}
Thread.sleep(5);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
class Server implements Runnable {
public Selector selector;
public SocketChannel sc;
public Server() throws IOException {
selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(Test.PORT));
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
#Override
public void run() {
boolean notSentData = true;
try {
while (true) {
int keys = selector.selectNow();
if (keys > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
sc = ssc.accept();
if (sc != null) {
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true); // Required in my application
sc.register(selector, SelectionKey.OP_WRITE);
System.out.println("Server accepted connection");
} else {
System.out.println("Got null connection");
}
}
}
}
if (sc != null && notSentData) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.put(new byte[]{ 1, 2, 3, -1});
bb.flip();
int wrote = sc.write(bb);
System.out.println("Wrote " + wrote + " bytes");
notSentData = false;
}
Thread.sleep(5);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}

Threads,Sockets and Streams

In my program I want to send some information to another computer with sockets. The first socket send some text throw the server and when the second socket receive this information it send a answer to the first socket. The problem is that another thread receive the answer and the only thing which I get is deadlock.
This is the server side:
else if(msgArray[0].compareTo("game_request") == 0){
if(userMap.containsKey(msgArray[1])){
Socket socketPlayerTwo = userMap.get(msgArray[1]);
otherPlayer = msgArray[1];
sendResult(socketPlayerTwo, "game_offer?"+username);
Boolean willPlay =Boolean.valueOf(recieveRequest(socketPlayerTwo).split("?")[1]);
if(willPlay)
playingUser.put(username,msgArray[1]);
sendResult(socket, "game_accept?"+willPlay);
}
}
This is the client side:
private void showGameRequest(String usernameOther) {
try{
int status = GameWindow.getInstance().showNotification("Game Offer","The user " + usernameOther + " offers you a game!\nDo you agree?",SWT.ICON_QUESTION|SWT.YES|SWT.NO);
if (status == SWT.YES){
otherPlayer = usernameOther;
sendRequest("send_message_to_user?user="+usernameOther+"&value=true");
GameWindow.getInstance().getDisplay().asyncExec(new Runnable() {
#Override
public void run() {
GameWindow.getInstance().startNewGame();
}
});
}
else
sendRequest("send_message_to_user?user="+usernameOther+"&value=false");
}
catch (IOException exc){
}
}
Here is the sendRequest method:
private void sendResult(Socket socket,String request) throws IOException{
DataOutputStream writer = new DataOutputStream(socket.getOutputStream());
writer.writeUTF(request);
writer.flush();
}
The client socket is created in the class Server
while (true) {
try {
Socket socket = serverSocket.accept();
new GameThread(socket,databaseManager);
} catch (IOException e) {
e.printStackTrace();
}
}
and it is put in hashmap when the user pass the login level:
if(msgArray[0].compareTo("user_info") == 0){
Integer flagUser = -1;
String[] userInfo = {msgArray[1].substring(msgArray[1].indexOf("=") + 1, msgArray[1].indexOf("&")),msgArray[1].substring(msgArray[1].lastIndexOf("=")+ 1, msgArray[1].indexOf(";"))};
Boolean status = databaseManager.checkUser(userInfo[0], userInfo[1]);
if(status){
if(!userMap.containsKey(userInfo[0])){
userMap.put(userInfo[0], socket);
username = userInfo[0];
flagUser = 0;
}
else
flagUser = 1;
}
sendResult(socket, "user_info_status?"+flagUser.toString()+"");
}
I thing I know what is the reason of the deadlock but I can't solve it. When the first user send the information to the other user he wait for response. Of course the other user send a response but this response is handle from other thread. So the deadlock is from a read method which block the thread. How can I send information from one socket to another without deadlock?
public GameThread(Socket socket, DatabaseManager databaseManager) {
this.socket = socket;
this.databaseManager = databaseManager;
parser = new RequestParser();
authorizationControl = new AuthorizationControl(databaseManager);
communication = new SocketCommunication(socket);
start();
}
Could you show us more of your code?
sendResult(socketPlayerTwo, "game_offer?"+username);
recieveRequest(socketPlayerTwo);
sendRequest("send_message_to_user?user="+usernameOther+"&value=true");
for starters.
Judging by the little i'm seeing you have a problem with the order in which you're using them. As far as i can tell you have a inputSteram.readObject() method that has blocked somewhere waiting for a message from the other side.

Categories