I'm pretty new to this whole "Sockets" and networking world.
First, I wanted to make a random chat program like "omegle" and it worked perfectly fine. I think I had some serious issues in the code, but it worked - so why bother? (I wish I did).
Now I am adding a "Multiplayer" option in my "Tic Tac Toe" game in android, it went wrong and I spent many hours figuring how to solve this problem but nothing worked, my app just kept crashing.
Here's the code for the simple chat program.
Server
public class server {
public static Map<Integer, MiniServer> clients;
public static void main(String args[]) throws IOException {
    clients = new HashMap<>();
    boolean listeningSocket = true;
    ServerSocket serverSocket = new ServerSocket(1234);
    while (listeningSocket) {
        Socket socket = serverSocket.accept();
        MiniServer mini = new MiniServer(socket);
        if (clients.isEmpty()) {
            clients.put(1, mini);
            mini.setId(1);
        } else {
            int i = 1;
            while (clients.containsKey(i))
                i++;
            clients.put(i, mini);
            mini.setId(i);
        }
        mini.start();
    }
    serverSocket.close();
}
Client
public class client {
private static String message;
private static boolean connected;
private static boolean connectedInternet;
public static void main(String args[]) throws UnknownHostException, IOException {
    Scanner textReader = new Scanner(System.in);
    Socket socket = new Socket("127.0.0.1", 1234);
    Scanner inputStreamReader = new Scanner(socket.getInputStream());
    connectedInternet = true;
    System.out.println("Hello Stranger, get ready to chat.");
    PrintStream printStream = new PrintStream(socket.getOutputStream());
    Thread getMessage = new Thread() {
        public void run() {
            while (true) {
                message = textReader.nextLine();
                if (!connected)
                    System.out.println("You are not connected to another Stranger yet, please wait.");
                else
                    printStream.println(message);
            }
        }
    };
    getMessage.start();
    while (connectedInternet) {
        String temp = inputStreamReader.nextLine();
        if (temp.equals("connected")) {
            connected = true;
            System.out.println("Found a Stranger, say hey !");
        } else if (connected) {
            if (temp.equals("!close")) {
                System.out.println("Stranger disconnected.");
                printStream.println("!new");
            } else
                System.out.println("Stranger: " + temp);
        }
    }
    textReader.close();
    socket.close();
    inputStreamReader.close();
}
MiniServer
public class MiniServer extends Thread {
private Socket socket = null;
public int id;
private boolean foundPlayer;
private int colleague;
private boolean connected;
public MiniServer(Socket socket) {
    super("MiniServer");
    this.socket = socket;
}
public void run() {
    Scanner inputStreamReader = null;
    String message;
    try {
        inputStreamReader = new Scanner(socket.getInputStream());
    } catch (IOException e) {
        e.printStackTrace();
    }
    PrintStream p = null;
    try {
        p = new PrintStream(socket.getOutputStream());
    } catch (IOException e) {
        e.printStackTrace();
    }
    List<Integer> keys = new ArrayList<Integer>(server.clients.keySet());
    while (!foundPlayer) {
        for (Integer key : keys) {
            if (!server.clients.get(key).foundPlayer && key != id) {
                server.clients.get(key).foundPlayer = true;
                foundPlayer = true;
                server.clients.get(key).colleague = id;
                colleague = server.clients.get(key).id;
            }
        }
        try {
            keys = new ArrayList<Integer>(server.clients.keySet());
        } catch (ConcurrentModificationException e) {
        }
    }
    p.println("connected");
    connected = true;
    while (connected) {
        try {
            message = inputStreamReader.nextLine();
            if (message.equals("!new")) {
                foundPlayer = false;
                keys = new ArrayList<Integer>(server.clients.keySet());
                while (!foundPlayer) {
                    for (Integer key : keys) {
                        if (!server.clients.get(key).foundPlayer && key != id) {
                            server.clients.get(key).foundPlayer = true;
                            foundPlayer = true;
                            server.clients.get(key).colleague = id;
                            colleague = server.clients.get(key).id;
                        }
                    }
                    try {
                        keys = new ArrayList<Integer>(server.clients.keySet());
                    } catch (ConcurrentModificationException e) {
                    }
                }
                p.println("connected");
            } else
                sendToClient(message);
        } catch (NoSuchElementException e) {
            server.clients.remove(id);
            sendToClient("!close");
            closeSocket();
            connected = false;
        }
    }
}
public void setId(int i) {
    id = i;
}
public void sendToClient(String message) {
    Socket colleagueSocket = server.clients.get(colleague).socket;
    PrintStream rr = null;
    try {
        rr = new PrintStream(colleagueSocket.getOutputStream());
    } catch (IOException e) {
        e.printStackTrace();
    }
    rr.println(message);
}
public void closeSocket() {
    try {
        socket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
This program works great, but I'm pretty sure there are tons of problems with it.
Now here's my Server-side code for my android application.
Server 
public class Server {
public static Map<Integer, MiniServer> clients;
public static void main(String args[]) throws IOException {
    clients = new HashMap<>();
    boolean listeningSocket = true;
    ServerSocket serverSocket = new ServerSocket(1234);
    while (listeningSocket) {
        Socket socket = serverSocket.accept();
        MiniServer mini = new MiniServer(socket);
        if (clients.isEmpty()) {
            clients.put(1, mini);
            mini.setId(1);
        } else {
            int i = 1;
            while (clients.containsKey(i))
                i++;
            clients.put(i, mini);
            mini.setId(i);
        }
        mini.start();
    }
    serverSocket.close();
}
Mini Server
public class MiniServer extends Thread {
private Socket socket;
private Socket colleagueSocket;
public int id;
private boolean foundPlayer;
private int colleague;
private boolean connected;
private String crossOrCircle;
private boolean thisGoes;
private Thread timeOut;
private PrintStream p;
private Timer timer;
public MiniServer(Socket socket) {
    super("MiniServer");
    this.socket = socket;
}
public void run() {
    Scanner inputStreamReader = null;
    String message;
    try {
        inputStreamReader = new Scanner(socket.getInputStream());
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        p = new PrintStream(socket.getOutputStream());
    } catch (IOException e) {
        e.printStackTrace();
    }
    List<Integer> keys = new ArrayList<Integer>(Server.clients.keySet());
    while (!foundPlayer) {
        for (Integer key : keys) {
            if (!Server.clients.get(key).foundPlayer && key != id) {
                Server.clients.get(key).foundPlayer = true;
                foundPlayer = true;
                Server.clients.get(key).colleague = id;
                colleague = Server.clients.get(key).id;
                crossOrCircle = "X";
                Server.clients.get(key).crossOrCircle = "O";
                thisGoes = true;
                Server.clients.get(key).thisGoes = false;
                colleagueSocket=Server.clients.get(key).colleagueSocket;
                Server.clients.get(key).colleagueSocket=socket;
            }
        }
        try {
            keys = new ArrayList<Integer>(Server.clients.keySet());
        } catch (ConcurrentModificationException e) {
        }
    }
    p.println("connected");
    connected = true;
    p.println(crossOrCircle);
    while (connected) {
        try {
            message = inputStreamReader.nextLine();
            if (Character.toString(message.charAt(0)).equals(crossOrCircle) && thisGoes) {
                p.println(message);
                sendToClient(message);
                thisGoes = false;
                Server.clients.get(colleague).thisGoes = true;
            } else if (message.equals("!close")) {
                sendToClient("!closeClient");
                p.println("!closeClient");
                Server.clients.get(colleague).connected = false;
                connected = false;
                Server.clients.get(colleague).closeSocket();
                closeSocket();
                Server.clients.remove(colleague);
                Server.clients.remove(id);
            } else if (message.equals("!pause")) {
                timeOut = new Thread() {
                    @Override
                    public void run() {
                        timer = new Timer();
                        timer.schedule(
                                new TimerTask() {
                                    @Override
                                    public void run() {
                                        sendToClient("!closeClient");
                                        p.println("!closeClient");
                                        Server.clients.get(colleague).connected = false;
                                        connected = false;
                                        Server.clients.get(colleague).closeSocket();
                                        closeSocket();
                                        Server.clients.remove(colleague);
                                        Server.clients.remove(id);
                                    }
                                },
                                5000
                        );
                    }
                };
                timeOut.start();
            } else if (message.equals("!resume")) {
                timer.cancel();
            }
        } catch (NoSuchElementException e) {
            sendToClient("!closeClient");
            p.println("!closeClient");
            Server.clients.get(colleague).connected = false;
            connected = false;
            Server.clients.get(colleague).closeSocket();
            closeSocket();
            Server.clients.remove(colleague);
            Server.clients.remove(id);
        }
    }
}
public void setId(int i) {
    id = i;
}
public void sendToClient(String message) {
        PrintStream rr = null;
        try {
            rr = new PrintStream(colleagueSocket.getOutputStream());
        } catch (IOException | NullPointerException e) {
            e.printStackTrace();
        }
        rr.println(message);
    }
public void closeSocket() {
    try {
        socket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
public Socket getSocket(){
    return this.socket;
}
There's a problem in the sendClient() method, it keeps throwing NullPointerException.
What can I do? I'm not asking you to solve my problem.
 Could you give me some   advices please?
Thank you very much :)
Edit:
I forgot to mention some thing- I'm running the server on my computer and I'm using two different devices that are connected to the LAN.
java.lang.NullPointerException
    at com.ilya.rabinovich.tictactoe.MiniServer.sendToClient(MiniServer.java:134)
    at com.ilya.rabinovich.tictactoe.MiniServer.run(MiniServer.java:75)
Exception in thread "MiniServer" java.lang.NullPointerException
    at com.ilya.rabinovich.tictactoe.MiniServer.sendToClient(MiniServer.java:138)
    at com.ilya.rabinovich.tictactoe.MiniServer.run(MiniServer.java:75)
Edit 2:
I fixed this exception by changing this line  
                    colleagueSocket=Server.clients.get(key).colleagueSocket;
To
                    colleagueSocket=Server.clients.get(key).socket;  
When running this app on the android emulators (android studio) it works perfectly fine, but when I try running this app on external devices (Lg g3 and nexus 7) it works really weird and crashes most of the times.
Edit 3:
Okay I solved the problem =)
The problem was in the client(runOnUiThread).
Anyways, do you think there are ways to improve my Server code? Thanks !
 
     
    