【问题标题】:NIO Hang Up Problems?NIO 挂断问题?
【发布时间】:2011-11-13 22:32:56
【问题描述】:

我在使用 NIO 框架在主机和客户端之间通过 SocketChannel 发送数据时遇到问题。

我以前从来没有真正费心学习过 NIO,但是随着 java.nio.files 包的引入和其他各种改进,我想我会尝试一下。

我能够让 SocketChannel 和 ServerSocketChannel 正常连接,但实际的数据传输表现得很奇怪。它永远不会在客户端正确完成,总是在最终读取后挂断。此外,它有时会读取不正确的数据量(太多或太少),甚至导致 Windows 资源管理器发疯并分配系统的所有内存,导致计算机崩溃。

这是我现在的代码(它是测试代码):

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();
        }
    }
}

【问题讨论】:

    标签: java networking nio java-7 java.nio.file


    【解决方案1】:

    ServerSocketChannel.accept() 产生的SocketChannel 已连接。不可能的。 isConnected() 测试毫无意义。

    您的服务器 I/O 代码不正确。通道写入必须以buffer.flip() 开头,后跟buffer.compact(). 从一个通道复制到另一个通道的规范方法如下(请注意,即使缓冲区中仍有待处理的数据,这在 EOS 上也能正常运行):

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

    与我的第一段类似,SocketChannelSocketChannel.open() 产生,然后是 SocketChannel.connect() 已连接:同样,测试毫无意义。如果未连接,则在connect() 呼叫中会有一个ConnectException

    您的客户端 I/O 代码与您的服务器 I/O 代码存在相同的问题。

    您没有关闭服务器中的SocketChannel,因此客户端将永远不会停止读取连接。

    您也没有在客户端中关闭SocketChannel

    File.exists() 测试毫无意义。如果文件不存在,以下行将引发异常,并且无论如何您都必须处理该异常,那么为什么要再次这样做呢?

    【讨论】:

    • 呃...是的,我是 NIO 以及 JDK 7 API 的新手。不过感谢您的帮助。我会尝试这些改变。那么 buffer.rewind() 呢?需要用到吗?
    • 关闭SocketChannel不会关闭底层Socket吗?
    • @ghostsoldier23 这不是 JDK 7 API。其中大部分是 JDK 1.4 API。当然,关闭通道会关闭套接字,正如 Javadoc 中所说的那样,但您并没有这样做。您唯一要关闭的是 FileChannel,并且仅在服务器中。所以你错过了三个关闭。你不需要buffer.rewind(),只要我发布的。
    • 成功了。谢谢你的帮助。我忍不住认出了你……你不久前还纠正了我的传统 I/O 库代码:D
    猜你喜欢
    • 1970-01-01
    • 2011-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    相关资源
    最近更新 更多