简体   繁体   English

具有非阻塞套接字的多线程

[英]multithreading with non-blocking sockets

I am trying to implement a TCP Server in Java using nio. 我正在尝试使用nio在Java中实现TCP服务器。 Its simply using the Selector's select method to get the ready keys. 它只是使用Selector的select方法来获取就绪键。 And then processing those keys if they are acceptable, readable and so. 然后处理这些密钥,如果它们是可接受的,可读的等等。 Server is working just fine till im using a single thread. 服务器工作正常,直到我使用单个线程。 But when im trying to use more threads to process the keys, the server's response gets slowed and eventually stops responding, say after 4-5 requests. 但是当我试图使用更多的线程来处理密钥时,服务器的响应变慢并最终停止响应,比如说4-5个请求之后。 This is all what im doing:(Pseudo) 这就是我正在做的事情:(伪)

Iterator<SelectionKey> keyIterator =  selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
                SelectionKey readyKey = keyIterator.next();
                if (readyKey.isAcceptable()) {
                    //A new connection attempt, registering socket channel with selector

                } else {
                    Worker.add( readyKey );
                }

Worker is the thread class that performs Input/Output from the channel. Worker是从通道执行输入/输出的线程类。 This is the code of my Worker class: 这是我的Worker类的代码:

private static List<SelectionKey> keyPool = Collections.synchronizedList(new LinkedList());

public static void add(SelectionKey key) {
    synchronized (keyPool) {
        keyPool.add(key);
        keyPool.notifyAll();
    }
}


public void run() {
    while ( true ) {

        SelectionKey myKey = null;
        synchronized (keyPool) {
            try {
                while (keyPool.isEmpty()) {
                    keyPool.wait();
                }
            } catch (InterruptedException ex) {                    
            }
            myKey = keyPool.remove(0);
            keyPool.notifyAll();
        }

        if (myKey != null && myKey.isValid() ) {

            if (myKey.isReadable()) {
                //Performing reading
            } else if (myKey.isWritable()) {
                //performing writing
                myKey.cancel();  
            }
        }
    }

My basic idea is to add the key to the keyPool from which various threads can get a key, one at a time. 我的基本想法是将密钥添加到keyPool,各个线程可以从中获取密钥,一次一个。 My BaseServer class itself is running as a thread. 我的BaseServer类本身作为线程运行。 It is creating 10 Worker threads before the event loop to begin. 它在事件循环开始之前创建10个Worker线程。 I also tried to increase the priority of BaseServer thread, so that it gets more chance to accept the acceptable keys. 我还尝试增加BaseServer线程的优先级,以便它有更多机会接受可接受的密钥。 Still, to it stops responding after approx 8 requests. 仍然,它在大约8个请求后停止响应。 Please help, were I am going wrong. 请帮忙,我出错了。 Thanks in advance. 提前致谢。 :) :)

Third, you aren't removing anything from the selected-key set. 第三,您没有从选定键集中删除任何内容。 You must do that every time around the loop, eg by calling keyIterator.remove() after you call next(). 你必须每次循环都这样做,例如在调用next()之后调用keyIterator.remove()。

You need to read the NIO Tutorials. 您需要阅读NIO教程。

Try using xsocket library. 尝试使用xsocket库。 It saved me a lot of time reading on forums. 它节省了我在论坛上阅读的大量时间。

Download: http://xsocket.org/ 下载: http//xsocket.org/

Tutorial: http://xsocket.sourceforge.net/core/tutorial/V2/TutorialCore.htm 教程: http//xsocket.sourceforge.net/core/tutorial/V2/TutorialCore.htm

Server Code: 服务器代码:

import org.xsocket.connection.*;

/**
 *
 * @author wsserver
 */
public class XServer {

    protected static IServer server;

    public static void main(String[] args) {
        try {
            server = new Server(9905, new XServerHandler());
            server.start();
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
     protected static void shutdownServer(){
        try{
            server.close();
        }catch(Exception ex){
            System.out.println(ex.getMessage());
        }        
    }
}

Server Handler: 服务器处理程序:

import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.*;
import org.xsocket.*;
import org.xsocket.connection.*;

public class XServerHandler implements IConnectHandler, IDisconnectHandler, IDataHandler {

    private Set<ConnectedClients> sessions = Collections.synchronizedSet(new HashSet<ConnectedClients>());

    Charset charset = Charset.forName("ISO-8859-1");
    CharsetEncoder encoder = charset.newEncoder();
    CharsetDecoder decoder = charset.newDecoder();
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    @Override
    public boolean onConnect(INonBlockingConnection inbc) throws IOException, BufferUnderflowException, MaxReadSizeExceededException {
        try {
            synchronized (sessions) {
                sessions.add(new ConnectedClients(inbc, inbc.getRemoteAddress()));
            }
            System.out.println("onConnect"+" IP:"+inbc.getRemoteAddress().getHostAddress()+" Port:"+inbc.getRemotePort());
        } catch (Exception ex) {
            System.out.println("onConnect: " + ex.getMessage());
        }
        return true;
    }

    @Override
    public boolean onDisconnect(INonBlockingConnection inbc) throws IOException {
        try {
            synchronized (sessions) {
                sessions.remove(inbc);
            }
            System.out.println("onDisconnect");
        } catch (Exception ex) {
            System.out.println("onDisconnect: " + ex.getMessage());
        }
        return true;
    }

    @Override
    public boolean onData(INonBlockingConnection inbc) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException {
        inbc.read(buffer);
        buffer.flip();
        String request = decoder.decode(buffer).toString();
        System.out.println("request:"+request);
        buffer.clear();
        return true;
    }
}

Connected Clients: 连接客户:

import java.net.InetAddress;
import org.xsocket.connection.INonBlockingConnection;

/**
 *
 * @author wsserver
 */
public class ConnectedClients {

    private INonBlockingConnection inbc;
    private InetAddress address;

    //CONSTRUCTOR
    public ConnectedClients(INonBlockingConnection inbc, InetAddress address) {
        this.inbc = inbc;
        this.address = address;
    }

    //GETERS AND SETTERS
    public INonBlockingConnection getInbc() {
        return inbc;
    }

    public void setInbc(INonBlockingConnection inbc) {
        this.inbc = inbc;
    }

    public InetAddress getAddress() {
        return address;
    }

    public void setAddress(InetAddress address) {
        this.address = address;
    }
}

Client Code: 客户代码:

import java.net.InetAddress;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.NonBlockingConnection;

/**
 *
 * @author wsserver
 */
public class XClient {

    protected static INonBlockingConnection inbc;
    public static void main(String[] args) {
        try {
            inbc = new NonBlockingConnection(InetAddress.getByName("localhost"), 9905, new XClientHandler());

            while(true){

            }
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

Client Handler: 客户端处理程序:

import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.IConnectExceptionHandler;
import org.xsocket.connection.IConnectHandler;
import org.xsocket.connection.IDataHandler;
import org.xsocket.connection.IDisconnectHandler;
import org.xsocket.connection.INonBlockingConnection;

/**
 *
 * @author wsserver
 */
public class XClientHandler implements IConnectHandler, IDataHandler,IDisconnectHandler, IConnectExceptionHandler {

    Charset charset = Charset.forName("ISO-8859-1");
    CharsetEncoder encoder = charset.newEncoder();
    CharsetDecoder decoder = charset.newDecoder();
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    @Override
    public boolean onConnect(INonBlockingConnection nbc) throws IOException {
        System.out.println("Connected to server");
        nbc.write("hello server\r\n");
        return true;
    }

    @Override
    public boolean onConnectException(INonBlockingConnection nbc, IOException ioe) throws IOException {

        System.out.println("On connect exception:"+ioe.getMessage());
        return true;
    }

    @Override
    public boolean onDisconnect(INonBlockingConnection nbc) throws IOException {

        System.out.println("Dissconected from server");
        return true;
    }

    @Override
    public boolean onData(INonBlockingConnection inbc) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException {

        inbc.read(buffer);
        buffer.flip();
        String request = decoder.decode(buffer).toString();
        System.out.println(request);
        buffer.clear();
        return true;
    }
}

I spent a lot of time reading on forums about this, i hope i can help u with my code. 我花了很多时间在论坛上阅读这篇文章,我希望我可以用我的代码帮助你。

First of all, you should not really be using wait() and notify() calls anymore since there exist good Standard Java (1.5+) wrapper classes in java.util.concurrent, such as BlockingQueue . 首先,您不应该再使用wait()和notify()调用,因为在java.util.concurrent中存在良好的标准Java(1.5+)包装类,例如BlockingQueue

Second, it's suggested to do IO in the selecting thread itself, not in the worker threads. 其次,建议在选择线程本身中执行IO,而不是在工作线程中执行IO。 The worker threads should just queue up reads/and writes to the selector thread(s). 工作线程应该排队读取/写入选择器线程。

This page explains it pretty good and even provides working code samples of a simple TCP/IP server: http://rox-xmlrpc.sourceforge.net/niotut/ 这个页面解释得很好,甚至提供了一个简单的TCP / IP服务器的工作代码示例: http//rox-xmlrpc.sourceforge.net/niotut/

Sorry, I don't yet have time to look at your specific example. 对不起,我还没有时间查看您的具体示例。

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

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