I'm working on a transport mechanism, where I am supposed to ignore OP_READ events on a socket, if the internal queue is already full.
SelectionKey next = it.next();
if (next.isReadable()) {
SocketChannel client = (SocketChannel) next.channel();
if (!innerqueueIsFull()) {
if (client.read(nextRead) == -1) {
break;
}
}
}
After writing a response for one of the pending requests, I want to go back and retry reading the data packets on the channel. How can I retry reading the request?
I'm working on a transport mechanism, where I am supposed to ignore OP_READ events on a socket, if the internal queue is already full.
The correct way to do that is to unregister interest in OP_READ for that channel, or for all channels if there is only one internal queue. Then you won't even get the isReadable() events on the channel. When the queue clears, re-register OP_READ.
The effect will be to block the affected peers from sending.
How can I retry reading the request?
You don't have to retry anything this way. Just react to isReadable() in the normal way. As a matter of fact you don't have to retry anything your way either, as you never did the read in the first place, so there is nothing to retry.
NB If client.read() returns -1 it is utterly bizarre to break. You should close the channel and continue. There's no reason not to process other events just because one client has disconnected.
Related
I am trying to simulate UDP using Java. I am sending a file from one host to another. This is the part of the receiver:
server.setSoTimeout(10000);
while (true)
{
try
{
DatagramPacket received = new DatagramPacket(receivedData,receivedData.length);
server.receive(received);
out.write(received.getData());
}
catch (IOException e) {
break;
}
}
server.close();
This solution works, but I am not satisfied with it for some reason.
Sender sends all the packets and then it closes the DatagramSocket. Receiver gets all the packets and it terminates, but it terminates because of the timeout.
So if switch on my receiver and don't execute anything for 10 secs, my Receiver shuts off, so nothing is transmitted.
Is there a way of terminating the loop without specifying the timeout?
I was also wondering if there is a method for the other host to establish connection - something like ServerSocket.accept(), which basically waits for the other host to connect.But, I decided to use DatagramSocket and I can't find a solution to this issue.
Does anybody know of a method that would perform this?
No.
Datagram (UDP) sockets are inherently connectionless. Closing a DatagramSocket does not have any effect which is visible to a remote system. It prevents an application from sending or receiving any further data on that socket, and frees up the port for use by other applications on the local system, but it does not cause any notification to be sent over the network.
If you want to notify the remote server that you are done sending data, you will need to send them a datagram notifying them of that.
If you are trying to transfer a file over UDP, keep in mind that UDP packets are not guaranteed to be received, nor are they guaranteed to be received in the same order they are transmitted! (That is, they may be dropped or reordered by the network.)
it's not my first time trying to understand this issue but i hope it will be the last one:
some background:
i have a Java SocketChannel NIO server working in non-blocking mode.
this server has multiple clients which send and receive messages from it.
each client maintain its connection to the server with "keepalive" messages every once in a while.
The main idea with the server is that the clients will remain connect "all the time" and receive messages from it in "push" mode.
now to my question:
in Java NIO read() function - when the read() return -1 - it means that its EOS.
in the question i've asked here i realized that it means that the socket has finished its current stream and doesn't need to be closed..
when searching in google a bit more about this i found out that it does mean that the connection is closed on the other side..
what does the word "stream" exactly means? is it the current message being sent from the client? is it the ability of the client side connection to send anymore messages ?
why would a SocketChannel be closed on the client side if the client never told him to be closed ?
what is the difference between read() return -1 and connection reset by peer I/O error ?
this is how i read from SocketChannel:
private JSONObject readIncomingData(SocketChannel socketChannel)
throws JSONException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
JSONObject returnObject = null;
ByteBuffer buffer = ByteBuffer.allocate(1024);
Charset charset = Charset.forName("UTF-8");
String endOfMesesage = "\"}";
String message = "";
StringBuilder input = new StringBuilder();
boolean continueReading = true;
while (continueReading && socketChannel.isOpen())
{
buffer.clear();
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1)
{
continueReading = false;
continue;
}
buffer.flip();
input.append(charset.decode(buffer));
message = input.toString();
if (message.contains(endOfMesesage))
continueReading = false;
}
if (input.length() > 0 && message.contains(endOfMesesage))
{
JSONObject messageJson = new JSONObject(input.toString());
returnObject = new JSONObject(encrypter.decrypt(messageJson.getString("m")));
}
return returnObject;
}
What does the word "stream" exactly means? is it the current message being sent from the client? is it the ability of the client side connection to send anymore messages ?
The stream means the data that is flowing between two locations, usually between the client and the server but effectively it's any kind of data flowing. E.g. if you read a file from your hard disc you use a FileInputStream which represents data flowing from the file on disc to your program. It's a very generic concept. Think of it as a river where the water is the data. Plus it's a very cool kind of river which allows you to control how the water/data is flowing.
Why would a SocketChannel be closed on the client side if the client never told him to be closed ?
That can happen if the connection between client and server is reset or interrupted. Your program should never assume that connections just live and are never interrupted. Connections are interrupted for all kinds of reasons, may it be a flaky network component, someone pulling a plug that should better be left where it was or the wireless network is going down. Also the server might close the connection, e.g. if the server program goes down, has a bug or the connection runs into a timeout. Always remember that open connections are a limited resource so servers might decide to close them if they are idle for too long.
What is the difference between read() return -1 and connection reset by peer I/O error ?
When the read() returns -1 this simply means that there is currently no more data in the stream. A connection reset means, there was probably more data, but the connection no longer exists and therefore this data cannot be read anymore. Again taking the river anology: Think of the data as some quantity of water being sent from a village upstream (aka Serverville) to a village downstream (aka Clientville) using a riverbed that connects the two villages (the connection). Now someone at Serverville pulls the big lever and the water (the data) flows down from Serverville to Clientville. After Serverville has sent all the water it wanted to send, it closes the lever and the riverbed will be empty again (and actually destroyed as the connection got closed). This is where Clientville get's the -1. Now imagine some bulldozer interrupting the riverbed and some of the water never makes it to Clientville. This is the "connection reset" situation.
Hope this helps :)
what does the word "stream" exactly means? is it the current message being sent from the client?
It is a stream of bytes, not messages. You can use those bytes to form a message but the stream has no idea you are doing this, nor does it support messages in any way.
why would a SocketChannel be closed on the client side if the client never told him to be closed ?
It can only be closed with a -1 if the other end closed it.
what is the difference between read() return -1 and connection reset by peer I/O error ?
You can close or drop a connection other ways such as closing it from the same side, or a timeout in the connection e.g.you pulled out the network cable.
BTW: The way you have written the code is better suited to blocking NIO. For example, if you receive more than one whole message, anything after the first one is discarded. If you use blocking IO and keep everything you read you will not get corrupted or dropped messages.
What does the word "stream" exactly means? is it the current message being sent from the client?
It basically means one side of the connection, which is full-duplex. TCP is a byte-stream protocol, providing two independent byte streams, one in each direction.
Why would a SocketChannel be closed on the client side if the client never told him to be closed?
It wouldn't. The client did close the connection. That's what read() returning -1 means.
What is the difference between read() return -1 and connection reset by peer I/O error ?
read() returning -1 means the peer closed the connection properly. 'Connection reset by peer' indicates a protocol error of some kind, usually that you have written data to a connection that had already been closed by the peer.
Re your code, if read() returns -1 you must close the channel. There is no other sensible way to proceed.
Is the code below sufficient to accept concurrent UDP transmissions? More specifically, if 2 clients transmit concurrently, will DatagramSocket queue up the transmissions and deliver them one by one as I call receive(), or will only one make it through?
DatagramSocket socket = new DatagramSocket(port, address);
byte[] buffer = new byte[8192];
while(!disconnect){
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
socket.receive(p);
}
There is no queuing by default. The client may retry till timeout or similiar are reach.
UDP is quiet fast but on heavy load you may have clients that cannot connect.
If the packets make it to your networking interface (imagine lost packets on a congested wireless channel) they will passed up and the blocking method socket.receive(p) will be called. If there is a collision of packets on the channel because of two clients transmitting at the same time you will not get any of the two packets. But this is most likely not possible because the access technology of networking interfaces will take care of this, check
CSMA/CA or CSMA/CD
After calling socket.receive(p) you should create a new thread to process the packet itself. That will make sure that the next packet can be received on the socket.
EDIT:
Description of INTEL's TX and RX descriptors
A basic solution would have on thread responsible for handling a number of incoming requests (with your desired limit) and then handing them off to other worker/request handler threads. This basic structure is very much the same with most servers: a main thread responsible for handing off requests to worker threads. When each of these worker threads is finished, the you can update a shared/global counter to let the main thread know that it can establish a new connection. This will require synchronization, but it's a neat and simple abstraction.
Here's the idea:
Server Thread:
// Receive Packet
while (true) {
serverLock.acquire();
try {
if (numberOfRequests < MAX_REQUESTS) {
packet = socket.receive();
numberOfRequests++;
requestThread(packet).run();
} else {
serverMonitor.wait(serverLock);
}
} finally {
serverLock.release();
}
}
Request Thread:
// Handle Packet
serverLock.acquire();
try {
if (numberOfRequests == MAX_REQUESTS){
numberOfRequests--;
serverMonitor.pulse();
}
} finally {
serverLock.release();
}
This is just to give you an idea of what you can start out with. But when you get the hang of it, you'll be able to make optimizations and enhancements to make sure the synchronization is all correct.
One particular enhancement, which also lends itself to limited number of requests, is something called a ThreadPool.
I am learning the NIO package. I refer the NioServer example from here. The selector thread in NioServer.java blocks on
this.selector.select();
Iterator<SelectionKey> selectedKeys = this.selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
this.accept(key);
} else if (key.isReadable()) {
this.read(key);
} else if (key.isWritable()) {
this.write(key);
}
When a remote client connects, this.accept(key) is called and in this method the interest
interestOps is changed to Read and awakes the selector.
is this what causes the selector to select this channel? So we signal in this way for the channel to be picked?
Now suppose in write to a socket channel selector is signalled by changing the
interest that the channel is ready for write.
But suppose in write did not complete due to socket buffer full as shown in code, then we don't change the interest and keep it as is as write only.
then when will the selector select this channel?
this.accept(key) calls serverSocketChannel.accept() which returns a new socket channel for communication with the client. It's this channel that is registered with the selector for "read" operations, i.e. the selector now has two registrations:
the original ServerSocketChannel, with OP_ACCEPT
the SocketChannel for the new client, with OP_READ
If a write cannot complete due to the buffer filling up, the corresponding SocketChannel remains registered with OP_WRITE. Once the client reads some data off the other end, the channel will be selected again, allowing us to write the remaining data before flipping the interest set back to OP_READ.
OP_WRITE triggers when then there is room in the socket send buffer.
NB getting a zero length write() result is the only occasion for using OP_WRITE. Most of the time there is room, so OP_WRITE will keep triggering. You don't want this, so you normally don't have OP_WRITE registered for a channel: only when it has just returned zero from write; and you deregister it when that write eventually completes via being re-triggered after an OP_WRITE.
With the advent of NIO most socket types could be "selectable" through the SelectableChannel implementation. Unfortunately the DatagramChannel does not support multicast prior to java 7. Multicast is supported in prior versions via the MulticastSocket class.
I want some way to be able to detect that there are pending messages (i.e. readable) messages on a multicast datagram socket. I would like to read until there are no remaining datagrams within the immediate time window. Having received all pending messages, then want to invoke a callback, but not individually or prior to having read all pending messages.
Making this simpler, let's assume one socket. In pseudo code:
List<Msg> received = new ArrayList<Msg>
while (true)
{
received.clear();
// initial blocking receive
data = receive_blocking (socket, datagram)
received.add (new Msg(data));
// flush out remaining messages
for (boolean receiving = true ; receiving ; )
{
// non-blocking
if (receive_nonblocking (socket, datagram))
received.add (new Msg(datagram));
else
receiving = false;
}
callback (received);
}
The question is how to implement receive_nonblocking without NIO 2. I do not need the Selector mechanism, but wondering whether there is some way I can do a non-blocking read(s) or otherwise detect whether there is something pending.
I had read that to use the selector, the channels must be created directly as in new DatagramChannel(), rather than acquiring a channel after socket creation. So if am correct, could not use the socket.getChannel() to create a selector post socket creation.
Is there any way to do this that doesn't involve JNI or timers, pre java 7?
Just set a very short read timeout, and catch SocketTimeoutException, which will be thrown when it expires, and break out of your reading loop.