I'm making a multyplayer game and I'm using java sockets for the server, the server is working very well but I think it needs some tweaks regarding the way I'm parsing/handling the requests.
I would like to know if there is a better way of parse the requests instead of splitting input lines by (,) commas like I'm doing.
Should the data sent between the client and server be encrypted in some way? Just a small encryption to obfuscate the requests on sniffers eyes. What is the best way of doing that?
And regarding the thread safe is it OK with Collections.synchronizedList and synchronized blocks on iterations? Or is there a better/cleaner way?
Finally is there any java sockes library that will do all those things above? If so should I use it or is that an overkill for a small java sokets game server.
Is any thing else that I should improve?
Thanks :)
Bellow is the basic structure of my server.
public class Servidor extends Thread {
private ServerSocket serverSocket;
public static boolean LISTENING = true;
private final List<Client> clients = Collections.synchronizedList(new ArrayList<Client>());
private final List<Game> games = Collections.synchronizedList(new ArrayList<Game>());
public Servidor() {
try {
serverSocket = new ServerSocket(SERVER_PORT);
} catch (IOException e) {
Log.add("error starting server: " + e);
}
}
#Override
public void run() {
// Wait for players to connect
while (LISTENING) {
try {
Client c = new Client(serverSocket.accept());
clients.add(c);
c.start();
} catch (IOException e) {}
}
}
class Client extends Thread {
private Socket socket;
private PrintWriter out;
private BufferedReader in;
private boolean loggedin;
private Player player;
private Game game;
public Cliente(Socket sock) {
socket = sock;
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8")), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
} catch (IOException e) {
Log.add("error connecting to player!");
}
}
/* Send to this player */
public void send(String s) {
out.println(s);
}
/* Send to all players on the server */
public void boardcast(String s) {
synchronized (clients) {
for (Client c : clients) {
c.send(s);
}
}
}
/* Get player by ID */
private Player getPlayerByID(int playerID) {
synchronized (clients) {
for (Client c : clients) {
if (c.player != null) {
if (c.player.getID() == playerID) {
return c.player;
}
}
}
}
return null;
}
/* Split inputLine */
private String[] splitInput(String input, int argsNumber) {
String[] args = null;
try {
args = input.split(",", -1);
if (args.length != argsNumber) {
args = null;
}
} catch (Exception ex) {
Log.add("error splitting input");
} finally {
return args;
}
}
#Override
public void run() {
char cmd;
String inputLine, outputLine;
String[] args;
try {
loop:
while ((inputLine = in.readLine()) != null) {
// check if inputLine have 2 chars (CMD_TYPE:)
if (inputLine.length() < 2) {
kickPlayer();
break loop;
}
// get CMD
cmd = inputLine.charAt(0);
// remove (CMD_TYPE:) from inputLine
inputLine = inputLine.substring(2);
// check e player is logged (L:username,password)
if (!loggedin) {
// check if the input string have 2 arguments
if ((args = splitInput(inputLine, 2)) == null) {
kickPlayer();
break loop;
}else{
// ... TESTE LOGIN ON DATABASE ...
// set player data
player = new Player(query.getInt("player_id"), query.getString("username"), query.getInt("level"))
}
}else{
// Commands
switch (cmd) {
// P:CARD_ID,TARGET_ID eg:(P:5:3)
case CMD_PLAY:
// check if the input string have 2 arguments
if ((args = splitInput(inputLine, 2)) == null) {
kickPlayer();
break loop;
} else {
// ... VALIDATE OTHER PARAMETERS ...
// update game
game.addCard(args[0], args[0]);
// update players
boardcast(CMD_PLAY + ":" + player.getID+ "," + game.LastCard());
}
break;
// ... TEST OTHER COMMANDS ...
default:
Log.add("invalid command";
break loop;
}
}
}
} catch (IOException e) {
Log.add("connection lost";
} finally {
removeClient();
}
}
}
}
I am a programmer for 3 years now but now I am really trying to learn network server programming.
Some tips that might help you:
You should take a look about some design patterns to make you code more organized and extensible. You can specify a class for every message type and wrap it. It can use text delimiters or message size for framing the messages.
Keep the message handling code separated from the Client class you created, this makes easier to add support to new messages.
This would be something like:
class ClientHandler
{
public void run()
{
while(isConnected())
messageRouter.handleMessage(getNextMessage());
}
}
interface MessageHandler
{
public boolean canHandle(Message m);
public void handleMessage(Message m);
}
class MessageRouter
{
private List<MessageHandler> handlers;
public void handleMessage(Message msg)
{
for(MessageHandler m : handlers)
{
if(m.canHandle(msg))
{
m.handle(msg);
return;
}
}
throw UnsupportedMessage();
}
}
Something like that...
BTW: I am Brazilian too
good luck with your project
Adding basic security to communication is fairly simple and can be achieved without a lot of hassle. It took me a day of "googling" to figure out how to use it ... I have forgotten most of the details but if you want I can upload the source of my project for you to have a look at ... I personally would recommend some encryption before actually releasing the game...
A link for java ssl sockets can be found here
A link to create your own keystore can be found here
That should help you get started and message me if you want me to upload my source code.
As far as the lists are concerned most of the time you will only be reading the lists so I would suggest that you make a custom lock so that writers must acquire write lock whereas multiple readers can acquire the lock concurrently.. Will require you to design a new class or perhaps you can find a solution on-line.
As far as parsing the string is concerned, to the best of my knowledge objects can be passed remotely so you can transmit array of string instead of having to parse them. I personally broke my communication into several calls to read and write to input streams (probably the slowest approach :) )
and any ways good luck with your project. Message me if you need the source :)
.
Related
im making a networked game that has a server which creates a clientHandler thread every time a client joins. I want to ask the first client that joined if it wants to start the game every time a new client joins, giving it the current number of players connected. Writting through the clientHandlers printwritter gives a nullPointerException, even though ive started the thread before doing this. what could be the problem?
Here is the server code:
`public class Server implements Runnable{
private ArrayList<ClientHandler> handlers = new ArrayList<>();
private ArrayList<Player> players = new ArrayList<>();
private Game game;
private boolean start;
public static void main(String[] args) {
Server server = new Server();
Thread s = new Thread(server);
s.start();
}
public void login(String name){
//todo
for (ClientHandler c : handlers){
if (c.getName().equals(name)){
alreadyTaken(name);//todo
}
else{
players.add(new HumanPlayer(name,c));//todo
}
}
}
public void setStart(){
start = true;
}
private void alreadyTaken(String name) {
}
public void setTurn(ServerHandler sh){
//todo
}
public void updateView(){
}
public String hello() {
return "Hello"; //?
}
public void place(String s){
}
#Override
public void run() {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(1800);
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("----Server----");
while (!serverSocket.isClosed()) {
try {
Socket socket = serverSocket.accept();
ClientHandler handler = new ClientHandler(socket,handlers,this);
handlers.add(handler);
Thread h = new Thread(handler);
h.start();
System.out.println("A new client has connected");
System.out.println(handlers.get(0));
handlers.get(0).out.println("START? "+ handlers.size());
if (start){
System.out.println("start request works");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
`
And here's the client handler code:
`public class ClientHandler implements Runnable{
private Socket socket;
private ArrayList<ClientHandler> handlers;
private Server server;
public PrintWriter out;
private BufferedReader in;
private String name;
public ClientHandler(Socket socket, ArrayList<ClientHandler> handlers, Server server){
this.socket = socket;
this.handlers = handlers;
this.server = server;
}
public void broadcastMessage(String msg){
System.out.println("Broadcasting");
for (ClientHandler s : this.handlers){
s.out.println("Player: " + msg);
}
}
public static String removePrefix(String s, String prefix)
{
if (s != null && s.startsWith(prefix)) {
return s.split(prefix, 2)[1];
}
return s;
}
public String getName(){
return name;
}
#Override
public void run() {
try {
out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
new Thread(() -> {
while(socket.isConnected()){
String msg;
try {
msg = in.readLine();
while(msg!=null){
switch (msg.split(" ")[0]){
case "LOGIN":
name = removePrefix(msg,"LOGIN ");
server.login(name);//todo
break;
case "HELLO":
server.hello();//todo
break;
case "PLACE":
server.place(removePrefix(msg,"PLACE "));
break;
case "QUIT":
//todo
break;
case "STOP":
//todo
break;
case "START":
server.setStart();
default:
broadcastMessage(msg);
break;
}
msg = in.readLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}`
I tried making a method in the client handler class which does the same thing. The server would just call that instead of writting directing through the PrintWriter, but i got the same error.
Starting a thread does not mean it is guaranteed to actually finish executing the first statement in its run() method before start() returns. In fact,
Usually it won't - starting a thread takes some time, and start() returns as soon as it can.
A JVM that runs a few statements in the thread you just started before start() returns is 'correct' - that is fine. A JVM that doesn't is also fine. Generally you don't want threads, because nothing is predictable anymore. At the very least you want to keep 'inter-thread comms' down to a minimum. Anytime a single field is used from more than one thread, things get very tricky.
What you need is synchronized or other tools to insert predictability in this code.
First, fix a bug
Your ClientHandler's run() code starts another thread for no reason. Take all that out, your run() method in ClientHandler should set up out and in and then immediately do while (socket.isConnected())
Synchronizing
At the very basic level, make a locker object and use notify/wait:
private final Object lock = new Object();
#Override public void run() {
try {
synchronized (lock) {
out = ...;
in = ...;
lock.notifyAll();
}
while (socket.isConnected()) { ... }
out definitely cannot be public here, you can't refer to a stream from multiple threads and expect things to work out!
Just 'fixing' your code involves then using something like:
public OutputStream getOutputStream() {
synchronized (lock) {
while (out == null) {
lock.wait();
}
}
return out;
}
Which will ensure that any thread that wants the out will wait for the other thread to get far enough, but, really, this is just setting you up for another 20 threading problems down the line. Instead, you want one object responsibile for all communication (both outgoing and incoming), and a concurrency-capable queue (there are various collections in the java.util.concurrent package good for this). Then:
Any other threads that want to just send data dump their message in the queue.
You have either 1 thread doing all comms, or 2 (one doing incoming, and one doing outgoing), both dedicated. The outgoing one just loops forever, grabbing objects from the queue and sending them.
If a thread wants to send a message and wait for the response, you need to use .wait() or nicer API from e.g. java.util.concurrent, or, use callback hell - you pass a closure with the code to run once the result is received.
I'm working on an academic project that consists in controlling a simulated environment created in Unity3D, from a conventional Android application. I already created the scenario in Unity and the mobile application is completely finished, my problem is in the connection. I chose to use sockets because of its simplicity. I managed to connect the application to a server written in C # through conventional Sockets, I know that information can be sent, but when I implemented it in Unity, everything failed, so I decided to use TCP Listener instead of Sockets in Unity / C # (the client Android still uses a conventional TCP connection through Sockets), and in effect, the application connects but there is no information transfer, because the Unity console throws an error that says: ObjectDisposedException: Can not access to disposed object.
Object name: 'System.Net.Sockets.TcpClient'.(The error is presented in line number 56: stream = client.tcp.GetStream ();) The main objective is to obtain the flow of information that would be represented in something like: "1: 0: 1: 0" and being able to make a Split to that string and according to that value change the state of a light bulb or other element, nevertheless I need the essential thing: to establish the connection flow. I'm not an expert in C # and much less using Unity, I really do not know much about design, but I wanted to do it to deliver an innovative work, I hope someone can guide me.
PD: C# Socket (No Unity yet) server working with Android Java client:
![1] https://imgur.com/a/HuNcDC3
//This is my Unity3D/C# Server:
public class SocketManager : MonoBehaviour
{
private List<ServerClient> connectedClients;
private List<ServerClient> disconectedClients;
private TcpListener server;
private string data;
private NetworkStream stream;
private StreamReader stremaReader;
private bool serverStarted;
public int socketPort = 7691;
private void Start()
{
connectedClients = new List<ServerClient>();
disconectedClients = new List<ServerClient>();
try
{
server = new TcpListener(IPAddress.Parse("192.168.1.64"), socketPort);
server.Start();
serverListening();
serverStarted = true;
Debug.Log("Server started. Port: " + socketPort.ToString());
}
catch (Exception ex)
{
Debug.Log("Server socket error: " + ex);
}
}
private void Update()
{
if (!serverStarted)
return;
foreach (ServerClient client in connectedClients)
{
if (IsConnected(client.tcp))
{
client.tcp.Close();
disconectedClients.Add(client);
continue;
}
else
{
stream = client.tcp.GetStream();
if (stream.DataAvailable)
{
this.stremaReader = new StreamReader(stream, true);
data = stremaReader.ReadLine();
if (data != null)
OnIcomingData(client, data);
}
}
}
}
private void OnIcomingData(ServerClient client, string data)
{
Debug.Log(client.clientName + ": " + data);
}
private bool IsConnected(TcpClient tcp)
{
try
{
if (tcp != null && tcp.Client != null && tcp.Client.Connected)
{
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
return !(tcp.Client.Receive(new byte[1], SocketFlags.Peek) == 0);
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
private void serverListening()
{
server.BeginAcceptTcpClient(AcceptTcpClient, server);
}
private void AcceptTcpClient(IAsyncResult asyncResult)
{
TcpListener tcplistener = (TcpListener)asyncResult.AsyncState;
connectedClients.Add(new ServerClient(tcplistener.EndAcceptTcpClient(asyncResult)));
serverListening();
}
}
public class ServerClient
{
public TcpClient tcp;
public string clientName;
public ServerClient(TcpClient tcp)
{
this.tcp = tcp;
this.clientName = "Android";
}
}
// This is my Android/Java Client:
public class SocketThread extends Thread {
private Socket adviser;
private DataOutputStream dataOut;
#Override
public void run() {
super.run();
Log.e("Status:", "Thread started");
try {
adviser = new Socket("192.168.1.64", 7691);
dataOut = new DataOutputStream(adviser.getOutputStream());
} catch (IOException ex) {
Logger.getLogger(SocketThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void sendCommand(String text) {
try {
dataOut.writeUTF(text);
Log.e("Sended Text: ", text);
} catch (IOException ex) {
Logger.getLogger(SocketThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
Here's a more detailed answer than my comment, for future reference.
The problem lies in the following if check if (IsConnected(client.tcp)).
Right now what is happening is that when the client is connected to the server (so the if returns true) you are Closing the connection using client.tcp.Close(); and adding the connection to your disconectedClients list (But you are not removing the client from your connectedClients list! Meaning the client is now in both lists). Then when the next iteration of the foreach loop foreach (ServerClient client in connectedClients) comes around the if (IsConnected(client.tcp)) will return false (the client is no longer connected to the server, since you called close() in the previous iteration.
You then try to call stream = client.tcp.GetStream(); on a stream that is already closed, resulting in an error telling you you cannot get the stream.
This should fix it:
if (IsConnected(client.tcp))
{
client.tcp.Close();
client.tcp.Dispose(); //Dispose of a stream to release it for GC
disconectedClients.Add(client);
connectedClients.Remove(client); //Note to also remove the disconnected client from the connectedClients list.
continue;
}
else
{ ...rest of logic }
Note that i added connectedClients.Remove(client); and client.tcp.Dispose(); to your logic closing the stream. If you don't do this it'll keep getting included in theforeachloop unnecessarily, and the programm will try to callclose()` on a connection that is already closed, resulting in more errors. Disposing of the stream will release it so the garbage collector can collect it, freeing up memory.
So I'm trying to create a client/server program. I want to know when my client disconnects of his own accord, so I've setup a heartbeat system. Every 6 seconds my client sends a ping to my server, if the client doesn't send a ping for a total of 30 seconds the client is considered disconnected and removed from the current connections list (for which I plan to implement a GUI). Or at least, that's the plan.
ConnectionManager.java
public class ConnectionManager implements Runnable{
static Socket connection;
private ArrayList<Thread> allConnections;
private ArrayList<Connection> allConnectionList;
private ServerSocket server;
private int id = 0;
public ConnectionManager() {
allConnections = new ArrayList<Thread>();
allConnectionList = new ArrayList<Connection>();
}
#Override
public void run() {
try {
server = new ServerSocket(5555);
System.out.println("Server is running!");
while(true) {
connection = server.accept();
Connection a = new Connection(connection, id);
Runnable runnable = a;
allConnectionList.add(a);
allConnections.add(new Thread(runnable));
allConnections.get(allConnections.size() - 1).start();
id++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void removeConnection(int id) {
allConnections.remove(id);
allConnectionList.remove(id);
}
Connection.java
public class Connection implements Runnable {
private Socket a;
public boolean amIActive;
private int id;
public Connection(Socket a, int id) {
amIActive = true;
this.a = a;
this.id = id;
}
public void onConnect() {
try {
String TimeStamp = new java.util.Date().toString();
String formattedAddress = a.getInetAddress().toString().replace("/", "");
System.out.println("Received connection from: " + formattedAddress + " at " + TimeStamp);
Runnable runnable = new ConnectionListener(this);
Thread connectionThread = new Thread(runnable);
connectionThread.start();
String returnCode = "Server repsonded to " + a.getInetAddress().toString().replace("/", "") + " at "+ TimeStamp + (char) 13;
BufferedOutputStream os = new BufferedOutputStream(a.getOutputStream());
OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
osw.write(returnCode);
osw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run() {
onConnect();
System.out.println("We got this far!");
while(amIActive) {
whileTrue();
}
System.out.println("This code never gets run because we get stuck in the while loop above");
Main.b.removeConnection(id);
System.out.println("Connection was closed from " + a.getInetAddress());
}
public void setOffline(boolean state) {
this.amIActive = state;
}
public void whileTrue() {
}
public Socket getSocket() {
return a;
}
ConnectionListener.java
public class ConnectionListener implements Runnable{
public Connection myConnection;
public boolean receivedHeartbeat;
public int missedHeartbeats = 0;
public ConnectionListener(Connection a) {
this.myConnection = a;
}
#Override
public void run() {
Runnable runnable = new Heartbeat(this);
Thread thread = new Thread(runnable);
thread.start();
while(myConnection.amIActive) {
try {
BufferedInputStream is;
is = new BufferedInputStream(myConnection.getSocket().getInputStream());
InputStreamReader isr = new InputStreamReader(is);
StringBuffer process = new StringBuffer();
int character;
while((character = isr.read()) != 13) { //GETTING STUCK HERE BECAUSE STUPID.
if(character == -1) {
myConnection.setOffline(true);
} else {
process.append((char)character);
}
}
handleInput(process);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void handleInput(StringBuffer process) {
String messageSent = process.toString();
if(messageSent.equals("Ping!")) {
receivedHeartbeat = true;
}
}
Heartbeat.java
public class Heartbeat implements Runnable{
private ConnectionListener b;
public Heartbeat(ConnectionListener a) {
b = a;
}
#Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
if(b.missedHeartbeats > 5) {
b.myConnection.amIActive = false;
System.out.println("Setting amIActiveToFalse!");
}
if(b.receivedHeartbeat) {
b.receivedHeartbeat = false;
} else {
b.missedHeartbeats++;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
My console is spammed with System.out.println("Setting amIActiveToFalse!"); from Heartbeat.java. But the while loop in Connection.java keeps running. I believe this might be something to do with my threading, but I can't figure it out.
When you have a non-volatile variable, there is no guarentee of visability of a change in one thread to another. In particular, if the JVM detects that a thread doesn't alter a boolean it can inline it, meaning you will never see the value change.
The simple solution is to make the boolean volatile and it will not be inlined and one thread will see when another changes it.
For more details http://vanillajava.blogspot.com/2012/01/demonstrating-when-volatile-is-required.html
The trivial answer to this is: make the variable volatile.
Without this, it is allowed for the thread changing the value to basically keep its updates in cache, committing them to main memory some time later.
This allows threaded code to run much faster, since it can keep its variables in cache rather than having to fetch from main memory. However, the consequence of this is that other threads don't see the update.
Making the variable volatile prevents this from happening: a thread always reads the value from main memory, and writes are immediately committed.
I say that this is the trivial answer because it doesn't necessarily fix all of your problems. There may also be an atomicity issue: in between one thread reading the variable and writing it again, another thread might sneak in and change its value, which may or may not put the first thread into an undefined state from the perspective of its invariants.
Specifically:
if(b.receivedHeartbeat) { b.receivedHeartbeat = false;
It is possible that some other thread can change b.receivedHeartbeat to false after this thread evaluates it to true, so this iteration is erroneously counted as a "non-missed" heartbeat.
This can be fixed by making the variable a (non-volatile) AtomicBoolean, on which there is an atomic compare-and-set method, which avoids such race conditions.
Java Concurrency In Practice is a great reference on these issues, I wholeheartedly recommend it. Look for the topics "visibility" and "atomicity".
Also read the advanced chapter on the Java Memory Model. That made me doubt myself at first, but made me a much stronger programmer after I digested it.
There are a couple issues I saw while debugging the code you posted, but I was able to successfully get the heartbeat functionality working.
In the Connection Listener class I don't think the if statement with .equals("Ping!") will match, because of the newline character at the end of each line.
In the Connection Listener class I would probably put the socket's Input Stream at the top of the loop not inside the loop. (I don't think this will break it but it's probably nicer this way)
ConnectionListener Updates:
public void run() {
Runnable runnable = new Heartbeat(this);
Thread thread = new Thread(runnable);
thread.start();
BufferedReader br = null;
try {
//is = new BufferedInputStream(myConnection.getSocket().getInputStream());
br = new BufferedReader(new InputStreamReader(myConnection.getSocket().getInputStream()));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while(myConnection.amIActive) {
try {
String processLine = br.readLine();
System.out.println("handleInput:" + processLine);
handleInput(processLine);
} catch (Exception e) {
System.out.println("Exception!");
e.printStackTrace();
}
}
}
public void handleInput(String messageSent) {
if(messageSent.startsWith("Ping!")) { //Need to use startsWith, or add newline character
receivedHeartbeat = true;
System.out.println("receivedHeartbeat!");
}
}
Also, in your Heartbeat class make sure you reset the missedHeartbeats counter to 0 on true:
if(b.receivedHeartbeat) {
b.receivedHeartbeat = false;
b.missedHeartbeats = 0;
} else {
b.missedHeartbeats++;
}
I have implemented a two player game with thread pool of sockets. each player connects to their own thread. I added a message Queue system according to this article.
Problem is the messages are lagging. the first respond from the first player is added to messageQueue as expected. But second player doesn't received it by calling poll() it only receives null yet.
And then the second player responds the second player receives the first players message. My intention is to send the message to second player before his/her response.
I must be doing some mistake or I have neglected some important concept.
Can you please help me find it?
My Code goes like this, there are two Classes related to this, GameRunnable.java and Game.java. I have omitted some code to simplify this mess.
In GameRunnable class ;
public static final Map<GamerRunnable, BlockingQueue<String>> messageQueues = new ConcurrentHashMap<>();
public static final Map<String, GamerRunnable> gameQueue = new ConcurrentHashMap<>();
private Game game;
public void run() {
System.out.println
(this.setGameInstance(clientSocket, readerIn, output) ? "OK": "FAILED");
messageQueues.put(this, new ArrayBlockingQueue<String>(100));
// If player 1
this.game.initGame(this);
// If Player 2
this.game.initGame(this);
}
public static GamerRunnable getGameThreadByName(String name) {
return gameQueue.get(name);
}
public String getName() {
return this.name;
}
in Game.java
public Game(Socket clientSocket, BufferedReader readIn, OutputStream output) {
this.sockGamer = clientSocket;
try {
this.out = output;
this.inGamer = readIn;
} catch(Exception e) {
e.printStackTrace();
}
public void sendToGamer(String msg) {
try {
this.out.write((msg+"\n").getBytes());
this.out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void initGame(GamerRunnable game) {
try {
boolean messageLoop = true;
String line1 = null;
String line2 = null;
while (messageLoop) {
line1 = inGamer.readLine();
if (line1 != null) {
System.out.println("Gamer says: "+line1);
GamerRunnable gamer2 = null;
if (game.getName().equals("red")) {
gamer2 = GamerRunnable.getGameThreadByName("black");
}
else if (game.getName().equals("black")) {
gamer2 = GamerRunnable.getGameThreadByName("red");
}
if (gamer2 != null) {
System.out.println("Adding to Queue");
GamerRunnable.messageQueues.get(gamer2).offer(line1);
}
}
line2 = GamerRunnable.messageQueues.get(game).poll();
if (line2 != null) {
//receiving from Queue
System.out.println(line2);
game.getGameInstance().sendToGamer(line2);
}
}
Due to network latency you may need a short delay to wait for the first message to arrive on the queue. Try poll(time, unit) instead. This will wait for the specified time for a message to appear on the queue. If nothing is there it will return a null like poll() is now. A user will probably not notice a 500ms to 1s delay if no message is available.
Im facing one problem in streaming data capture for reading the broadcast data during multithreading, pls help or suggest,
Actually there is one class which is reading data from one of the udp socket. Another class accepts the tcp connection from every client request, creates a thread for every client and request the same udp class for data. The thing is working with 1st thread which gets created. But when i request with another client from another pc/ip the packets get losted to the 2nd client/thread
I have made a workaround by creating a list where im storing the Threads outputstream object
and looping it to send the data to all the client. But this is just temporary as it ll delay the packets if clients/connections gets increased.
code for reading UDP Data
public class EventNotifier
{
private InterestingEvent ie;
public DatagramSocket clientSocket;
public String[] split_str;
byte[] receiveData;
HashMap<String, String> secMap = new HashMap<String, String>();
public EventNotifier(InterestingEvent event)
{
ie = event;
clientSocket = new DatagramSocket(9050);
receiveData = new byte[500];
}
public String getDataFeed(String client_id)
{
try
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
clientSocket.receive(receivePacket);
String s = new String(receivePacket.getData());
String split_str = s.split(",");
if(secMap.containsValue(split_str[0]))
return s;
else
return "";
} catch(Exception e3) {}
}
}// end of eventNotifier class
code for multithreading handling client requests
public class multiServer
{
static protected List<PrintWriter> writers = new ArrayList<PrintWriter>();
static String client_id = "";
public static void main(String[] args)
{
try
{
ServerSocket servsock = new ServerSocket(8858);
Socket incoming;
while(true)
{
incoming = servsock.accept();
multiServerThread connection = new multiServerThread(incoming);
Thread t1 = new Thread(connection);
t1.start();
}
}
catch(IOException e)
{
System.out.println("couldnt make socket");
}
}
}
class multiServerThread extends Thread implements InterestingEvent
{
Socket incoming;
PrintWriter out=null;
PrintWriter broad=null;
BufferedReader in = null;
String cliString=null;
private EventNotifier en;
int id;
public static String udp_data;
public void interestingEvent(String str1)
{
this.udp_data = str1;
}
public String getUdpData()
{
String _udp_data = this.udp_data;
return _udp_data;
}
multiServerThread(Socket incoming)
{
this.incoming=incoming;
en = new EventNotifier(this);
}
public void run()
{
try
{
out = new PrintWriter(incoming.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
cliString = in.readLine();
multiServer.writers.add(out);
while(true)
{
try
{
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
{
//workaround for serving the data to all cleints who are connected
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(udp_data.trim());
}
//else will directly write to the outputstream object for every thread which is connected
// out.println(udp_data.trim());
}
}
catch (Exception e)
{
System.out.println("exception "+e);
}
Thread.sleep(1);
}
} catch(IOException e)
{
System.out.print("IO Exception :: "+ e);
}
catch(InterruptedException e)
{
System.out.print("exception "+ e);
}
}
}
You need mutual exclusion (or a different design).
For example, what will happen if two threads call multiServer.writers.add(out); concurrently?
From the ArrayList Javadocs
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or [...])
Another problem is two calling udp_data = en.getDataFeed(cliString); concurrently. The second thread might overwrite the result of the first. You'll loose data!
What happens if one thread calls for (int i=0; i<multiServer.writers.size();i++) while another thread is busy doing multiServer.writers.add(out);? The size may have increased, before out has actually been added to the list!
public class multiServer
{
private List<PrintWriter> writers = new ArrayList<PrintWriter>();
public synchronized void addWriter(PrintWrite out) {
writers.add(out);
}
public synchronized void serveAllWriters(String data) {
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(data);
}
}
}
Now when a thread tries to add a writer, the synchronizeds will make sure no other thread is adding or printing. So multiServerThread should be fixed to use the new methods:
class multiServerThread extends Thread implements InterestingEvent
{
//...
private String udp_data;
//...
myMultiServer.addWriter(out);
//...
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
myMultiServer.serveAllWriters(udp_data.trim());
//...
}
There might be more problems, not sure I don't fully understand your code. The question you must ask yourself is, can another thread read and/or write the same data or object? Yes? Then you'll need proper synchronization.