简体   繁体   English

服务器和客户端的客户端数量不同 Java NIO

[英]Number of clients at Server and Client Varies Java NIO

I am learning how the system scales millions of connections to a single box.我正在学习系统如何将数百万个连接扩展到一个盒子。

With the early set of learnings TCP 3-way handshake, NIO's way of accepting connections.与早期的一组学习TCP 3次握手,NIO的接受连接的方式。 I ran multiple tests (simple client-server code) and kept following counters -我运行了多个测试(简单的客户端-服务器代码)并保持跟踪计数器 -

At server Number of successful accept()在服务器上成功接受的次数()

At client Number of successful open() ie success count & exception count when the connection is not accepted by the server & exception is thrown在客户端成功打开()的次数,即服务器不接受连接时的成功计数和异常计数并抛出异常

On the client-side, sum of success count & exception count is equal to the number of connections initiated to the server (N) but on the server-side, the success count is lesser than the client-side success count (even when no connections were rejected by server).在客户端,成功计数和异常计数的总和等于向服务器发起的连接数 (N),但在服务器端,成功计数小于客户端成功计数(即使没有连接被服务器拒绝)。

Ex: Number of connections to be initiated N = 10_000例如:要启动的连接数 N = 10_000

Scenario 1: ( No connections rejected by server ie no Exception raised at client side when opne() invoked)场景 1:(没有连接被服务器拒绝,即调用 opne() 时没有在客户端引发异常)

Server success count: 9997服务器成功次数:9997

Client success count: 10_000, exception count: 0客户端成功计数:10_000,异常计数:0

Scenario 2: ( Few of connections rejected by server ie Exception raised at client side when opne() invoked with error connection reset )场景 2:(很少有连接被服务器拒绝,即当 opne() 调用错误连接重置时在客户端引发异常)

Server success count: 9795服务器成功次数:9795

Client success count: 9995, exception count: 5客户端成功数:9995,异常数:5

Server Code:服务器代码:

import java.nio.*;
import java.nio.channels.*;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Server implements Runnable {
    private final int port;
    private ServerSocketChannel ssc;
    private Selector selector;
    private ByteBuffer buf = ByteBuffer.allocate(256);
    private AtomicInteger clientCount = new AtomicInteger(0);

    Server(int port) throws IOException {
        this.port = port;
        this.ssc = ServerSocketChannel.open();
        this.ssc.socket().bind(new InetSocketAddress(port),128);
        this.ssc.configureBlocking(false);
        this.selector = Selector.open();

        this.ssc.register(selector, SelectionKey.OP_ACCEPT);
    }

    @Override
    public void run() {
        try {
            System.out.println("Server starting on port " + this.port);

            Iterator<SelectionKey> iter;
            SelectionKey key;
            while (this.ssc.isOpen()) {
                selector.select();
                iter = this.selector.selectedKeys().iterator();
                while (iter.hasNext()) {
                    key = iter.next();
                    iter.remove();

                    if (key.isAcceptable()) this.handleAccept(key);
                    if (key.isReadable()) this.handleRead(key);
                }
            }
        } catch (IOException e) {
            System.out.println("IOException, server of port " + this.port + " terminating. Stack trace:");
            e.printStackTrace();
        }
    }

    private final ByteBuffer welcomeBuf = ByteBuffer.wrap("Welcome to Server!\n".getBytes());

    private void handleAccept(SelectionKey key) throws IOException {
        SocketChannel sc = ((ServerSocketChannel) key.channel()).accept();
        String address = (new StringBuilder(sc.socket().getInetAddress().toString())).append(":").append(sc.socket().getPort()).toString();
        sc.configureBlocking(false);
        sc.register(selector, SelectionKey.OP_READ, address);
        /*sc.write(welcomeBuf);
        welcomeBuf.rewind();*/
        System.out.println(String.format("accepted connection from: %s, number of clients: %d", address, clientCount.incrementAndGet()));//this count is lesser than success_count of client
    }

    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel ch = (SocketChannel) key.channel();
        StringBuilder sb = new StringBuilder();

        buf.clear();
        int read = 0;

        while (ch.isConnected() && (read = ch.read(buf)) > 0) {
            buf.flip();
            byte[] bytes = new byte[buf.limit()];
            buf.get(bytes);
            sb.append(new String(bytes));
            buf.clear();
        }
        String msg;
        if (read < 0) {
            msg = key.attachment() + " left the chat.\n";
            ch.close();
        } else {
            msg = key.attachment() + ": " + sb.toString();
        }

        System.out.println(String.format("Received message from client: %s", msg));
    }

    public static void main(String[] args) throws IOException {
        Server server = new Server(10523);
        (new Thread(server)).start();
    }
}

Client Code:客户代码:

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

class Task implements Runnable {
    int id;
    Client client;

    public Task(int id) {
        this.id = id;
    }

    public Task(int id, Client client) {
        this.id = id;
        this.client = client;
    }

    @Override
    public void run() {
        try {
            int port = 10523;
            InetAddress hostIP = InetAddress.getLocalHost();
            InetSocketAddress myAddress =
                    new InetSocketAddress(hostIP, port);

            SocketChannel myClient = SocketChannel.open();
            myClient.socket().connect(myAddress);

            if(myClient.socket().isConnected()){
                client.successCount.incrementAndGet();
            }
        } catch (Exception e) {
            System.out.println("exception count: "+client.exceptionCount.addAndGet(1));
            e.printStackTrace();
        }
    }
}

public class Client {
    AtomicInteger successCount = new AtomicInteger();
    AtomicInteger exceptionCount = new AtomicInteger();
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();
        client.process();
    }

    private void process() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(50);
        int N = 10_000;
        for (int i = 0; i < N; i++) {
            Task task = new Task(i, this);
            executorService.submit(task);
        }
        while (true){
            Thread.sleep(8000);
            System.out.println("success count: "+successCount.get());//success_count
        }
    }
}

I am just accepting connections and not doing any read/write.我只是接受连接而不做任何读/写。

This could be very fundamental but I am stuck and unable to debug further.这可能是非常基本的,但我被卡住了,无法进一步调试。 Any pointer would help me learn something new today.任何指针都会帮助我今天学习新的东西。

Edit:编辑:

I tried a single-threaded client that opens N connection sequentially, but the same problem is occurring.我尝试了一个按顺序打开N个连接的单线程客户端,但同样的问题正在发生。 A number of success/connect showing at the client is more than on the server side.在客户端显示的成功/连接数量比在服务器端更多。

  • After learning NIO in details and it's semantic, I understood the problem.在详细了解了 NIO 和它的语义之后,我明白了这个问题。
  • Here in the above client code, I am kind of hitting the server (making the connection) but not listening to whether the connection took place successfully.在上面的客户端代码中,我有点点击服务器(建立连接)但没有监听连接是否成功。 I achieved this by Selector and OP_CONNECT event.我通过 Selector 和 OP_CONNECT 事件实现了这一点。 Code -代码 -
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Simple Client, connect to server, listens for any events, writes dummy message to server every 3 seconds
 */
class ClientTask implements Runnable {
    private Client client;
    private String clientId;
    private Selector clientSelector;
    private static final int BUFFER_SIZE = 1024;
    private ByteBuffer myBuffer = ByteBuffer.allocate(BUFFER_SIZE);
    private SelectionKey key;

    public ClientTask(String clientId, Client client) throws IOException {
        this.clientId = clientId;
        this.client = client;
        this.clientSelector = Selector.open();
    }

    @Override
    public void run() {
        try {
            InetSocketAddress serverAddr = new InetSocketAddress(client.getHost(), client.getPort());
            System.out.println(String.format
                    ("%s connecting to <%s:%d>", clientId, serverAddr.getHostName(), serverAddr.getPort()));

            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            this.key = socketChannel.register(this.clientSelector, SelectionKey.OP_CONNECT);//listens for connection establishment events
            this.key.attach(clientId);
            socketChannel.connect(serverAddr);
            while (true) {
                //wait for seconds
                synchronized (this) {
                    wait(3_000);
                }
                clientSelector.selectNow();//non blocking call
                Iterator<SelectionKey> iterator = clientSelector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    if (selectionKey.isConnectable()) {
                        accept(selectionKey);
                    } else if (selectionKey.isReadable()) {
                        read(selectionKey);
                    }
                }
                //write dummy message to server
                write(this.key, "Dummy message from Client");
            }
        } catch (Exception e) {
            System.out.println("exception count: " + client.failedConnectionCount.addAndGet(1));
            e.printStackTrace();
        }
    }

    /*
     * On successful connection, add read(OP_READ) as new event to interested Operation set
     * */
    private void accept(SelectionKey selectionKey) throws IOException {
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        boolean finishConnect = channel.finishConnect();
        String cId = selectionKey.attachment().toString();
        if (finishConnect) {
            selectionKey.interestOps(SelectionKey.OP_READ);
            System.out.println(String.format("%s connected properly", cId));
            client.successConnectCount.addAndGet(1);//increment successful connection count
        } else {
            throw new RuntimeException(String.format("%s didn't connect properly", cId));
        }
    }

    // Writes to server
    private void write(SelectionKey clientKey, String msg) throws IOException {
        if (clientKey.isValid() && clientKey.channel() instanceof SocketChannel) {
            ByteBuffer msgBuf = ByteBuffer.wrap(msg.getBytes());
            SocketChannel ch = (SocketChannel) clientKey.channel();
            String address = ch.getRemoteAddress().toString();
            int writeBytes = ch.write(msgBuf);
            msgBuf.rewind();
            if (writeBytes <= 0) {
                throw new RuntimeException(String.format("Wrote %d bytes to client %s", writeBytes, address));
            }
        }
    }

    // Reads from server
    private void read(SelectionKey selectionKey) throws IOException {
        String cId = selectionKey.attachment().toString();
        StringBuilder sb = new StringBuilder();
        ByteBuffer buf = ByteBuffer.allocate(256 * 4);
        buf.clear();
        SocketChannel ch = (SocketChannel) selectionKey.channel();

        while (key.isValid() && ch.isConnected() && ch.read(buf) > 0) {
            buf.flip();
            byte[] bytes = new byte[buf.limit()];
            buf.get(bytes);
            sb.append(new String(bytes));
            buf.clear();
        }

        System.out.println(String.format("%s received data :%s from server\n\n", cId, sb.toString()));
        myBuffer.clear();
    }
}

/**
 * A simple Java NIO Client Manager creates N clients listening to given server
 */
public class Client {
    AtomicInteger successConnectCount = new AtomicInteger();
    AtomicInteger failedConnectionCount = new AtomicInteger();

    private int noOfClients;
    private int port;
    private String host;

    public Client(String host, int port, int noOfClients) {
        this.host = host;
        this.port = port;
        this.noOfClients = noOfClients;
    }

    public static void main(String[] args) throws InterruptedException, IOException {
        if (args.length < 3) {
            throw new RuntimeException("Pass 3 arguments <server-ip> <server-port> <no-of-clients> and start the client (e.g java Client localhost 1234 100)");
        }

        Client client = new Client(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]));
        client.process();
    }

    private void process() throws InterruptedException {
        try {
            ExecutorService executorService = Executors.newFixedThreadPool(noOfClients);
            for (int i = 1; i <= noOfClients; i++) {
                ClientTask task = new ClientTask(String.format("Client_%d", i), this);
                executorService.submit(task);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        Thread.sleep(Integer.MAX_VALUE);//mock long waiting period
    }

    public int getPort() {
        return port;
    }

    public String getHost() {
        return host;
    }
}

If we look at while block , it listens for events and increments the successConnectCount by one when OP_CONNECT (selectionKey.isConnectable()) event is being received.如果我们查看while 块,它会侦听事件并在收到 OP_CONNECT (selectionKey.isConnectable()) 事件时将successConnectCount加一。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM