How to connect multiple players via sockets in java? - java

I'm currently working on a tcg (Trading card Game) in java. The UI for the players and the game itself are working fine, I just need to connect them now. I have a class Game.java that contains all the information about the current match as well as the methods to manipulate that state (for example, playing a card). Then I have the GUI (a JavaFX application) which is supposed to get the appropriate information from the Game instance and ddiplay it to the user as well as sending out requests to do stuff like playing a card. As far as I understand I now need to create a Server class that connects to 2 sockets and checks the requests. However, all the examples I found online only echoed Strings and never manipulated a data structure or returned something like an int. How would I go about that?
One example I found went like this:
public class EchoMultiServer {
private ServerSocket serverSocket;
public void start(int port) {
serverSocket = new ServerSocket(port);
while (true)
new EchoClientHandler(serverSocket.accept()).start();
}
public void stop() {
serverSocket.close();
}
private static class EchoClientHandler extends Thread {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public EchoClientHandler(Socket socket) {
this.clientSocket = socket;
}
public void run() {
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
if (".".equals(inputLine)) {
out.println("bye");
break;
}
out.println(inputLine);
}
in.close();
out.close();
clientSocket.close();
}
}
There are 2 things missing: How do I get a different output type, like an int, and how do I respond to input that changes the game state? For example when someone plays a card I want to show it in both GUIs not just in the one that sent the request.
Another thing I'm interested in is how to open up a new game. Since only 2 players should ever get placed together in a game, the first connection should wait for the 2nd client to start a game. And then, when I get 2 more, i need to open another game that runs parallel to it.

Related

Implementing a simple java TCP server

There are two readers plugged to the internal network. They just send data to the server machine, where I want to have this java app running receiving the data. It won't be that much data. Each reader could be transmitting single strings like "1234567" to a rate as much as maybe half a dozen times per second.
Without entering into much detail about the readers, they are datalogic and both are different models, but with this in common: They are configured to transmit the data via tcp/ip to a certain ip:port.
I tested the data trasmission with this software: https://www.hw-group.com//products/hercules/index_en.html to see if I'm receiving the data properly and it does check out, it works well.
The problem comes when I run my TCP server implementation: I receive the data from one of the devices 100% of the times, while the other one is hit and miss: Sometimes the data sent over the network never makes it to my app and I don't know why.
I'm pasting the code of what I'm using: It's simple but, with my java knowledge and after digging around the internet, it's the best I came up with.
Here's the java file with the main:
package tcpserverclasstest;
public class TCPServerClassTest {
public static void main(String[] args) throws InterruptedException {
TCPServerThread myTCPServerThread = new TCPServerThread();
myTCPServerThread.start();
}
}
Here's TCPServer.java:
package tcpserverclasstest;
import java.io.IOException;
import java.net.Socket;
import java.io.*;
public class TCPServer extends Thread {
public static final int PORT_NUMBER = 4413;
protected Socket socket;
public TCPServer(Socket socket) {
this.socket = socket;
System.out.println("New client connected from " + socket.getInetAddress().getHostAddress());
start();
}
public void run() {
InputStream in = null;
try {
in = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String request;
request = br.readLine();
System.out.println("Message received:" + request);
} catch (IOException ex) {
System.out.println("Unable to get streams from client");
} finally {
try {
in.close();
socket.close();
System.out.println("Socket closed");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
And here's TCPServerThread.java:
package tcpserverclasstest;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerThread extends Thread {
public static final int PORT_NUMBER = 4413;
protected Socket socket;
public void run(){
System.out.println("Waiting for incoming connections on port " + PORT_NUMBER);
ServerSocket server = null;
try {
server = new ServerSocket(PORT_NUMBER);
server.setReceiveBufferSize(262144);
server.setReuseAddress(true);
while (true) {
new TCPServer(server.accept());
}
} catch (IOException ex) {
System.out.println("Unable to start server.");
} finally {
try {
if (server != null)
server.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
I tried to implement the server on a thread since I want to implement this into a bigger project in the future and I don't want that app locked in the loop that is waiting for connections.
Any clues on what should I do to see why I'm not receiving some of the data? Any suggestion on a better way to achieve this?
Thanks!
first thing that comes to mind is your server only connects to one of the clients. Your TCPServerThread's run method listens for one connection and when it is made it initializes a TCPServer with this connection, meaning only one of the readers connects. Maybe after connecting to one reader you can listen for other.
Second, your TCPServer reads only one line and then closes the connection, and a line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed. So your readers should send linefeed ('\n', or '\r', or '\r\n'). If not you shouldn't use readLine, maybe read some amount of data in a while loop.
To debug your TCP connections and really be sure the data is actually coming to your server, you can use WireShark. This shows all the packets coming in your network card and you can filter based on tcp.port to see only your connections.

How do I call a method in a specific socket thread using "implements Runnable"?

I have a working Java Server (Although a tad rough around the edges) which contains 3 main classes.
The first class runs the server and gets the socket to listen on a port and passes new connections to a client handler.
The second class is a threaded client handler
The third is a protocol class which is called from the client handler and processes information. Once the information is processed, the protocol class returns a processed or formatted response back to the client handler to pass to the client.
The advantage is that the second class just needs to be loaded with what is acceptable data to accept from the socket. The data can be passed to the protocol handler, and the protocol handler can be loaded with whatever protocol you want the server to use to talk to the client.
In this instance I have loaded in a telnet-based chat class.
If, for example, someone leave the chat the client handler class may execute code such as:
for (i = 0; i < currentClientsConnected; i++) {
if(threads[i] != null && threads[i] != this) {
outputLine = threads[i].serverprotocol.processInput("** " + username + " has left the room **");
threads[i].out.printf(outputLine);
}
}
This passes "** [username] has left the room **" to the serverprotocol class, which then returns the data in the best best way to transmit the message to the clients. In this case the serverprotocol class formats the message with telnet control code which tells the client to re-draw the screen, add the new message and scroll up the existing current messages in the buffer.
I may also only want the client handler class to send message to sockets where the users are in certain chat rooms for example, so I will not want to always send to all the sockets.
In my code, this is Class 1 - the server class which accepts sockets with:
while (true) {
int i;
// Try and accept the connection
try {
clientSocket = serverSocket.accept();
// System.out.printf("Remote IP:");
// System.out.printf(clientSocket.getRemoteSocketAddress().toString());
// Find an unused socket if one is available
for (i = 0; i < maxClientsAllowed; i++) {
// If found create thread
if (threads[i] == null) {
(threads[i] = new clientThread(clientSocket, threads)).start();
break;
}
}
// If all sockets are taken
if (i == maxClientsAllowed) {
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
out.printf("Server too busy. Try later.\n");
out.close();
clientSocket.close();
}
} catch(IOException e) {
System.out.println(e);}
}
and Class 2 is a class which extends thread:
class clientThread extends Thread {
private String clientName = null;
private DataInputStream in;
private PrintWriter out;
private Socket clientSocket = null;
private final clientThread[] threads;
private int currentClientsConnected;
private serverprotocol serverprotocol;
public clientThread(Socket clientSocket, clientThread[] threads) {
this.clientSocket = clientSocket;
this.threads = threads;
currentClientsConnected = threads.length;
}
public void run() {
//stuff
}
}
I have been trying desperately to see if I can get this working using implements Runnable instead, but I have had no luck in calling a thread's processInput (or maybe that should read dataToBeProcessed) method based on the instance number of the thread (Simply called i in the code here).
The closest I have seen in:
https://github.com/ico77/chat-server-client/blob/master/src/main/java/hr/ivica/chat/server/ChatServer.java
which can take advantage as running the server as a threaded pool server.
However the sendToAll function in this case writes directly to the PrintWriters associated to the socket via a HashMap. The server does not let you send to individual protocol handler classes, or even to the individual ChatServerWorker class instances. This means I can't, for example, send a message to socket 1 and 3 only and then a separate message to socket 2.
I can't find a single example online where an instance of a socket handler can be called without using extends Thread.
Specifically,I want to keep the ability to use lines like:
threads[i].out.printf(outputLine);
or
if(threads[i].[class].[var] == 'something') {
// stuff
}
Where an integer can be used to reference the thread instance, or any class vars or methods used by that thread.
Am I missing something?
Your big problem is that you are using the Threads themselves directly as the communication layer between the Server and the Client threads, something you should not do.
Instead, create your own interface Message objects that communicate the different information between the threads, and use LinkedBlockingQueue to process them.
You should probably have:
One queue for the server to receive messages
Depending on your implementation, one queue for each of the client threads to receive messages from the server, or one queue that's shared (if it's designed so that any thread can handle any message).
So you might do something like:
Message:
public interface Message {
accept(Server server);
}
Disconnection Message (I'm just going to do one):
public class DisconnectionMessage implements Message {
String username;
public void accept(Server server) {
server.handleMessage(this);
}
}
Server Runnable:
public void run() {
while(isServerOnline()) {
Message clientMessage = queue.poll();
clientMessage.accept(this);
}
}
public void handleMessage(DisconnectionMessage msg) {
// code
}
public void handleMessage(ConnectionMessage msg) {
// code
}
etc.
Client Runnable:
private final Socket socket;
private final BlockingQueue<Message> queue;
public Client(BlockingQueue<Message> queue, Socket socket) {
this.queue = queue;
this.socket = socket;
}
public void run() {
while(true) {
Message msg = receiveMessage();
queue.offer(msg);
}
}
I am not sure if I understood your question.
The short answer: if you want to make clientThread a Runnable, just do it and then change the line
(threads[i] = new clientThread(clientSocket, threads)).start();
into
(threads[i] = new Thread(new clientThread(clientSocket, threads))).start();
If you look at the documentation:
http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#Thread(java.lang.Runnable)
Threads accept objects with Runnable supertype.
The long answer: you should not store threads directly but make an abstraction that represents a Client on the server side. This abstraction should encapsulate functionality for communication. That way, if you want to implement a different communication library you can easily subclass it and avoid breaking the open-close principle.
https://en.wikipedia.org/wiki/Open/closed_principle
Good luck.

Using a Synchronized collection between threads

Code -> http://pastebin.com/1PFCGWQy
Blocks that I'm having problems with
class ClientSender implements Runnable {
Socket server;
ServerClientFrontEnd SCFE;
public ClientSender(Socket server, ServerClientFrontEnd SCFE){
this.server = server;
this.SCFE = SCFE;
}
public void run(){
try(ObjectOutputStream out = new ObjectOutputStream(server.getOutputStream())){
//System.out.println("Client chat ver. 0.1");
//Scanner get = new Scanner(System.in);
while(!server.isClosed()){
//System.out.print("YOU:");
if(!SCFE.synchronizedOutputCollection.isEmpty()) // Here
{
logger.info("Has made it to ClientSender!");
String string = SCFE.synchronizedOutputCollection.firstElement();
logger.info(string);
out.writeObject(string); // Here
logger.info("Output Queue: " + SCFE.synchronizedOutputCollection.toString());
}
//else{ logger.info("It failed the conditional"); }
}
} catch (IOException ex) {
//logger.info("Closing connection...");
//System.exit(0);
}
}
}
class ClientReceiver implements Runnable {
Socket server;
ServerClientFrontEnd SCFE;
public ClientReceiver(Socket server, ServerClientFrontEnd SCFE){
this.server = server;
this.SCFE = SCFE;
}
public void run(){
try(ObjectInputStream in = new ObjectInputStream(server.getInputStream())){
while(!server.isClosed()){
SCFE.ChatBox.setText(SCFE.ChatBox.getText() + "\nOTHER: " + (String) in.readObject()); //Here
logger.info("Receiver has read object!");
}
} catch (IOException ex) {
logger.info("Closing connection");
System.exit(0);
} catch (ClassNotFoundException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
For some reason, I can not get this to work. I already got it working on a command-line environment, quite perfectly, but I wanted to port it to a graphical user interface and this problem has had me stumped for more than on hour. I didn't know how to handle the fact that Client's original class called other threads which I needed to send and receive the information to and from the server.
Basically, my program works by having the client connect to the server via a ServerSocket, which THEN processes each request. Of course, I've just recently learned about sockets on Thursday but I wanted to make a program of my own... anyway, moving on, the problem is with the ServerClientFrontEnd Class, which for some reason, and I don't know how for the life of me, the collection I'm using to get the inputted text either remains empty or it just will not read from it.
Maybe it might have to do with my while loop, but it worked perfectly before. I have a TON of loggers everywhere to log everything, and if I add an else statement when it checks if the collection is empty, it definitely activates the else statement repeatedly, EVEN AFTER the synchronizedOutputCollection was given a value. In fact, I even print the value inside of the collection when the send button is pressed. In fact, when I attempt a similar print statement inside the thread, the collection is empty and it remains empty.
How can I share a synchronized collection of objects among threads? This question is plaguing me and I would really appreciate a reply.
Also this is runnable, you just have to activate server and 2 clients to test it. P.S I have tried BlockingQueues but they make the GUI thread to freeze up because the queue is never read from, causing a deadlock.
As #markspace pointed out in a comment you have lots of funny things going on in your code. You should take a step backward, go back to the command line interface and rework your entire class structure. Remove those inner classes, use some interfaces like MessageListener or ConnectionListnener that your client or server uses to talk to other classes (like your GUI) about things like messages received or connection created/lost.
When you get done your client main method should look very simple:
public static void main(String [] args) {
Client client = new Client("127.0.0.1");
client.addMessageListener(new MessageListener() {
public void messageRecieved(String message) {
System.out.println(message);
}
});
client.connect();
System.out.println("Connected to server.");
Scanner scanner = new Scanner(System.in);
String userInput = null;
boolean quit = false;
do {
userInput = scanner.readLine();
if(userInput != null && userInput.equals("quit")) {
client.sendMessage(userInput);
} else {
quit = true;
}
} while(!quit);
}
Of course I just made this up but its just an example of once you have your class structure properly broken out and things where they should be it will be very easy to hook a GUI up.
The list could go on but bottom line is you need to take a hard look at what classes need to know what information and why. Break apart classes and make fields private and dont share information unless they need to be shared! Its important that you really think about reducing code coupling.
Any way enough rambling and onto the actual problem with your code: in ServerClientFrontEnd.main you have this snipplet:
new ServerClientFrontEnd().startClient();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ServerClientFrontEnd().setVisible(true);
}
});
You are creating 2 instances of ServerClientFrontEnd, one that starts the client the other that shows the GUI. The one that shows the GUI is the one where you change the List of strings and the other list is always empty. To make it work change the snipplet to read:
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
ServerClientFrontEnd fontEnd = new ServerClientFrontEnd();
fontEnd.startClient();
fontEnd.setVisible(true);
}
});

Java - check if ObjectInputStream.readUTF() is null, or run in separate threads

I am trying to make a chat server (as a smaller part of a game I'm coding) that accepts two clients. The way I have done this so far is by creating two completely different sets of a Socket, ObjectInputStream, and ObjectOutput stream.
private JTextField userText;
private JTextArea chatWindow;
private ObjectOutputStream output;
private ObjectInputStream input;
private ObjectOutputStream output2;
private ObjectInputStream input2;
private ServerSocket server;
private Socket connection;
private Socket connection2;
static final int PORT = 6789;
The outputting is working just fine, but the input method is not working as planned. This is the input method:
//runs while conversation is active
private void whileChatting() throws IOException{
waitForConnection();
setupStreams();
String message = " You are now connected! ";
sendMessage(message);
ableToType(true);
do{
message = input.readUTF();
showMessage("\n" + message);
message = input2.readUTF();
showMessage("\n" + message);
}while(!message.contains("END"));
}
The line message = input.readUTF(); is waiting for there to be something to read. Is there a way to check if there is something to read and only set message equal to it if not null? No, if(input.readUTF() != null) does not work. Alternatively, I think there would be a way to do this with multiple threads, but I do not have a good grasp on how threads work, so if someone could give me an example, that would be very helpful.
Thanks.
readUTF() is considered a blocking call, which means when it is called, it will hold up the thread until it returns a value. Even if you were to nullcheck, it will still block your thread from continuing until something came through the stream (whether it be a string or a null).
To handle something like a multithreaded connection:
Create a class that implements Runnable
class User implements Runnable {
}
In that class, add you in\out stream. Make sure they're aren't static so they're instance variables. You are gonna want a new in/out stream for each connection
class User implements Runnable {
DataOutputStream out;
DataInputStream in;
Socket socket;
public User(Socket s) {
socket = s;
}
public void run() {
try {
out = new DataOutputStream(socket.getOutputStream());
//same with inputstream
String input;
while(!(input = in.readUTF()).equals("END")) {
//do something with input
}
}
}
}
When your server accepts a connection...
public static void main(String[] args) {
ExecutorService executors = Executors.newCachedThreadPool(); //contains your threads
ServerSocket ss;
while(true)
executor.execute(new User(ss.accept()));
}
ServerSocket.accept() is also a blocking call, which means the loop it's being called in will block (wait) until a user is accepted. This is called Blocking-IO.
All read calls from java.io are blocking.
DataInputStream.readUTF
DataInputStream.readInt
ObjectInputStream.readObject
If you want a system where methods such as these don't block, I suggest looking into the java.nio package (new IO). It far more advanced in my opinion, especially for someone who doesn't have a grasp on basic networking yet, but non-blocking IO's allow for your underlying OS to inform your application when to read/write (through a selector), thus removing the need for blocking calls.
Other than that, there is no way you can prevent readUTF to stop blocking, or somehow skip it without data coming through it.

Server with multiclients via thread

I'm having a bit of trouble, now I have looked at this tutorial
http://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html
This tutorial gives you a server that multiple clients can connect to, when they connect to the server they are told to go along with a knock knock job, now I understand how to transfer the data and what not, but how does the threads work?
I'm working on a networked pong game where a server will hold the positions and pass them to the clients, now I have a client connected to the server and the ball position is passed to the client, works fine, a bit jumpy but I'm sure a thread with .sleep will help. but anyways my question is, how can i get my client to become a thread? and how can I store them?
For example here is the knock knock server multiThread class
package knockKnockServer;
import java.net.*;
import java.io.*;
public class KKMultiServerThread extends Thread {
private Socket socket = null;
public KKMultiServerThread(Socket socket) {
super("KKMultiServerThread");
this.socket = socket;
}
public void run() {
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String inputLine, outputLine;
KnockKnockProtocol kkp = new KnockKnockProtocol();
outputLine = kkp.processInput(null);
out.println(outputLine);
while ((inputLine = in.readLine()) != null) {
outputLine = kkp.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye"))
break;
}
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And here in the server we have
package knockKnockServer;
import java.net.*;
import java.io.*;
public class MultiKKServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(-1);
}
while (listening)
new KKMultiServerThread(serverSocket.accept()).start();
serverSocket.close();
}
}
Now looking at the server it will create a new KKMultiServerThread on each connection, but how can i store them? can i make a array of KKMultiServerThread?
I tried to make an array of KKMultiServerThread
and when i try this line
multi[0] = new KKMultiServerThread(serverSocket.accept()).start();
I get this error "cannot convert void to Thread"
If anyone can shine some light on my problem it would be great.
Canvas
Update
I now have my own thread class
package Pong;
import java.net.*;
import java.io.*;
public class PongPlayerThread extends Thread
{
private Socket socket = null;
private String pongData = "";
public PongPlayerThread(Socket socket, int id)
{
super("PongPlayerThread");
this.socket = socket;
}
public void passData(String data)
{
pongData = data;
}
public void run()
{
try
{
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
while(true)
{
out.println(pongData);
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
the pongData is a string that holds all the information together in a string, now if i declare a player1 at the top of my pong server like so
private static PongPlayerThread player1;
and do this line when it is listening
while(listen)
{
PongPlayerThread player1 = new PongPlayerThread(serverSocket.accept(), 0).start();
}
it gives me this error "cannot convert from void to PongPlayerThread" how do i fix this?
Your array declaration is missing the object type
KKMultiServerThread multi[0] = new KKMultiServerThread(serverSocket.accept()).start();
Why bother though? Unless the threads needs to communicate with each other, letting the threads run freely is ok. The Run() method defines the entire lifetime of the socket as far as the server is concerned. Each thread has a separate copy of the state of the game (as long as you don't use statics) and will happily communicate with the client without any extra intervention.
This is a case where the Socket/Thread library in Java is doing you a big favor, don't make it more complicated unless you have a specific need.
When ever a client connects to the server. The server will typically create a new thread specifically for that client. Here is some pseudo code:
WHILE SERVER IS RUNNING
SERVER WAITS FOR A CLIENT TO CONNECT
SERVER ACCEPTS THE CLIENT IF THERE IS ENOUGH MEMORY TO CREATE A NEW THREAD
SERVER CREATES A NEW THREAD ROUTINE FOR THE CLIENT PASSING THE CLIENT INFORMATION TO THE THREAD
SERVER CONTINUES TO LISTEN WHILE EACH THREAD IS SPECIFICALLY TAILORED FOR THE CLIENTS
REPEAT
You asked what steps are needed to reduce lag? Well for starters, set a maximum allowed connections. You do not want 5000 clients having their own thread. Unless your machine can handle all that and still run. Use UDP instead of TCP, and data compression try to minimize bandwidth don't send 50 GB of information at a time; if all you need is a couple of bytes of information to send. Try to send information of positions not as strings but in bytes. For example you can send the position X=5Y=0 as 50 and parse the first decimal digit as X and the second decimal digit as Y.
Instead of passing the client socket inside the thread routine pass a unique identifier for the client. Since Pong is two players limit the connections to two clients. 0 for Player 1 and 1 for Player 2. So
new KKMultiServerThread(clientID).start(); // clientID is of type int
Edit:
int id = 0;
while(serverIsRunning)
{
Client client = server.accept();
if (id > 2) client.Close(); // Do not accept.
Thread.New(id).Start();
id++;
}

Categories

Resources