When I run the below locally (on my own computer) it works fine - I can send messages to it and it reads them in properly. As soon as I put this on a remote server and send a message, only half the message gets read.
try {
this.asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(80));
this.asynchronousServerSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
#Override
public void completed(AsynchronousSocketChannel asynchronousSocketChannel, Void att) {
try {
asynchronousServerSocketChannel.accept(null, this);
ByteBuffer byteBuffer = ByteBuffer.allocate(10485760);
asynchronousSocketChannel.read(byteBuffer).get(120000, TimeUnit.SECONDS);
byteBuffer.flip();
System.out.println("request: " + Charset.defaultCharset().decode(byteBuffer).toString());
} catch (CorruptHeadersException | CorruptProtocolException | MalformedURLException ex) {
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
}
}
#Override
public void failed(Throwable exc, Void att) {
}
});
} catch (IOException ex) {
}
I've looked around at other questions and tried some of the answers but nothing worked so far. I thought the cause might be that it's timing out due to it being slower over the network when it's placed remotely but increasing the timeout didn't resolve the issue. I also considered that the message might be too large but allocating more capacity to the ByteBuffer didn't resolve the issue either.
I believe your issue is with the Asynchronous nature of the code you're using. What you have is an open connection and you've called the asynchronous read method on your socket.
This reads n bytes from the channel where n is anything from 0 to the size of your available buffer.
I firmly believe that you have to read in a loop. That is, with Java's A-NIO; you'd need to call read again from your completed method on your CompletionHandler by, possibly, passing in the AsynchronousSocketChannel as an attachment to a new completed method on a CompletionHandler you create for read , not the one you already have for accept methods.
I think this is the same sort of pattern you'd use where you'd call accept again with this as the completion handler from your completed method in the CompletionHandler you're using for the accept method call.
It then becomes important to put an "Escape" clause into your CompletionHandler for instance, if the result is -1 or if the ByteBuffer had read X number of bytes based on what you're expecting, or based on if the final byte in the ByteBuffer is a specific message termination byte that you've agreed with the sending application.
The Java Documentation on the matter goes so far as to say the read method will only read the amount of bytes on the dst at the time of invocation.
In Summary; the completed method call for the handler for the read seems to execute once something was written to the channel; but if something is being streamed you could get half of the bytes, so you'd need to continue reading until you're satisfied you've got the end of what they were sending.
Below is some code I knocked together on reading until the end, responding whilst reading, asynchronously. It, unlike myself, can talk and listen at the same time.
public class ReadForeverCompletionHandler implements CompletionHandler<Integer, Pair<AsynchronousSocketChannel, ByteBuffer>> {
#Override
public void completed(Integer bytesRead, Pair<AsynchronousSocketChannel, ByteBuffer> statefulStuff) {
if(bytesRead != -1) {
final ByteBuffer receivedByteBuffer = statefulStuff.getRight();
final AsynchronousSocketChannel theSocketChannel = statefulStuff.getLeft();
if (receivedByteBuffer.position()>8) {
//New buffer as existing buffer is in use
ByteBuffer response = ByteBuffer.wrap(receivedByteBuffer.array());
receivedByteBuffer.clear(); //safe as we've not got any outstanding or in progress reads, yet.
theSocketChannel.read(receivedByteBuffer,statefulStuff,this); //Basically "WAIT" on more data
Future<Integer> ignoredBytesWrittenResult = theSocketChannel.write(response);
}
}
else {
//connection was closed code
try {
statefulStuff.getLeft().shutdownOutput(); //maybe
}
catch (IOException somethingBad){
//fire
}
}
}
#Override
public void failed(Throwable exc, Pair<AsynchronousSocketChannel, ByteBuffer> attachment) {
//shout fire
}
The read is originally kicked off by a call from the completed method in the handler from the very original asynchronous accept on the server socket like
public class AcceptForeverCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, Pair<AsynchronousServerSocketChannel, Collection<AsynchronousSocketChannel>>> {
private final ReadForeverCompletionHandler readForeverAndEverAndSoOn = new ReadForeverCompletionHandler();
#Override
public void completed(AsynchronousSocketChannel result, Pair<AsynchronousServerSocketChannel, Collection<AsynchronousSocketChannel>> statefulStuff) {
statefulStuff.getLeft().accept(statefulStuff, this); //Accept more new connections please as we go
statefulStuff.getRight().add(result); //Collect these in case we want to for some reason, I don't know
ByteBuffer buffer = ByteBuffer.allocate(4098); //4k seems a nice number
result.read(buffer, Pair.of(result, buffer ),readForeverAndEverAndSoOn); //Kick off the read "forever"
}
#Override
public void failed(Throwable exc, Pair<AsynchronousServerSocketChannel, Collection<AsynchronousSocketChannel>> attachment) {
//Shout fire
}
}
Related
I am working on a Java UDP application. There is a thread in the application whose only job is to listen to a server on a specific port.
I wrote the application under the mistaken assumption that the server I am listening to will always be up; this, however, was a bad assumption.
If my application starts after the server is running, then everything works fine. If my application starts before the server is up, or if the server is restarted while my application is running, my application breaks.
MainClass.java
public class MainClass {
public static void main(String[] args){
ListeningClass myListeningClass = new ListeningClass();
Thread listenerThread = new Thread(myListeningClass);
listenerThread.setName("My Listening Thread");
listenerThread.start();
}
}
ListeningClass.java
public class ListeningClass implements Runnable {
private volatile boolean run = true;
private byte[] receiveBuffer;
private int receiveBufferSize;
private DatagramSocket myDatagramSocket;
private DatagramPacket myDatagramPacket;
#Override
public void run(){
try {
myDatagramSocket = new DatagramSocket(null);
InetSocketAddress myInetSocketAddress = new InetSocketAddress(15347);
myDatagramSocket.bind(myInetSocketAddress);
receiveBuffer = new byte[2047];
myDatagramPacket = new DatagramPacket(receiveBuffer, 2047);
while(run){
myDatagramSocket.receive(myDatagramPacket);
byte[] data = myDatagramPacket.getData();
receiveBufferSize = myDatagramPacket.getLength();
// process the data received here
}
} catch (SocketException se){
// do stuff
} catch (IOException ioe){
// do stuff
}
}
public boolean isRun(){
return run;
}
public void setRun(boolean run){
this.run = run;
}
}
Like I said, if my application starts after the server is running, everything works perfectly, just as expected. However, if my application starts before the server is running, then nothing works. Obviously, is is because the thread tries to open the connection once, and if it fails, then it never tries again.
I moved the DatagramSocket open code to within the while block but that wasn't pretty. I got a bunch of "port already bound" errors.
So, how can I reconstruct this so that it works properly?
It's not really a concurrency question. You just need to check the exceptions thrown on receive and handle appropriately. In this case, rebind the socket. See the docs for receive.
For example:
...
while(run) {
try {
myDatagramSocket.receive(myDatagramPacket);
byte[] data = myDatagramPacket.getData();
receiveBufferSize = myDatagramPacket.getLength();
// process the data received here
} catch (IOException ioe) {
// Perhaps use PortUnreachableException but not guaranteed
rebind(myDatagramSocket, myInetSocketAddress);
}
}
private void rebind(DatagramSocket s, InetSocketAddress addr) {
s.bind(addr);
}
I think that should be enough. the point is, you only want to rebind if your receive indicates there's an I/O problem with the server. You're binding for each receive if you place the bind in the loop - which is ok in your happy-path situation.
The important things to note here are the precise points in which the program fails and the type of exception that you're given.
Presumably it fails on line myDatagramSocket.receive(myDatagramPacket);, but double check with the stacktrace on your exception. The second thing to check is the type of exception. Is it a SocketException or a subclass of SocketException? Is there a specific error code? Try to be as specific as possible.
At this point, you should surround the section of code that fails in its own try catch within the while loop. You want to be able to say that should it fail, your thread will sleep and try again after a hiatus (to not bombard the server with requests). And to simply things further, I would section the code in its own method, so you would expect something like:
public class ListeningClass implements Runnable {
private static final int MAX_RETRIES = 30;
private static final int RETRY_SLEEPTIME = 30000;
private volatile boolean run = true;
private InetSocketAddress myInetSocketAddress;
#Override
public void run(){
try {
DatagramSocket myDatagramSocket = new DatagramSocket(null);
myInetSocketAddress = new InetSocketAddress(15347);
myDatagramSocket.bind(myInetSocketAddress);
byte[] receiveBuffer = new byte[2047];
DatagramPacket myDatagramPacket = new DatagramPacket(receiveBuffer, 2047);
awaitRequests(myDatagramSocket, myDatagramPacket)
} catch (SocketException se){
// do stuff
} catch (IOException ioe){
// do stuff
}
}
private void awaitRequests(DatagramSocket myDatagramSocket, DatagramPacket myDatagramPacket) throws SocketException, IOException {
int maxRetries = MAX_RETRIES;
while(run){
try {
myDatagramSocket.receive(myDatagramPacket);
byte[] data = myDatagramPacket.getData();
// Packet received correctly, reset retry attempts
maxRetries = MAX_RETRIES;
process(myDatagramPacket);
} catch (SocketException e) {
maxRetries--;
// Good place to write to log of some kind
if(maxRetries == 0) {
throw e;
}
Thread.currentThread().sleep(RETRY_SLEEPTIME);
// Lets attempt to restablish the connection
reconnect(myDatagramSocket);
}
}
}
private void process(DatagramPacket myDatagramPacket) {
int receiveBufferSize = myDatagramPacket.getLength();
// process the data received here
}
private void reconnect(DatagramSocket myDatagramSocket) {
myDatagramSocket.bind(myInetSocketAddress);
}
public boolean isRun(){
return run;
}
public void setRun(boolean run){
this.run = run;
}
}
Note a couple things. I only caught SocketException because I am assuming the type of exception that you're getting is a SocketException. If you're getting an IOException of some kind, then you should check that. Better still if you're specifying the subtype of that exception. The reason is this: you don't want to blanket handle all errors, but only those pertaining to the server being down. If the program lacks authentication to open the socket, you would want to fail immediately, not continually retry.
The second thing is that I've separated the processing of the packet in its own method, because I think that's the proper thing to do in these cases.
I've create a tcp socket with a thread and it works well. Now I need to send a request to server and to listen it. It could answer me after 100 seconds. So I've thought to use an asynctask, but 100 second are a lot...
So I use an IntentService in a class (this class is private because there is in an other class):
public class Generate extends Fragment
{
/* ***
the code about of my app
*** */
private class Position extends IntentService
{
public Position()
{
super("Position");
}
protected void onHandleIntent(Intent intent)
{
try
{
socket.setSoTimeout(100000);
PrintWriter out = new PrintWriter(socket.getOutputStream());
out.write("TX_DATA");
byte[] bytes = new byte[1000];
StringBuilder reader = new StringBuilder();
int numRead = 0;
if ((numRead = socket.getInputStream().read(bytes)) >= 0)
{
reader.append(new String(bytes, 0, numRead));
}
if(reader.toString().equals("RX_DATA"))
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),"Data received",Toast.LENGTH_LONG).show();
}
});
}
catch (SocketException e)
{
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
but..it's correct to do how I need to? I need to wait 100 second, listen the answers of the server, upgrade the UI and close the service...
How this intentservice works?
Thank you for the answers
So I've thought to use an asynctask, but 100 second are a lot
Yes. In particular, if the user leaves your app (e.g., BACK, HOME), there is a chance that Android will terminate your process before your work is complete.
So I use an IntentService
That is better than an AsyncTask in this case. If you have background work that will take longer than a couple of seconds, having that background work be managed by a service increases the likelihood that your process will be around long enough to complete that work.
For 100+ seconds, you also have the issue of the device falling asleep before your work is complete. You will need to use a WakeLock (perhaps via my WakefulIntentService) and perhaps a WifiLock to ensure that everything stays stable.
Would it be appropriate to use a thread to get objects received by a socket's InputStream and then add them to a ConcurrentLinkedQueue so that they can be accessed from the main thread without blocking at the poll-input loop?
private Queue<Packet> packetQueue = new ConcurrentLinkedQueue<Packet>();
private ObjectInputStream fromServer; //this is the input stream of the server
public void startListening()
{
Thread listeningThread = new Thread()
{
public void run()
{
while(isConnected()) //check if the socket is connected to anything
{
try {
packetQueue.offer((Packet) fromServer.readObject()); //add packet to queue
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
listeningThread.start(); //start the thread
}
public Packet getNextPacket()
{
return packetQueue.poll(); //get the next packet in the queue
}
It depends on what you need to do with this object that you'll use in main thread.
If need sometime to process it or if it'll be used many times than you can put it in a queue or in another class that will hold this object for you, but if the time you need to process it is low you and you don't need this object further after processing you don't really need to use a queue.
About using the ConcurrentQueue depends too, you need order? you need guarantee synchronism between the read and the write?
You can use Asynchronous socket too to handle many clients and process in the same thread or even getting the objects from them and throwing in a queue to further process.
But "be appropriate" is hard to answer because depends on what you need to do with this objects and how you'll handle it.
hey I am trying to make a console application that can receive and send messages to the clients.
It will accept multiple clients & handle them.
To add a new client i do this in the run method:
#Override
public void run() {
try {
this.server = new ServerSocket(this.port);
this.factory = new ServerFactory(this.server);
System.out.println("Server runs and now waiting for clients");
this.runClientHandler();
Socket client;
while ((client = this.server.accept()) != null) {
this.handler.addClient(this.factory.createClient(client));
System.out.println("done");
}
} catch (IOException e) {
e.printStackTrace();
}
}
But "done" will never be printed because of this client's infinite loop for his message:
public void handleClient() throws IOException {
byte[] buffer = new byte[5*1024];
int read = -1;
byte[] data;
String message;
while ((read = this.socket.getInputStream().read(buffer)) > -1) {
data = new byte[read];
System.arraycopy(buffer, 0, data, 0, read);
message = new String(data, "UTF-8");
System.out.println("Client message: " + message);
}
}
handleClient() method will run in Thread-2 at handleClients.add():
public void addClient(Client c) throws IOException {
c.writeMessageStream("hey");
System.out.println("New client!");
this.clients.add(c);
//prints here
c.handleClient();
//never reaches this..
}
How can I ignore the while loop and let the program execute while the while loop runs without making a new thread for each client?
Check NIO Selectors. They are part of Java NIO in JDK. Or you can use an out-of-the-box solutions like Netty or (worse) Apache MINA.
Your code won't be able to handle multiple clients as it is serving the client from the same thread it is accepting connections. Generally, the client connections should be handled by different threads and you may like to use asynchronous IO so that multiple connections can be handled from a single thread. You should use Netty which simplified all these. Here are some example programs http://netty.io/5.0/xref/io/netty/example/telnet/package-summary.html
I've been using this tutorial for a simple file transfer client/server using socket IO. I changed the response handler to accept multiple reads as a part of one file, as I will be dealing with large files, potentially up to 500 MB. The tutorial didn't account for large server responses, so I'm struggling a bit, and I've created a race condition.
Here's the response handler code:
public class RspHandler {
private byte[] rsp = null;
public synchronized boolean handleResponse(byte[] rsp) {
this.rsp = rsp;
this.notify();
return true;
}
public synchronized void waitForResponse() {
while(this.rsp == null) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println("Received Response : " + new String(this.rsp));
}
public synchronized void waitForFile(String filename) throws IOException {
String filepath = "C:\\a\\received\\" + filename;
FileOutputStream fos = new FileOutputStream(filepath);
while(waitForFileChunk(fos) != -1){}
fos.close();
}
private synchronized int waitForFileChunk(FileOutputStream fos) throws IOException
{
while(this.rsp == null) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
fos.write(this.rsp);
int length = this.rsp.length;
this.rsp = null;
if(length < NioClient.READ_SIZE)//Probably a bad way to find the end of the file
{
return -1;
}
else
{
return length;
}
}
}
The main thread of the program creates a RspHandler on the main thread, and passes it to a client, created on a separate thread. The main thread tells the client to request a file, then tells the RspHandler to listen for a response. When the client reads from the server(it reads in chunks of about 1KB right now), it calls the handleResponse(byte[] rsp) method, populating the rsp byte array.
Essentially, I'm not writing the received data to a file as fast as it comes. I'm a bit new to threads, so I'm not sure what to do to get rid of this race condition. Any hints?
this is classic consumer/producer. the most straightforward/easiest way to handle this is to use a BlockingQueue. producer calls put(), consumer calls take().
note, using a BlockingQueue usually leads to the "how do i finish" problem. the best way to do that is to use the "poison pill" method, where the producer sticks a "special" value on the queue which signals to the consumer that there is no more data.