Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I handle multiple clients connected to a server using sockets?

I have an Android app that needs to let multiple devices connect. One device should act as the group owner and issue instructions to all of the clients to do specific things. I suppose it's comparable to a wireless handheld game where one player is the host.

I have a couple of questions so I will try to keep them concise. Even an answer to just the first would help.

First, I've successfully paired a single server and single client phone using sockets. I did this using Android's Wi-Fi Direct technology (Described here). The tutorial is helpful but unfortunately not very thorough, particularly in describing one-to-many connections. Once a peer list is found, socket connections can be opened. I was able to connect two devices using a thread for the server (Using this example), like so:

public class ServerThread implements Runnable {

        public void run() {
             (Code...)
             client = serverSocket.accept();
             (...other code here.)
        }
}

A client is created following a button press (I think, still trying to get my head around my modified code):

public class MusicClientThread implements Runnable {
     Socket socket = new Socket(serverAddr, port);
     while (connected) {
          //(Other code here.)
          while ((line = in.readLine()) != null) {
          //While there are still messages coming
          }
     }
     socket.close();
     //(Other code here too.)
}

So I suppose my first question is: How would I allow more clients to be connected? My ServerThread refers to a single client variable above so I do not see how I would allow a varying number (my application is aiming for anything from 2 to 10 users) nor do I know the correct way to differentiate between all my different clients. My only guess is that I would use the unique IP address of each phone.

My 2nd question is, once I've established a connection with multiple clients/peers, how would I then send and receive instructions to them correctly? Currently my single server awaits an instruction, and upon receiving it, issues a response instruction. I need it so that the server can send instructions from the start, using button presses, and the result of these is visible on the client devices.

I hope I have made everything clear!

like image 720
Chucky Avatar asked Mar 04 '13 19:03

Chucky


People also ask

Can one socket handle multiple connections?

A single listening port can accept more than one connection simultaneously.

Can multiple clients connect to same socket TCP?

It is possible for a client to request a specific TCP port to connect from using the bind() system call; however, if two clients request the same port, only the first request will succeed.

How many clients can connect to a server socket?

Maximum number of sockets. For most socket interfaces, the maximum number of sockets allowed per each connection between an application and the TCP/IP sockets interface is 65535.


2 Answers

You have to start a new thread for each client, as the sockets need a thread for themselves to run (as it waits for input most of the time). This can for example be done (in this simplified manner):

public class ThreadHandler extends Thread {
      private ArrayList<ServerThread> serverThreads = new ArrayList<ServerThread>();
      public void run() {
            while (!isInterrupted()) { //You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app.
                  (Code...)
                  ServerThread thread = new ServerThread(serverSocket.accept());
                  serverThreads.add(thread);
                  thread.start();
            }
      }
      public void doSomethingOnAllThreads() {
            for (ServerThread serverThread : serverThreads) {
                  serverThread.otherMethod();
            }
      }
}

Where the ServerThread looks something like this:

public class ServerThread extends Thread {
    private Socket socket;
    public ServerThread(Socket socket) {
          this.socket = socket;
    }
    public void run() {
         (...other code here.)
    }
    public void otherMethod() {
          //Signal to the thread that it needs to do something (which should then be handled in the run method)
    }
}
like image 199
ddmps Avatar answered Nov 03 '22 00:11

ddmps


I have a Google TV Box. And i have two mobile phones.

On the GoogleTV Box i run a server. The Server has one ServerSocket on port 6001. Also the Server has two sockets for two clients

The first device connects to server and using socket number one the second the other....

I can simultanous post diffrent messages from 2 devices to the google tv box socket and show them on TV.

I using the following solution:

FOR THE MOBILE CLIENT (2 devices)

create a new android project with a blank activity and copy this code into. create a layout for the client containing a edittext and a button. MAKE SURE TO SET INTERNET AND ACCESS NETWORK PERMISSIONS IN ANDROIDMANIFEST.XML!!! And edit serverIpAddress in this file to your servers accessible IP.

package de.android.googletv.gameclient;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Random;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

/**
 * 
 *  
 *
 */
public class FullscreenActivity extends Activity {

    private Socket socket;
    private String serverIpAddress = "192.168.22.253"; // GOOGLE TV IP
    private static final int PLAYER_1_SERVERPORT = 6001; 

    private Button bt;
    private TextView tv;

    String DEVICE_NAME;


    private class ConnectToServer extends AsyncTask {

        @Override
        protected Object doInBackground(Object... params) {

                System.out.println("connecting to server...");

                try {

                     InetAddress serverAddr = InetAddress.getByName(serverIpAddress);
                     socket = new Socket(serverAddr, PLAYER_1_SERVERPORT);

                  } catch (UnknownHostException e1) {
                      System.out.println("ERROR REACHING SERVER! 1");
                     e1.printStackTrace();
                  } catch (IOException e1) {
                      System.out.println("ERROR REACHING SERVER! 2");
                     e1.printStackTrace();
                  }

                System.out.println("Done!");

                return params;
         }

         protected void onPostExecute() {

         }

     }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_fullscreen);

        DEVICE_NAME = android.os.Build.MODEL;

        Button exit = (Button) findViewById(R.id.dummy_button);
        exit.setOnClickListener(new OnClickListener() {         
            @Override
            public void onClick(View v) {
                System.exit(1);             
            }
        });

        new ConnectToServer().execute("");  

        tv = (TextView) findViewById(R.id.editText1);

        bt = (Button) findViewById(R.id.button1);
        bt.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                try {

                   Random rnd = new Random();                  

                   EditText et = (EditText) findViewById(R.id.editText1);
                   String str = DEVICE_NAME + " ID" + rnd.nextInt() + " says: " + et.getText().toString();

                   PrintWriter out = new PrintWriter(
                           new BufferedWriter(
                                   new OutputStreamWriter(
                                           socket.getOutputStream())),true
                                           );
                   out.println(str);

                   Log.d("Client", "Client sent message");

                } 
                catch (UnknownHostException e) {
                   tv.setText("UnknownHostException");
                   e.printStackTrace();
                } 
                catch (IOException e) {
                   tv.setText("IOException");
                   e.printStackTrace();
                } 
                catch (Exception e) {
                   tv.setText("Exception");
                   e.printStackTrace();
                }
             }
        });
    }

}

FOR THE SERVER (google tv box)

create a new android project with blank activity and copy this code into. create a layout with only a textfield MAKE SURE TO SET INTERNET AND ACCESS NETWORK PERMISSIONS IN ANDROIDMANIFEST.XML!!!

package de.android.googletv.gameserver;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

/**
 * 
 * 
 *
 */
public class FullscreenActivity extends Activity {


    // server socket
    ServerSocket ss_plr1 = null;
    public static final int SERVERPORT_1 = 6001;

    int nr_connections = 0;

    // socket for player1
    Socket s1;

    // socket for player2
    Socket s2;

    Thread myCommsThread = null;

    protected static final int MSG_ID = 0x1337;
    String mClientMsg = "";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_fullscreen);

        Button exit = (Button) findViewById(R.id.dummy_button);
        exit.setOnClickListener(new OnClickListener() {         
            @Override
            public void onClick(View v) {
                System.exit(1);             
            }
        });

        TextView tv = (TextView) findViewById(R.id.fullscreen_content);
        tv.setText("Nothing from client yet");

        myCommsThread = new Thread(new CommsThread());
        myCommsThread.start();
    }


    @Override
    protected void onStop() {
        super.onStop();
        try {
            // make sure you close the socket upon exiting
            ss_plr1.close();            
        } 
        catch (IOException e) {e.printStackTrace(); }
    }

    Handler myUpdateHandler = new Handler() {
        public void handleMessage(Message msg) {

            System.out.println("handleMessage("+msg+")");

            switch (msg.what) {

            case MSG_ID:
                TextView tv = (TextView) findViewById(R.id.fullscreen_content);
                tv.setText((String)msg.obj);
                break;

            default:
                break;

            }
            super.handleMessage(msg);
        }
    };

    class CommsThread implements Runnable {

        public void run() {

            System.out.println("creating new sockets...");

            try {

                ss_plr1 = new ServerSocket(SERVERPORT_1 );

                if (s1 == null)
                    s1 = ss_plr1.accept();

                if (s2 == null)
                    s2 = ss_plr1.accept();

            } 
            catch (IOException e) {e.printStackTrace();}

            new Thread(new ConnectionHandler(s1, myUpdateHandler)).start();
            new Thread(new ConnectionHandler(s2, myUpdateHandler)).start();

        }

    }


}

... and required by the server is the connection handler for threaded messaging ...

create a additional class in server project called: "ConnectionHandler.java" and copy this code into. it handles the async connections.

package de.android.googletv.gameserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

import android.os.Handler;
import android.os.Message;

public class ConnectionHandler implements Runnable {

    Socket m_socket;
    Handler m_updateHandler;

    public ConnectionHandler(Socket socket, Handler updateHandler) {
        m_socket = socket;
        m_updateHandler = updateHandler;
    }

    @Override
    public void run() {

        while (!Thread.currentThread().isInterrupted()) {

            try {

                BufferedReader input = new BufferedReader(new InputStreamReader(m_socket.getInputStream()));

                String st = null;
                st = input.readLine();

                Message m = new Message();
                m.what = 0x1337;
                m.obj = st;
                m_updateHandler.sendMessage(m);

            } 
            catch (IOException e) { e.printStackTrace();}

        }

    }

}

This is not the nicest solution. Multiple "not nice" codings. System.exit(1) for example. And it is only has two devices support. But it works for more than one device, and im pretty sure you will modify it for you purposes. Its based on three web sources and additional attemps from myself to make it work. Its only my prototype....

I CANNOT LINK TO THEM :( ... to less reputation.

If you build and run everthing it should look like this:

https://plus.google.com/u/0/109268217221135029753/posts/3iz6SF1hiJa

like image 28
thesparxinc Avatar answered Nov 03 '22 00:11

thesparxinc