java - 通过 java.nio channel 发送字符串无法正常工作

标签 java sockets io nio

这是我尝试过的:

服务器:

import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class JavaApplication12 {
    public static void main(String[] args) throws Exception{
    Charset charset = Charset.forName("ISO-8859-1");
    ServerSocketChannel s = ServerSocketChannel.open();
    s.configureBlocking(true);
    s.socket().bind(new InetSocketAddress(1024));
    CharBuffer c = CharBuffer.wrap("Hello from server!");
    System.out.println("writing " + c);
    ByteBuffer b = charset.encode(c);
    SocketChannel sc = s.accept();
    sc.configureBlocking(true);
    b.flip();
    int a = sc.write(b);
    sc.close();
    s.close();
    System.out.println("wrote " + a);
}
}

客户:

import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class JavaApplication11 {

    public static void main(String[] args) throws Exception {
    Charset charset = Charset.forName("ISO-8859-1");
    SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 1024));
    sc.configureBlocking(true);
    ByteBuffer b = ByteBuffer.allocate(32);
    b.flip();
    int a = sc.read(b);
    sc.close();
    b.flip();
    CharBuffer c = charset.decode(b);
    c.flip();
    System.out.println("Got " + c);
    System.out.println("read " + a );
}
}

另一边似乎只是得到一个很长且空的字符串,我不知道我做错了什么。

更新:我更新了代码,发现服务器正在写入 0 字节。有字节要写入,那么为什么 sc.write() 不写入任何内容?

更新 2:在 Vishal 的帮助下,我们终于有了一个可行的解决方案:

服务器:

Charset charset = Charset.forName("ISO-8859-1");
ServerSocketChannel s = ServerSocketChannel.open();
s.configureBlocking(true);
s.socket().bind(new InetSocketAddress(1024));
CharBuffer c = CharBuffer.wrap("Hello from server!");
ByteBuffer b = charset.encode(c);
SocketChannel sc = s.accept();
sc.configureBlocking(true);
b.compact();
b.flip();
int a = sc.write(b);
sc.close();
s.close();
System.out.println("wrote " + a);

客户:

Charset charset = Charset.forName("ISO-8859-1");
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 1024));
sc.configureBlocking(true);
ByteBuffer b = ByteBuffer.allocate(32);
int a = sc.read(b);
sc.close();
b.flip();
CharBuffer c = charset.decode(b);
System.out.println("Got " + c);

最佳答案

Buffer.flip() method switches a Buffer from writing mode to reading mode. Calling flip() sets the position back to 0, and sets the limit to where position just was.
i.e the position now marks the reading position, and limit marks how many bytes, chars etc. were written into the buffer - the limit of how many bytes, chars etc. that can be read

如果您在 Buffer.flip() 的文档中看到它指出:

After a sequence of channel-read or put operations, invoke this method to prepare for a sequence of channel-write or relative get operations.

进一步指出:

This method is often used in conjunction with the compact method when transferring data from one place to another.

在您的情况下,put操作不用于ByteBuffer创建。因此,在调用 Flip 之前,您必须调用 compact 方法。

ByteBuffer 的 compact()方法仍然是:

The bytes between the buffer's current position and its limit, if any, are copied to the beginning of the buffer. That is, the byte at index p = position() is copied to index zero, the byte at index p + 1 is copied to index one, and so forth until the byte at index limit() - 1 is copied to index n = limit() - 1 - p. The buffer's position is then set to n+1 and its limit is set to its capacity. The mark, if defined, is discarded.

The buffer's position is set to the number of bytes copied, rather than to zero, so that an invocation of this method can be followed immediately by an invocation of another relative put method. Invoke this method after writing data from a buffer in case the write was incomplete.

在服务器端的代码中,在调用 flip() 之前,位置本身就是 0 。因此,要将 position 设置为写入 b 的字节数,您必须在 b.flip() 之前调用 compact() 方法调用 以便 b.flip()limit 设置为前一个 position,即写入的字节数ByteBuffer ,并将 position 设置为 0

因此,您的服务器代码应如下所示:

import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class JavaApplication12 {
    public static void main(String[] args) throws Exception{
    Charset charset = Charset.forName("ISO-8859-1");
    ServerSocketChannel s = ServerSocketChannel.open();
    s.configureBlocking(true);
    s.socket().bind(new InetSocketAddress(1024));

    CharBuffer c = CharBuffer.wrap("Hello from server!");
    System.out.println("writing " + c);
    ByteBuffer b = charset.encode(c);
    System.out.println(new String(b.array()));
    SocketChannel sc = s.accept();
    //sc.configureBlocking(true);
    b.compact();
    System.out.println(b.capacity() + " "+ b.position() + " " + b.limit());
    b.flip();
    System.out.println(b.capacity() + " "+ b.position() + " " + b.limit());
    int a = 0;
    while (b.hasRemaining())
    {
        a += sc.write(b);
    }

    sc.close();
    s.close();
    System.out.println("wrote " + a);
    }
} 

同样,在客户端你的代码应该是这样的:

import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class JavaApplication11 {
    public static void main(String[] args) throws Exception {
    Charset charset = Charset.forName("ISO-8859-1");
    SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 1024));
    sc.configureBlocking(true);
    ByteBuffer b = ByteBuffer.allocate(32);
    //b.flip();//Don't flip the ByteBuffer here because it sets the position to 0 and limit to 0 also. Hence no read.
    int a = sc.read(b);
    sc.close();
    b.flip();//sets the Position to 0 and limit to the number of bytes to be read.
    CharBuffer c = charset.decode(b);
    //c.flip();//Don't flip the ChharBuffer. Because it is setting the position to zero and limit to previous position i.e zero
    System.out.println("Got " + c);
    System.out.println("read " + a );
    }
}

Note:I have put comment on lines where you made mistakes.

关于java - 通过 java.nio channel 发送字符串无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15075354/

相关文章:

c# - File.ReadAllLines 将可执行路径添加到提供的路径之前

java - android studio 中的 build.gradle 依赖

c++ - 简单的UDP套接字代码,发送和接收消息

c# - 套接字消息头构建

c++ - 视频文件读写

c - C 中文件 I/O 的一些最佳实践是什么?

java - Spring Redis - 从 application.properties 文件中读取配置

java - 如何在 REST Web 服务服务器端接收 "Accept" header

java - 输出为空,而其他则不为空

java - 服务器端无法接收数据(Java Socket编程)