Java NIO SocketChannel
- Opening a SocketChannel
- Closing a SocketChannel
- Reading from a SocketChannel
- Writing to a SocketChannel
- Non-blocking Mode
A Java NIO SocketChannel is a channel that is connected to a TCP network socket. It is Java NIO's equivalent of Java Networking's Sockets. There are two ways a SocketChannel
can be created:
- You open a
SocketChannel
and connect to a server somewhere on the internet. - A
SocketChannel
can be created when an incoming connection arrives at aServerSocketChannel
.
Opening a SocketChannel
Here is how you open a SocketChannel
:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80)); // http:// 需要去掉
Closing a SocketChannel
You close a SocketChannel
after use by calling the SocketChannel.close()
method. Here is how that is done:
socketChannel.close();
Reading from a SocketChannel
To read data from a SocketChannel
you call one of the read()
methods. Here is an example:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
First a Buffer
is allocated. The data read from the SocketChannel
is read into the Buffer
.
Second the SocketChannel.read()
method is called. This method reads data from the SocketChannel
into the Buffer
. The int
returned by the read()
method tells how many bytes were witten into the Buffer
. If -1 is returned, the end-of-stream is reached (the connection is closed).
每次读取buffer 最大读到buffer.capacity的数据,int值返回的是读到的实际字节数。
如果返回的是-1,那么说明到了channel的尾部,连接已关闭
Writing to a SocketChannel
Writing data to a SocketChannel
is done using the SocketChannel.write()
method, which takes a Buffer
as parameter. Here is an example:
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
Notice how the SocketChannel.write()
method is called inside a while-loop. There is no guarantee of how many bytes the write()
method writes to the SocketChannel
. Therefore we repeat the write()
call until the Buffer
has no further bytes to write.
Non-blocking Mode
You can set a SocketChannel
into non-blocking mode. When you do so, you can call connect()
, read()
and write()
in asynchronous mode.
connect()
If the SocketChannel
is in non-blocking mode, and you call connect()
, the method may return before a connection is established. To determine whether the connection is established, you can call the finishConnect()
method, like this:
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
while(! socketChannel.finishConnect() ){
//wait, or do something else...
}
write()
In non-blocking mode the write()
method may return without having written anything. Therefore you need to call the write()
method in a loop. But, since this is already being done in the previous write examples, no need to do anything differently here.
read()
In non-blocking mode the read()
method may return without having read any data at all. Therefore you need to pay attention to the returned int
, which tells how many bytes were read.
Non-blocking Mode with Selectors
The non-blocking mode of SocketChannel
's works much better with Selector
's. By registering one or more SocketChannel
's with a Selector
, you can ask the Selector
for channels that are ready for reading, writing etc. How to use Selector
's with SocketChannel
's is explained in more detail in a later text in this tutorial.
写了个大致的代码,不过无法从远程server中读取到数据:
{
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open(); // 创建
socketChannel.configureBlocking(false); // 异步
socketChannel.connect(new InetSocketAddress("www.vip.com", 80)); // 连接
// socketChannel.connect(new InetSocketAddress(9999));就可以连接到下一章节的ServerSocketChannel,并且做信息交换
System.out.println(socketChannel.isConnected());
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT); // 注册到Selector
// 循环处理
while (true) {
int count = selector.select(2000); // select,判断有多少个readyChannel
System.out.println("count=" + count);
if (0 == count) continue;
Set<SelectionKey> selectionKeys = selector.selectedKeys(); // 获取SelectionKey
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) { // 遍历
SelectionKey key = keyIterator.next();
if (key.isConnectable()) {
SocketChannel socketChannel1 = (SocketChannel) key.channel(); // 获取socketChannel
System.out.println(socketChannel.equals(socketChannel1)); // true 同一个socketChannel
System.out.println(socketChannel1.isConnected());
// 从socketChannel中读取数据
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
int readBytesSize = 0;
// 如果不用finishConnect,这里会报异常 Exception in thread "main" java.nio.channels.NotYetConnectedException
// 但是这样总是取不出来数据,不确定是不是server的问题
while (socketChannel1.finishConnect()) {
readBytesSize = socketChannel1.read(byteBuffer);
while (readBytesSize != -1) { // 读取的数据放入byteBuffer中
System.out.println(readBytesSize);
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
System.out.print((char) byteBuffer.get());
}
byteBuffer.clear();
// 写数据进去
String newData = "New String to write to file..." + System.currentTimeMillis();
byteBuffer.put(newData.getBytes());
byteBuffer.flip();
while(byteBuffer.hasRemaining()) {
socketChannel1.write(byteBuffer);
}
}
}
socketChannel1.close(); // 处理结束后 close
}
keyIterator.remove();
}
}
}