Java : zeromq , try to set response timeout in MT server - java

im using ZeroMQ to client / server application ,
now in my MT server i try to set timeout, i tried to set on the server :
socket.setReceiveTimeOut(2000);
socket.setSendTimeOut(2000);
with no luck , how do i set timeout on response .
this is my multi thread server code this is taken from zeromq examples for mt server :
/*
* Multithreaded Hello World server in Java
*
* #author Vadim Shalts
* #email vshalts#gmail.com
*
*/
import org.zeromq.ZMQ;
import org.zeromq.ZMQQueue;
class mtserver {
static void main(String[] args) {
final ZMQ.Context context = ZMQ.context(1);
ZMQ.Socket clients = context.socket(ZMQ.ROUTER);
clients.bind ("tcp://*:5555");
ZMQ.Socket workers = context.socket(ZMQ.DEALER);
workers.bind ("inproc://workers");
for(int thread_nbr = 0; thread_nbr < 5; thread_nbr++) {
Thread worker_routine = new Thread() {
#Override
public void run() {
ZMQ.Socket socket = context.socket(ZMQ.REP);
socket.connect ("inproc://workers");
while (true) {
// Wait for next request from client (C string)
byte[] request = socket.recv (0);
System.out.println ("Received request: ["+new String(request,0,request.length-1)+"]");
// Do some 'work'
try {
Thread.sleep (1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
// Send reply back to client (C string)
byte[] reply = "World ".getBytes();
reply[reply.length-1] = 0; //Sets the last byte of the reply to 0
socket.send(reply, 0);
}
}
};
worker_routine.start();
}
// Connect work threads to client threads via a queue
ZMQQueue zMQQueue = new ZMQQueue(context,clients, workers);
zMQQueue.run();
// We never get here but clean up anyhow
clients.close();
workers.close();
context.term();
}
}

Why do you need a timeout? The original examples don't use it.

Why do you need a timeout on the server?
A natural place to put a timeout in the client.
socket.setReceiveTimeOut(int milliseconds); works on a client side.

Related

How to properly use wait() and notify() in Java? (HiveMQ Client) [duplicate]

This question already has answers here:
java.lang.IllegalMonitorStateException: object not locked by thread before wait()?
(3 answers)
Closed 3 years ago.
I've writing a program using HiveMQ Client (an MQTT Open source implementation in Java) that involves using two multithreaded clients. One client is designated as the publisher and the other as the subscriber (I'm aware I could the same client can both publish and subscribe). I'm trying to design a test where the publisher sends 100 messages to the client. The goal is to time how long it takes to send and receive all the messages. I realized if I wanted to time how long it would take for the messages to be received, I would need to have the Subscribing thread wait until the publishing thread was ready to send the message. I decided to use wait() and notify() but I can't seem to implement it correctly. I'm aware that you need to use the same object which I tried to do but I can't get the design correct. I added snipers on code for both of the run methods of the two clients. CommonThread.java isn't actually a thread and I'm not running it but I tried to use it an in between class to be able to wait() and notify() but I'm missing something.
HiveMQ:
https://github.com/hivemq/hivemq-community-edition
https://github.com/hivemq/hivemq-mqtt-client
SubMainThread.java:
public void run() {
// Creates the client object using Blocking API
Mqtt5BlockingClient subscriber = Mqtt5Client.builder()
.identifier(UUID.randomUUID().toString()) // the unique identifier of the MQTT client. The ID is randomly generated between
.serverHost("localhost") // the host name or IP address of the MQTT server. Kept it localhost for testing. localhost is default if not specified.
.serverPort(1883) // specifies the port of the server
.addConnectedListener(context -> ClientConnectionRetreiver.printConnected("Subscriber1")) // prints a string that the client is connected
.addDisconnectedListener(context -> ClientConnectionRetreiver.printDisconnected("Subscriber1")) // prints a string that the client is disconnected
.buildBlocking(); // creates the client builder
subscriber.connect(); // connects the client
ClientConnectionRetreiver.getConnectionInfo(subscriber); // gets connection info
try {
Mqtt5Publishes receivingClient1 = subscriber.publishes(MqttGlobalPublishFilter.ALL); // creates a "publishes" instance thats used to queue incoming messages // .ALL - filters all incoming Publish messages
subscriber.subscribeWith()
.topicFilter(subscriberTopic)
.qos(MqttQos.EXACTLY_ONCE)
.send();
PubSubUtility.printSubscribing("Subscriber1");
System.out.println("Publisher ready to send: " + PubMainThread.readyToSend);
x.threadCondWait(); // <<<<< HOW TO MAKE THIS WORK
System.out.println("Back to the normal execution flow :P");
startTime = System.currentTimeMillis();
System.out.println("Timer started");
for (int i = 1; i <= messageNum; i++) {
Mqtt5Publish receivedMessage = receivingClient1.receive(MESSAGEWAITTIME,TimeUnit.SECONDS).get(); // receives the message using the "publishes" instance waiting up to 5 minutes // .get() returns the object if available or throws a NoSuchElementException
PubSubUtility.convertMessage(receivedMessage); // Converts a Mqtt5Publish instance to string and prints
}
endTime = System.currentTimeMillis();
finalTime = endTime - startTime;
System.out.println( finalTime + PubMainThread.finalTime + " milliseconds");
finalSecTime = TimeUnit.MILLISECONDS.toSeconds(finalTime);
System.out.println(finalSecTime + PubMainThread.finalSecTime);
}
catch (InterruptedException e) { // Catches interruptions in the thread
LOGGER.log(Level.SEVERE, "The thread was interrupted while waiting for a message to be received", e);
}
catch (NoSuchElementException e){
System.out.println("There are no received messages"); // Handles when a publish instance has no messages
}
subscriber.disconnect();
}
PubMainThread.java:
public void run() {
// Creates the client object using Blocking API
Mqtt5BlockingClient publisher = Mqtt5Client.builder()
.identifier(UUID.randomUUID().toString()) // the unique identifier of the MQTT client. The ID is randomly generated between
.serverHost("localhost") // the host name or IP address of the MQTT server. Kept it localhost for testing. localhost is default if not specified.
.serverPort(1883) // specifies the port of the server
.addConnectedListener(context -> ClientConnectionRetreiver.printConnected("Publisher1")) // prints a string that the client is connected
.addDisconnectedListener(context -> ClientConnectionRetreiver.printDisconnected("Publisher1")) // prints a string that the client is disconnected
.buildBlocking(); // creates the client builder
publisher.connect(); // connects the client
ClientConnectionRetreiver.getConnectionInfo(publisher); // gets connection info
PubSubUtility.printPublising("Publisher1");
readyToSend = true;
x.threadCondNotify(); <<<<< HOW TO MAKE THIS WORK
// Think about making the PubClient Thread sleep for a short while so its not too ahead of the client
startTime = System.currentTimeMillis();
for (int i = 1; i <= messageNum; i++) {
publisher.publishWith()
.topic(publisherTopic) // publishes to the specified topic
.qos(MqttQos.EXACTLY_ONCE)
.payload(convertedMessage) // the contents of the message
.send();
}
endTime = System.currentTimeMillis();
finalTime = endTime - startTime;
finalSecTime = TimeUnit.MILLISECONDS.toSeconds(finalTime);
PubSubUtility.printNumOfPublished("Publisher1", messageNum);
publisher.disconnect();
}
public class CommonThread {
private static final Logger LOGGER = Logger.getLogger(SubMainThread.class.getName()); // Creates a logger instance
public synchronized void threadCondNotify() {
notify();
System.out.println("Notified other thread");
}
public synchronized void threadCondWait() {
try {
while (PubMainThread.readyToSend != true) {
System.out.println("Waiting for another thread....");
wait();
}
}
catch (InterruptedException e) {
LOGGER.log(Level.SEVERE, "The thread was interrupted while waiting for another thread", e);
}
}
}
In Sender (rough Java code with some details omitted):
//package statement and imports here
class Sender extends Thread {
public static final Boolean x= new Boolean(true);
public void run() {
//initialize here
synchronized(x) {
x.notify();
}
//send messages here
}
}
In Receiver (start before Sender):
//package statement and imports here
class Receiver extends Thread {
public void run() {
//initialize here
synchronized(Sender.x) {
Sender.x.wait(); //blocks till Sender.x.notify()
}
Date start= new Date();
//receive messages here
Date end= new Date();
int duration_milliseconds= end.getTime()-start.getTime();
}
}
maybe you have to add
try{ /* code here */ } catch (InterruptedException e) {}
Feel free to discuss sense and nonsense of direct use of notify() and wait() especially in Java versions with extended concurrency libraries...

Netty Nio read the upcoming messages from ChannelFuture in Java

I am trying to use the following code which is an implementation of web sockets in Netty Nio. I have implment a JavaFx Gui and from the Gui I want to read the messages that are received from the Server or from other clients. The NettyClient code is like the following:
public static ChannelFuture callBack () throws Exception{
String host = "localhost";
int port = 8080;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
}));
}
});
ChannelFuture f = b.connect(host, port).sync();
//f.channel().closeFuture().sync();
return f;
}
finally {
//workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
ChannelFuture ret;
ClientHandler obj = new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
});
ret = callBack();
int connected = connectedClients;
if (connected != 2) {
System.out.println("The number if the connected clients is not two before locking");
synchronized (lock) {
while (true) {
connected = connectedClients;
if (connected == 2)
break;
System.out.println("The number if the connected clients is not two");
lock.wait();
}
}
}
System.out.println("The number if the connected clients is two: " + connected );
ret.channel().read(); // can I use that from other parts of the code in order to read the incoming messages?
}
How can I use the returned channelFuture from the callBack from other parts of my code in order to read the incoming messages? Do I need to call again callBack, or how can I received the updated message of the channel? Could I possible use from my code (inside a button event) something like ret.channel().read() (so as to take the last message)?
By reading that code,the NettyClient is used to create connection(ClientHandler ),once connect done,ClientHandler.channelActive is called by Netty,if you want send data to server,you should put some code here. if this connection get message form server, ClientHandler.channelRead is called by Netty, put your code to handle message.
You also need to read doc to know how netty encoder/decoder works.
How can I use the returned channelFuture from the callBack from other parts of my code in order to read the incoming messages?
share those ClientHandler created by NettyClient(NettyClient.java line 29)
Do I need to call again callBack, or how can I received the updated message of the channel?
if server message come,ClientHandler.channelRead is called.
Could I possible use from my code (inside a button event) something like ret.channel().read() (so as to take the last message)?
yes you could,but not a netty way,to play with netty,you write callbacks(when message come,when message sent ...),wait netty call your code,that is : the driver is netty,not you.
last,do you really need such a heavy library to do network?if not ,try This code,it simple,easy to understanding

com.sun.net.HttpServer load

I'm using the lightweight server provided in oracle java since java 1.6 and I'm using just to receive POST responses from another server.
Now for 2 or 3 responses it run well but when the load is increased I'm not receiving all the responses from the server, some of them are ignored.
I tried to increase the backlog to 1500 but it's the same thing.
here's how I create the server :
public class HttpServerZipWhip {
private final String SERVER_IP_ADDRESS = "X.X.X.X";
private final Integer SERVER_PORT = 9333;
private final short SERVER_BACKLOG = 1500;
private final String SERVER_CONTEXT = "/message/receive";
/**
* Constructor : Server initiation
* #param muc
* #param smshandle
*/
public HttpServerClass(Muc muc, SMSHandling_impl smshandle){
//Server Initialization
InetSocketAddress addr = new InetSocketAddress(SERVER_IP_ADDRESS, SERVER_PORT);
HttpServer server = null;
try {
server = HttpServer.create(addr, SERVER_BACKLOG);
} catch (IOException e) {
e.printStackTrace();
}
server.createContext(SERVER_CONTEXT, new MyHandler(muc, smshandle));
server.setExecutor(Executors.newCachedThreadPool());
server.start();
}
}

Netty UDP Performance Issue

I've implemented three small UDP server. One with a plain Java DatagramSocket (threaded), one with Netty and the last one also with Netty but with a threaded message handling (because Netty doesn't support multiple threads with UDP).
After some measurements I got the following results for requests per second:
DatagramSocket ~30.000 requests/second
Netty ~1.500 requests/second
Netty (threaded): ~8.000 requests/second
The real application I have to implement must handle > 25.000 requests/second. So my question is if I make something wrong with Netty or if Netty is not designed to handle that much of connections per second?
Here are the implementations
DatagramSocket Main
public static void main(String... args) throws Exception {
final int port = Integer.parseInt(args[0]);
final int threads = Integer.parseInt(args[1]);
final int work = Integer.parseInt(args[2]);
DATAGRAM_SOCKET = new DatagramSocket(port);
for (int i = 0; i < threads; i++) {
new Thread(new Handler(work)).start();
}
}
DatagramSocket Handler
private static final class Handler implements Runnable {
private final int work;
public Handler(int work) throws SocketException {
this.work = work;
}
#Override
public void run() {
try {
while (!DATAGRAM_SOCKET.isClosed()) {
final DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
DATAGRAM_SOCKET.receive(receivePacket);
final InetAddress ip = receivePacket.getAddress();
final int port = receivePacket.getPort();
final byte[] sendData = "Hey there".getBytes();
Thread.sleep(RANDOM.nextInt(work));
final DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ip, port);
DATAGRAM_SOCKET.send(sendPacket);
}
} catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
}
}
Netty Main
public static void main(String[] args) throws Exception
{
final int port = Integer.parseInt(args[0]);
final int sleep = Integer.parseInt(args[1]);
final Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup());
bootstrap.channel(NioDatagramChannel.class);
bootstrap.handler(new MyNettyUdpHandler(sleep));
bootstrap.bind(port).sync().channel().closeFuture().sync();
}
Netty Handler (threaded)
public class MyNettyUdpHandler extends MessageToMessageDecoder<DatagramPacket> {
private final Random random = new Random(System.currentTimeMillis());
private final int sleep;
public MyNettyUdpHandler(int sleep) {
this.sleep = sleep;
}
#Override
protected void decode(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket, List list) throws Exception {
new Thread(() -> {
try {
Thread.sleep(random.nextInt(sleep));
} catch (InterruptedException e) {
System.out.println("ERROR while sleeping");
}
final ByteBuf buffer = Unpooled.buffer(64);
buffer.writeBytes("Hey there".getBytes());
channelHandlerContext.channel().writeAndFlush(new DatagramPacket(buffer, datagramPacket.sender()));
}).start();
}
}
The non threaded Netty Handler is the same but without the thread.
You can change your Netty decode() method like so to make it equivalent to the DatagramSocket code:
#Override
protected void decode(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket, List list) throws Exception {
final Channel channel = channelHandlerContext.channel();
channel.eventLoop().schedule(() -> {
final ByteBuf buffer = Unpooled.buffer(64);
buffer.writeBytes("Hey there".getBytes());
channel.writeAndFlush(new DatagramPacket(buffer, datagramPacket.sender()));
}, random.nextInt(sleep), TimeUnit.MILLISECONDS);
}
But I'm guessing the sleep() code is simulating business code you will later execute.
If that is the case make sure you don't run blocking code inside the handler.
EDIT:
To answer your question below:
You got a bit confused with the channels. You create a pipeline in the bootstrap, and you bind to some port. The returned channel is the server channel. The channel in the handlers method (your decode method in your case), is like the socket you get when you accept() in traditional socket programming. Note that port you extracted from the incoming DatagramPacket - it's roughly the same. So you send data to the client back on this channel.
The code I wrote that schedules the response is simply doing the same as what your DatagramSocket code, and the threaded netty code you wrote.
I wasn't sure why you did that, and simply assumed you have a business requirement to delay the response.
If this isn't the case, you can remove the schedule call, and your code will run much faster.
If your business logic is non-blocking, and runs in a few millis, you're done. If it's blocking, you need to try to find a non-blocking alternative, or run it in an executor, i.e. not on the event loop.
Hope this helps, even though this wasn't part of your original question. Netty is awesome, and I hate seeing bad examples and bad vibes about it so it's worth my time I guess ;)
Creating a thread in every decode() is inefficient.
You can submit the task to channel.eventLoop() as Eran said if the task is simple and won't block(In fact decode() in MesaggeToMessageDecoders is executed by the channel's EventLoop,so you need not submit it manually unless you want to shedule it).
Or you can submit the task to a ThreadPoolExecutor or EventExecutorGroup.
The latter is better because you can add listeners to the Future returned by EventExecutorGroup.submit() so you don't have to wait for the task to be completed.
My English is poor,hope these can help you.
Edit:
You can write as following,just executing the simple logic code in the EventLoop(ie.I/O thread):
#Override
protected void decode(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket, List list) throws Exception {
//do something simple with datagramPacket
...
final ByteBuf buffer = Unpooled.buffer(64);
buffer.writeBytes("Hey there".getBytes());
channelHandlerContext.channel().writeAndFlush(new DatagramPacket(buffer, datagramPacket.sender()));
}

Java NIO reading and writing from distant machine

I'd like to use the NIO to send/receive data to/from a distant machine. I can send or receive data at any time, when i need to send data i just send it without any queries from the distant machine, and the distant machine send me data at regular interval. I don't understand the NIO mechanism. What generates and read or write event on the Selector SelectionKey ? Is it possible to use only one ServerSocketChannel on my side, to read data from the distant machine et to write data to it ? That is what i understand but i don't see how the writing event can be triggered... Thank you for your explanation.
I already did some coding and i can read data coming in from the distant machine, but cannot write. I use Selector and i don't know how can i write data. The logged message "handle write" is never written, but in wireshark i can see my packet.
public class ServerSelector {
private static final Logger logger = Logger.getLogger(ServerSelector.class.getName());
private static final int TIMEOUT = 3000; // Wait timeout (milliseconds)
private static final int MAXTRIES = 3;
private final Selector selector;
public ServerSelector(Controller controller, int... servPorts) throws IOException {
if (servPorts.length <= 0) {
throw new IllegalArgumentException("Parameter(s) : <Port>...");
}
Handler consolehHandler = new ConsoleHandler();
consolehHandler.setLevel(Level.INFO);
logger.addHandler(consolehHandler);
// Create a selector to multiplex listening sockets and connections
selector = Selector.open();
// Create listening socket channel for each port and register selector
for (int servPort : servPorts) {
ServerSocketChannel listnChannel = ServerSocketChannel.open();
listnChannel.socket().bind(new InetSocketAddress(servPort));
listnChannel.configureBlocking(false); // must be nonblocking to register
// Register selector with channel. The returned key is ignored
listnChannel.register(selector, SelectionKey.OP_ACCEPT);
}
// Create a handler that will implement the protocol
IOProtocol protocol = new IOProtocol();
int tries = 0;
// Run forever, processing available I/O operations
while (tries < MAXTRIES) {
// Wait for some channel to be ready (or timeout)
if (selector.select(TIMEOUT) == 0) { // returns # of ready chans
System.out.println(".");
tries += 1;
continue;
}
// Get iterator on set of keys with I/O to process
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while (keyIter.hasNext()) {
SelectionKey key = keyIter.next(); // Key is a bit mask
// Server socket channel has pending connection requests?
if (key.isAcceptable()) {
logger.log(Level.INFO, "handle accept");
protocol.handleAccept(key, controller);
}
// Client socket channel has pending data?
if (key.isReadable()) {
logger.log(Level.INFO, "handle read");
protocol.handleRead(key);
}
// Client socket channel is available for writing and
// key is valid (i.e., channel not closed) ?
if (key.isValid() && key.isWritable()) {
logger.log(Level.INFO, "handle write");
protocol.handleWrite(key);
}
keyIter.remove(); // remove from set of selected keys
tries = 0;
}
}
}
}
The protocol
public class IOProtocol implements Protocol {
private static final Logger logger = Logger.getLogger(IOProtocol.class.getName());
IOProtocol() {
Handler consolehHandler = new ConsoleHandler();
consolehHandler.setLevel(Level.INFO);
logger.addHandler(consolehHandler);
}
/**
*
* #param key
* #throws IOException
*/
#Override
public void handleAccept(SelectionKey key, Controller controller) throws IOException {
SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept();
clntChan.configureBlocking(false); // Must be nonblocking to register
controller.setCommChannel(clntChan);
// Register the selector with new channel for read and attach byte buffer
SelectionKey socketKey = clntChan.register(key.selector(), SelectionKey.OP_READ | SelectionKey.OP_WRITE, controller);
}
/**
* Client socket channel has pending data
*
* #param key
* #throws IOException
*/
#Override
public void handleRead(SelectionKey key) throws IOException {
Controller ctrller = (Controller)key.attachment();
try {
ctrller.readData();
} catch (CommandUnknownException ex) {
logger.log(Level.SEVERE, null, ex);
}
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
/**
* Channel is available for writing, and key is valid (i.e., client channel
* not closed).
*
* #param key
* #throws IOException
*/
#Override
public void handleWrite(SelectionKey key) throws IOException {
Controller ctrl = (Controller)key.attachment();
ctrl.writePendingData();
if (!buf.hasRemaining()) { // Buffer completely written ?
// Nothing left, so no longer interested in writes
key.interestOps(SelectionKey.OP_READ);
}
buf.compact();
}
}
The controller
/**
* Fill buffer with data.
* #param msg The data to be sent
* #throws IOException
*/
private void writeData(AbstractMsg msg) throws IOException {
//
writeBuffer = ByteBuffer.allocate(msg.getSize() + 4);
writeBuffer.putInt(msg.getSize());
msg.writeHeader(writeBuffer);
msg.writeData(writeBuffer);
logger.log(Level.INFO, "Write data - message size : {0}", new Object[]{msg.getSize()});
logger.log(Level.INFO, "Write data - message : {0}", new Object[]{msg});
}
/**
* Write to the SocketChannel
* #throws IOException
*/
public void writePendingData() throws IOException {
commChannel.write(writeBuffer);
}
ServerSocketChannel is used to make a connection, but not send data. You need one ServerSocketChannel and one SocketChannel per each connection.
Examples of reading and writing using SocketChannel:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
Your program will sleep at second line until data will come. You need to put this code in infinite loop and run it in background Thread. When data came you can process it from this thread, then wait for another data to come.
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put("Hello!".getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
There is no blocking methods, so if you sending small byte buffer you can call this from your main Thread.
Source
ADD:
Don't set OP_WRITE key on new connection. Only OP_READ. When you want to write some data you need to notify selector that you want to send something and send it in events loop. Good solution is to make a Queue of outcoming messages. Then follow this steps:
adding data to Queue
setting OP_WRITE to channel's key
in while (keyIter.hasNext()) loop you'll have writable key, write all data from queue and remove OP_WRITE key.
It's hard for me to understand your code, but I think you'll find out what's the problem. Also if you want to have only one connection there is no need to use Selector. And this is weird that you binding few ServerSocketChannels.
I would suggest you use blocking NIO (which is the default behaviour for SocketChannel) You don't need to use a Selector but you can use one thread for reading and another for writing.
Based on your example.
private final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024*1024);
private void writeData(AbstractMsg msg) {
writeBuffer.clear();
writeBuffer.putInt(0); // set later
msg.writeHeader(writeBuffer);
msg.writeData(writeBuffer);
writeBuffer.putInt(0, writeBuffer.position());
writeBuffer.flip();
while(writeBuffer.hasRemaining())
commChannel.write(writeBuffer);
}
What generates and read or write event on the Selector SelectionKey?
OP_READ: presence of data or an EOS in the socket receive buffer.
OP_WRITE: room in the socket send buffer.

Categories