-2

I have a WebSocket server.

my server create a new thread for handle a new connection.the thread is live until websocket break.

my problem: for 1_000_000 connections i need 1_000_000 threads. how i can handle many websockets by a thread? without wait?

ServerSocket server;
private ExecutorService executor = new ThreadPoolExecutor(1_000_000 , 1_000_000 , 7, TimeUnit.SECONDS, queue, threadFactory);

try
{
    server = new ServerSocket(port); 
}
catch (IOException e) {}

while (true)
 {
    Socket client = null;

   try
   {
        client = server.accept();

        Runnable r = new Runnable()
        {
           run()
           {
              // this is simple, original is complete WebSocket imp
               client.getInputStream().read();
           }
        };

        executor.execute(r);
    }

 catch (IOException e) {}
}
Ali Bagheri
  • 3,068
  • 27
  • 28
  • 3
    Why are you writing a WebSocket server, which is a very non-trivial task, when so many good implementations already exist? Are you sure this really is a WebSocket server, or just a TCP server? – user207421 Mar 19 '20 at 03:38
  • 1
    You say, "my problem: for 1_000_000 connections i need 1_000_000 threads". That's not different to saying "for 1,000,000 cars, I need 4,000,000 wheels". It's a factual statement, but not describing any problem. – Holger Mar 23 '20 at 14:12
  • In real word 4,000,000 wheels is not problem, but in a Server 1_000_000 threads is problem. – Ali Bagheri Mar 23 '20 at 16:51

3 Answers3

1

Your concept is wrong. You should not start a new thread every few milliseconds because that will slow down your system a lot. Also you cannot have 1 Million connections open at the same time. No normal operating system would allow that.

Rather than that, normal web servers run a maximum number of threads (e.g. 100 on an average server) which process the incoming requests sequentially.

Stefan
  • 1,789
  • 1
  • 11
  • 16
  • 1
    "Also you cannot have 1 Million connections open at the same time. No normal operating system would allow that." - do you have a source for that? There are only 32ki or so different unassigned TCP ports available, but maybe you can spend all of them on each of 32ki different destination IP/ports? – John Dvorak Mar 15 '20 at 14:01
  • I have only my experience as source. – Stefan Mar 16 '20 at 16:16
  • The following topic may be worth to read: https://stackoverflow.com/questions/410616/increasing-the-maximum-number-of-tcp-ip-connections-in-linux – Stefan Mar 16 '20 at 17:33
  • @JohnDvorak A server only uses one TCP port. Incoming connections are limited by memory, FD limits, threads, etc., but not by TCP ports. – user207421 Mar 19 '20 at 09:33
  • @user207421 There's some software (FTP?) in which the server listens to predesignated port to negotiate further connections, and then each actual file is then transferred on a separate high port chosen by the (server, I think?). In that case you could theoretically run 1M files at once. – John Dvorak Mar 19 '20 at 11:10
  • not to mention that a typical HTTP server actually listens on two ports already, not one - 80 and 443. – John Dvorak Mar 19 '20 at 11:20
  • 1
    The OP does not "start a new thread every few milliseconds". The OP uses a thread pool. – Holger Mar 23 '20 at 14:14
1

Think about this you have a map of sockets and every time a message received to server you will get message and related socket !

this operation done with OS(linux , windows , unix , mac-OS , ...) kernel !

so you can handle a million connection just in one thread !

we call this None-Blocking sockets which means they never block your thread to read or write or any other operation such as accept and ... !

java has a package to handle this ! java.nio.*

how it's work ?

  • A Thread to handle IO operations
  • A Selector to select which sockets has operation and what type of operation
  • ByteBuffer to handle read and write instead of using socket.stream in blocking-socket

also you can use multiple thread and selectors (each selector has its own thread)

look at this example :

NoneBlockingServer.java :

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NoneBlockingServer {

public static void main(String[] args) throws Exception
{
    runServer("localhost" , 5050);
}


private final static void runServer(String host , int port)throws Exception {
    Selector selector = Selector.open();
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress(host, port));
    serverSocketChannel.configureBlocking(false); //config to be a none-blocking serve-socket
    SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    //register to selector for operation ACCEPT !
    //also you can use selectionKey for some other stuffs !

    while (true) {

        int numberOfReadSockets = selector.select();
        //it will wait until a socket(s) be ready for some io operation
        //or other threads call selector.wakeup()

        if(numberOfReadSockets==0){
            //maybe selector.wakeup() called
            //do some sync operations here !
            continue; // continue selecting !
        }

        Iterator<SelectionKey> keys = selector.selectedKeys().iterator();

        while (keys.hasNext())
        {
            SelectionKey key = keys.next();
            keys.remove(); //remove selected key from current selection !

            //handle selected key


            if(key.isValid() && key.isReadable())
            {

                //it means this socket is valid and has data to read


                SocketChannel socketChannel =
                        (SocketChannel) key.channel();

                ByteBuffer buffer = ByteBuffer.allocate(100); // allocate 100 bytes for buffer
                //maybe you must use an allocated buffer for each connection
                // instead of allocate for each operation

                int read = socketChannel.read(buffer);

                if(read<0)
                {
                    //need to close channel !
                    socketChannel.close(); // explicitly remove from selector
                    System.out.println("CONNECTION CLOSED");
                    continue; //socket closed and other operations will skip
                }else
                {
                    buffer.flip(); // you need to learn work with ByteBuffers
                    byte[] bytes = new byte[buffer.remaining()];
                    buffer.get(bytes);
                    //maybe convert it to String
                    String msg = new String(bytes);
                    //use msg !
                    System.out.println("MESSAGE : "+msg);

                    key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
                    //set interestOps to WRIT and READ to write hello back message !
                    key.attach(ByteBuffer.wrap("Hello Client !".getBytes("UTF-8")));
                    //wrap a array of bytes using wrap and attach it to selectionKey
                }

            }

            if(key.isValid() && key.isWritable())
            {
                //it means this socket is valid and have space to write data !

                SocketChannel socketChannel =
                        (SocketChannel) key.channel();

                //you must represent data you want to write to this socket
                //maybe attached to selection key !
                ByteBuffer dataToWrite = (ByteBuffer) key.attachment();
                //key.attachment here to help u have some meta data about each socket
                //use it smart !

                int write = socketChannel.write(dataToWrite);

                if(write<0)
                {
                    //so means some error occurs better to close it !
                    socketChannel.close();
                    System.out.println("CONNECTION CLOSED !"); //log
                    continue;//as socket closed we will skip next operations !
                }else if(!dataToWrite.hasRemaining())
                {
                    //so all data putted to buffer !
                    key.interestOps(SelectionKey.OP_READ); // just need to read !
                }
            }

            if(key.isValid() && key.isAcceptable())
            {
                ServerSocketChannel server =
                        (ServerSocketChannel) key.channel();//just server channels has accept operation

                SocketChannel socketChannel = server.accept(); //accept it !

                socketChannel.configureBlocking(false); // config none-blocking mode

                socketChannel.register(selector , SelectionKey.OP_READ);

                //also you can register for multiple operation using | operation
                //for example register for both read and write SelectionKey.READ|SelectionKey.WRITE
                //also you can change is late using key.interestOps(int ops)


                System.out.println("NEW CONNECTION"); //log
            }

            //there is another type of key,  key.isConnectable()
            //google it !




        }
    }
}
}

and here is BlockingClient.java :

import java.net.InetSocketAddress;
import java.net.Socket;

public class BlockingClient {

//using blocking sockets !
public static void main(String[] args)throws Exception
{
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("localhost" , 5050));
    socket.getOutputStream()
            .write("Hello Server".getBytes("UTF-8"));
    byte[] buffer = new byte[100];
    int len  = socket.getInputStream().read(buffer);
    System.out.println(new String(buffer , 0 , len , "UTF-8"));
    socket.close();
}
}

at this example we send Hello Server message from Blocking Client to None-Blocking Server and server will response Hello Client message !

just run !

Good luck

Alireza Akhoundi
  • 214
  • 3
  • 12
  • 1
    Now we only need a proof that maintaining the session state per connection and selecting the right one to make progress is more efficient than a thread scheduler basically doing same. The answer might be different for a trivial server and a complex server. – Holger Mar 23 '20 at 14:21
-5

A tempting thought is "don't use java". Java has terrible green thread support but golang and erlang are built on green threading so they do it very well.

The java way seems to be worker pools. So you create an Executor pool (see java.util.concurrent), decide how many workers you want for a given number of connections, then pass connections to workers via a queue. A worker then has to iterate over its set of connections deciding to process each or yield.

Your number of live tcp connections is hard capped at about 2^16 (65_536: number of available ports) but the system is unlikely to be performant if you have that many connections anyway. Most systems can't maintain performance for more than ~200 persistent connections. If you can far exceed that number I would assume your system isn't doing enough per connection to really justify the use of web sockets, but I'm only guessing.

Keynan
  • 1,338
  • 1
  • 10
  • 18
  • 2
    Java hasn't had green thread support for 20 years, and never had it on most platforms. – user207421 Mar 19 '20 at 03:37
  • Technically it hasn't had them for just under 20 years (May 2000, Java 1.3), but that hardly matters. Yet there are still ways of simulating many of the properties of green thread in java, there just aren't user space threads scheduled by the jvm. – Keynan Apr 01 '20 at 14:36