Question

I'm having problems with sending data over SocketChannels between a Host and Client using the NIO framework.

I've never really bothered to learn NIO before now, but with the introduction of the java.nio.files package and other various improvements I thought I would give it a try.

I'm able to get the SocketChannel and ServerSocketChannel to connect fine, but the actual data transfer is acting very weird. It NEVER finishes correctly on the Client side, always hanging up after the final read. Furthermore, it sometimes reads the incorrect amount of data (too much or too little) and has even caused Windows Explorer to go crazy and allocate literally ALL of the system's memory, crashing the computer.

Here is the code (it is test code) I have right now:

package bg.jdk7.io;

import static java.nio.file.StandardOpenOption.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class NetIO {

static volatile long filesize;

public static void main(String[] args) {
    new Thread(new Client()).start();
    try {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(5555));
        SocketChannel sc = ssc.accept();
        if(sc.isConnected()) {
            ByteBuffer buff = ByteBuffer.allocate(10240);
            Path fp = Paths.get(System.getProperty("user.home")+"\\Documents\\clip0025.avi");
            if(Files.exists(fp)) {
                FileChannel fc = (FileChannel) Files.newByteChannel(fp, StandardOpenOption.READ);
                long tot = Files.size(fp);
                long run = 0;
                int read = 0;
                int prog = 0;
                while((read = fc.read(buff))>0) {
                    buff.rewind();
                    sc.write(buff);
                    run+=buff.position();
                    int last = prog;
                    prog = (int)(((double)run/tot)*100);
                    if(prog !=last) {
                        System.out.println(prog + "%");
                    }
                    buff.flip();
                }
                fc.close();
                System.out.println("Sending completed");
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

static class Client implements Runnable {

    public void run() {
        try {
            SocketChannel sc = SocketChannel.open();
            sc.connect(new InetSocketAddress("localhost",5555));
            if(sc.isConnected()) {
                Path dpf = Paths.get("\\NIO_TESTING\\");
                Path dp = Paths.get(dpf+"\\clip.avi");
                Files.createDirectories(dpf);
                FileChannel fc = (FileChannel)  Files.newByteChannel(dp, CREATE, WRITE, TRUNCATE_EXISTING);
                ByteBuffer buff = ByteBuffer.allocate(10240);
                int read;
                int total = 0;
                while((read = sc.read(buff))>0) {
                    total+=read;
                    buff.rewind();
                    fc.write(buff);
                    System.out.println(fc.size());
                    buff.flip();
                    if(total == filesize) System.out.println("File data received successfully...");
                }
                System.out.println("Completed successfully");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Was it helpful?

Solution

A SocketChannel resulting from ServerSocketChannel.accept() is connected. There is no way it can't be. The isConnected() test is pointless.

Your server I/O code is incorrect. Channel writes must be preceded by buffer.flip() and followed by buffer.compact(). The canonical way to copy from one channel to another is as follows (note that this behaves correctly at EOS even when there is pending data still in the buffer):

while (in.read(buffer) >= 0 || buffer.position() > 0)
{
  buffer.flip();
  out.write(buffer);
  buffer.compact();
}

Similarly to my first paragraph, a SocketChannel resulting from SocketChannel.open() followed by SocketChannel.connect() is connected: again, the test is pointless. If it wasn't connected there would have been a ConnectException on the connect() call.

Your client I/O code has the same problems as your server I/O code.

You aren't closing the SocketChannel in the server, so the client will never stop reading from the connection.

You aren't closing the SocketChannel in the client either.

The File.exists() test is pointless. The following line will throw an exception if the file isn't there, and you have to handle that exception anyway, so why do it all again?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top