Bi-direction multithreaded socket connection - java

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();
}
}
}
}
}

Related

Why is this message getting sent to the wrong client? (Deduping)

I have a chat program. The problem is that I am trying to disallow dupe names. Essentially, whenever a name change request is sent to the server, it is checked against the list of names currently in use and if it is already taken, the person is added to my shitlist (not allowed to post) and they are sent a message that they need to change their name.
I commented the crap out of the code since there is a lot so you can understand it easily.
The problem is that the wrong person is being sent the message that the name is already in use! I have spent the last 8 hours trying to find it and It's bloody driving me mad!
The server side code is long; I'll post the relevant bits and any further will be provided on request. I'll also link to the complete program. (Not the source, the JAR.)
JAR: https://www.mediafire.com/?4t2shjdjf7blpg2
//...Irrelevant bits ommitted...//
public class Server
{
// The server object reference
static Server server;
// Declarations:
private ArrayList<ObjectOutputStream> clientOutputStreams; // out streams
private ArrayList<String> takenNames = new ArrayList<>(); // taken names
private InetAddress ip;
private final int serverPort; // the port the server is running on
private static ObjectOutputStream changer; // the last person to change names
private ArrayList<ObjectOutputStream> shitList = new ArrayList<>();
private HashMap <InetAddress, ObjectOutputStream> ipMap =
new HashMap<>(); // <ip, outputstream>
//...Irrelevant bits ommited...//
// Don't mind this non-indentation, it is supposed to be.
public void tellEveryone(Message message, InetAddress senderIP)
{
// First check some special conditions..
if(message.getType() == Message.TYPE.IN_USE)
{
try
{
changer.writeObject(message);
}
catch (IOException e)
{
e.printStackTrace();
}
}
// If someone is on my shitlist,
if(shitList.contains(ipMap.get(senderIP)))
{
// Warn them of their sins...
Message nopeMessage = new Message(Message.TYPE.SERVER,
"You may not send any messages until you change your name!",
"Server");
try
{
ipMap.get(senderIP).writeObject(nopeMessage);
}
catch(IOException e)
{
e.printStackTrace();
}
}
else
{
// Send message normally to everyone...
// Sync, just to be safe
synchronized(clientOutputStreams)
{
for(ObjectOutputStream oo : clientOutputStreams) // while more clients...
{
try
{
oo.writeObject(message);
oo.flush();
}
catch(IOException e)
{
System.out.println("IOException caught during tellEveryone()");
e.printStackTrace();
}
}
}
System.out.println(getTimeStamp() + ": Message Sent by:".
concat(" " + senderIP + "/ " + message.getSenderName()));
}
}
The server handler inner class...
public class ServerHandler implements Runnable
{
#Override
public void run()
{
// Create a list of client out streams to send stuff...
clientOutputStreams = new ArrayList<>();
try // To establish a connection with clients
{
// Create server socket...
ServerSocket serverSocket = new ServerSocket(serverPort);
while(true) // Will always run! Blocks!
{
// Assign a client socket to any new socket connections...
// (The var used here is temp, but will be passed off soon.)
Socket clientSocket = serverSocket.accept();
// Get's the ip of the client that connected...
ip = clientSocket.getInetAddress();
System.out.println(ip + " " + "connected.");
// Create ooStream to send messages to client...
ObjectOutputStream ooStream =
new ObjectOutputStream(
clientSocket.getOutputStream());
// Add the client oo stream to the list of outputs...
clientOutputStreams.add(ooStream);
// Add user IP data to map of ip's
ipMap.putIfAbsent(ip, ooStream);
// Create new thread to run inner class ClientHandler...
Thread t = new Thread(new ClientHandler(clientSocket));
// Running the thread makes it safe to overwrite the...
// ...clientsocket variable.
t.start();
}
}
catch (IOException e)
{
System.out.println("Exception in server.run()");
// TODO: Revise
e.printStackTrace();
}
}
}
The client handler inner class
public class ClientHandler implements Runnable
{
private ObjectInputStream oInStream; // The client's input stream.
private Socket socket; // Socket to the client
public ClientHandler(Socket clientSocket)
{
try // to create an input stream...
{
socket = clientSocket; // <-- The one passed in to the method
// Potential error from previous version... REMOVE WHEN TESTED
oInStream = new ObjectInputStream(socket.getInputStream());
}
catch(IOException e)
{
System.out.println("Error establishing input stream");
}
}
#Override
public void run()
{
Message message;
try // To process incoming messages...
{
while(socket.isClosed() == false) // If the socket is open...
{
// While there are more messages...
// Also assigns to the message var.
while((message = (Message)oInStream.readObject()) != null)
{
// Passes on the message and sender info.
if(message.getType() == Message.TYPE.NAME_REQUEST)
{
changer = ipMap.get(socket.getInetAddress());
System.out.println(socket.getInetAddress());
System.out.println(changer.toString());
handleNameRequests(message);
}
else
{
tellEveryone(message, ip); // TEST CHANGE- DELETED IF TEST
}
}
// TEST TEST TEST
synchronized(clientOutputStreams)
{
int index =
clientOutputStreams.indexOf(
socket.getOutputStream());
clientOutputStreams.remove(index);
System.out.println("Removed the client in sync");
}
}
// TEST TEST TEST
socket.close(); // TEST CLOSING SOCKET WHEN DONE.
System.out.println("Sock closed after while loop in ch run()");
}
catch(IOException e)
{
System.out.println("IOException caught when "
+ "reading message.");
}
catch (ClassNotFoundException e)
{
System.out.println("Some poor sap is going to have to debug"
+ "this!");
}
finally
{
// THIS WHOLE BLOCK: TEST TEST TEST
try
{
oInStream.close();
System.out.println("just closed oinStream");
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
I FINALLY FOUND IT!
For any future people encountering a similar problem, the problem was that I was assigning the ip variable in the wrong place! This essentially resulted in the list of ip's being all the same! Another bug confounded that problem, in that when I disabled the sending ability of messages when on my shitlist (aren't programmers the darndest?), I disabled ALL types of messages, including those from the server, ect! Oops!
The lesson? Bugs hide in the darndest places. Walk through everything, and doubt what you know to be true. Assume nothing, verify everything. There are never enough print statements when debugging!

MultiThreaded Server, which is a client to another server

I'm using a server socket to accept clients on the main thread, when a thread is accepted,the clients socket is given to a handler which is started in a new thread to process communications. However, before I start running my server to access clients, it connects to a second server which it must list to and be able to respond to and pass on the messages it gets to it's clients.
Hopefully this image illustrate what I mean:
The small server must be continuously listening for input from the big server, and also able to output responses.
//Default constructor
private smallServer(){}
//method to initialise and start the server
public static void StartServer(int port) throws IOException {
smallServer ss = new smallServer();
ss.bs= new bigServerClient(ss);
Thread nsc_Thread = new Thread(ss.bsc);
bsc_Thread.start();
//accepts clients and starts new thread for them
ss.ServerRun(port);
}
private void ServerRun(int port) throws IOException {
ServerSocket server = new ServerSocket(port);
server.setSoTimeout(50);
while (run) {
Socket client = null;
try {
client = server.accept();
} catch (SocketTimeoutException e) {
}
if (client != null) {
ClientHandler handler = new ClientHandler(client, this);
Thread handleThread = new Thread(handler);
handleThread.start();
}
}
if (!run) {
synchronized (ClientHandler.handlers) {
for (ClientHandler handler : ClientHandler.handlers) {
handler.terminateHandler();
}
}
System.exit(0);
}
}
public void processBigServerCommand(String toProcess) {
System.out.println("RESEAVED: " + toProcess);
}
The big server client(on the small server) then does this:
public class bigServerClient implements Runnable {
private smalsServer ss;
private PrintWriter printer;
private BufferedReader reader;
private Socket socket;
public bigServerClient(smallServer _ss) throws IOException {
ss = _ss;
socket = new Socket("Localhost", 5000);
printer = new PrintWriter(socket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
printer.flush();
SendBigServerMessage("Starting String");
}
private void SendBigServerMessage(String toSend) {
printer.print(toSend);
printer.flush();
}
#Override
public void run() {
try {
while (ss.state()) {
String inputLine;
while ((inputLine = reader.readLine()) != null) {
ss.processBigServerCommand(inputLine);
System.out.println(inputLine);
}
}
} catch (IOException e) {
} finally {
try {
socket.close();
} catch (IOException ex) {
}
}
}
}
From what's above, can anyone see why the big server client isn't responding to the big server when a message is sent? I'm guessing it's something to do with the main thread blocking the second thread, but I'm not sure... Any help would be greatly appreciated.
You lost me in your code...
Simplify it.
Your smallServer (see class names conventions) should have persistent connection to BigServer (effectively it is BigServer client) - you can implement it in your smallServer class, it should connect (once) and open I/O to BigServer (once) and close everything once the connection is terminated.
As your smallServer will handle multiple clients and pass their requests to BigServer there is no guarantee of the order of BigServer responses - you should do something to handle that (maybe pass UUID with requests?)
Simplify your smallServer and make sure that it runs...

Object stream I/O issue, client object not updated

i'm working a client-server based game. The game is mainly caracterized by a Grid object which must be synchronized between clients. To do that I use the Object I/O Streams over Sockets.
However i encounter an issue during the synchronization process. The Grid is sent and recieved by all clients but its state is not modified after the first upload to each client.
I mean by that that clients do recieve the object in its present state when they connect but subsequent receiptions (initiated by either another client connection or previous client moves) don't present any modification over the initial sent state...
here are (stripped-down) snippets of the server side code:
while(true) //continuously accept new connections
{
//wait incoming connection, and accept it
Socket newSocket = serverListener.accept();
//create player details and save it in hashtable
Player newPlayer = new Player(newSocket); //streams saved here
Players.put(newPlayer);
//update all clients
sendGridToAll();
}
The Player class constructor:
public Player(Socket s) throws IOException
{
this.Tx = new ObjectOutputStream(socket.getOutputStream());
this.Rx = new DataInputStream(socket.getInputStream());
}
The SendToAll method:
public void sendGridToAll()
{
synchronized(Players) //do nothing while players HT is being modified
{
for(Enumeration e = Players.elements(); e.hasMoreElements(); )
{
Player tmpPlayer = (Player)e.nextElement();
ObjectOutputStream tmpTx = tmpPlayer.getTx();
try {
tmpTx.writeObject(grid);
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
print.log("Grid update sent");
}
}
and here is the client side snippet handling the object reception (ran in a thread):
public void run()
{
ObjectInputStream RX = new ObjectInputStream(socket.getInputStream());
while(true)
{
try
{
RX_grid = (Grid)RX.readObject();
}
catch (IOException ex)
{
print.log("IO Error");
ex.printStackTrace();
}
catch (ClassNotFoundException ex)
{
print.log("Bad grid class UID");
}
finally
{
print.log("grid recieved");
c.updateGui(RX_grid);
}
}
}
Thank you for your help

ServerSocket java-server reads input only once?

I have written a java server and here is the code:
try
{
ss = new ServerSocket(8080);
while (true)
{
socket = ss.accept();
System.out.println("Acess given");
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//out = new PrintWriter(socket.getOutputStream(),true);
line = in.readLine();
System.out.println("you input is :" + in.readLine());
}
}
And an iphone application is the client and there is the code for it:
- (void)viewDidLoad {
[super viewDidLoad];
socket = [[LXSocket alloc]init];
if ([socket connect:#"10.211.55.2" port:8080]) {
NSLog(#"socket has been created");
}
else {
NSLog(#"socket couldn't be created created");
}
#try {
}#catch (NSException * e) {
NSLog(#"Unable to send data");
}
[super viewDidLoad];
}
-(IBAction)sendData{
[socket sendString:#"A\n"];
}
I am having 2 problems here: first is that the server is only reading the input once. The second is that when ever I try to output the data it doesn't output until I have called the method twice (clicked on the uibutton twice). Not sure what is happening here. What am I doing wrong?
You are creating a new reader everytime in your while loop. Instead move the code outside the while loop and block on the readLine() call.
socket = ss.accept();
in = new BufferedReader(new InputStreamReader(socket.getInputStream());
String line = "";
while ( true) {
line = in.readLine();
System.out.println("you input is :" + line);
if ( "Bye".equals(line) )
break;
}
Here is an example server side program.
Since alphazero posted the pattern, I will post a brief stripped down implementation:
This is the Server:
try {
ServerSocket ss = new ServerSocket(portNumber);
logger.info("Server successfully started on port " + portNumber);
// infinite loop that waits for connections
while (true) {
SocketThread rst = new SocketThread(ss.accept());
rst.start();
}
} catch (IOException e) {
logger.info("Error: unable to bind to port " + portNumber);
System.exit(-1);
}
The SocketThread is something like:
public class SocketThread extends Thread {
private Socket communicationSocket = null;
public SocketThread(Socket clientSocket) {
communicationSocket = clientSocket;
try {
input = new ObjectInputStream(communicationSocket.getInputStream());
} catch (IOException e) {
logger.info("Error getting communication streams to transfer data.");
try {
communicationSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public void run() {
boolean listening=true;
DataObject command = null;
while (listening) {
try {
Object currentObject = input.readObject();
if (currentObject != null
&& currentObject instanceof DataObject) {
command = (DataObject) currentObject;
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
// If we got to this point is because we received a request from
// the client
// we can exit the loop
listening = false;
}
}
}
}
Note: "DataObject" is just a custom class which could be more practical since you can read the Dataobject itself from the socket without worrying about how many bytes you are reading, etc. Only condition is that DataObject is flagged as Serializable.
Hope it helps.
Tushar,
The general pattern is this (almost java but pseudo-code):
while (server-socket is accepting new connections)
{
// The server-socket's job is to listen for connection requests
// It does this typically in a loop (until you issue server-shutdown)
// on accept the server-socket returns a Socket to the newly connected client
//
socket s = server-socket.accept-connection();
// various options here:
//
// typically fire off a dedicated thread to servie this client
// but also review NIO or (home-grown) connection-map/handler patterns
// the general pattern:
// create a dedicated thread per connection accepted.
// pass Socket (s) to the handler method (a Runnable) and start it off
// and that is it.
// Here we use the general pattern and create a dedicated
// handler thread and pass of the new connections' socket reference
//
Thread handler-thread = new Thread (handler-routine-as-runnable, s);
handler-thread.start();
}

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