package
com;
import
java.io.FileInputStream;
import
java.io.IOException;
import
java.nio.ByteBuffer;
import
java.nio.channels.FileChannel;
public
class
Test{
public
static
void
main (String[]argv)
throws
IOException{
FileInputStream aFile =
new
FileInputStream(
"/Users/wudiyong/xxx"
);
FileChannel inChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(
48
);
int
bytesRead = inChannel.read(buf);
while
(bytesRead != -
1
) {
System.out.println(
"Read "
+ bytesRead);
buf.flip();
while
(buf.hasRemaining()){
System.out.print((
char
) buf.get());
}
buf.clear();
bytesRead = inChannel.read(buf);
}
aFile.close();
}
}
import
java.io.IOException;
import
java.nio.ByteBuffer;
import
java.nio.channels.Channels;
import
java.nio.channels.ReadableByteChannel;
import
java.nio.channels.WritableByteChannel;
public
class
Test {
public
static
void
main(String[] agrs)
throws
IOException {
ReadableByteChannel source = Channels.newChannel(System.in);
WritableByteChannel dest = Channels.newChannel(System.out);
// channelCopy1(source, dest);//方式一
channelCopy2(source, dest);
//方式二
source.close();
dest.close();
}
private
static
void
channelCopy1(ReadableByteChannel src,WritableByteChannel dest)
throws
IOException {
ByteBuffer buffer = ByteBuffer.allocateDirect(
16
*
10
);
while
(src.read(buffer) != -
1
) {
buffer.flip();
dest.write(buffer);
/*
* 因为可能不能够一次把buffer全部输出,此时buffer里还有剩余的数据,需要用compact()把
* 这些数据往前移,新的数据才能从后面写入,如果一次就能完全输出,则compact的作用相当于clear
*/
buffer.compact();
}
//可能buffer里还有数据,把剩余的数据全部输出
buffer.flip();
while
(buffer.hasRemaining()) {
dest.write(buffer);
}
}
private
static
void
channelCopy2(ReadableByteChannel src,WritableByteChannel dest)
throws
IOException{
ByteBuffer buffer = ByteBuffer.allocateDirect (
16
*
10
);
while
(src.read (buffer) != -
1
) {
buffer.flip();
//循环把buffer里的所有数据都输出,再接收新的数据
while
(buffer.hasRemaining()){
dest.write (buffer);
}
buffer.clear( );
}
}
}
public
class
Test{
private
static
final
String DEMOGRAPHIC =
"blahblah.txt"
;
public
static
void
main(String[] argv)
throws
Exception {
FileOutputStream fos =
new
FileOutputStream(DEMOGRAPHIC);
GatheringByteChannel gatherChannel = fos.getChannel();
ByteBuffer[] bs = utterBS();
while
(gatherChannel.write(bs) >
0
);
//gatherChannel
fos.close();
}
private
static
String [] col1 = {
"Aggregate"
,
"Enable"
,
"Leverage"
,
"Facilitate"
,
"Synergize"
,
"Repurpose"
,
"Strategize"
,
"Reinvent"
,
"Harness"
};
private
static
String [] col2 = {
"cross-platform"
,
"best-of-breed"
,
"frictionless"
,
"ubiquitous"
,
"extensible"
,
"compelling"
,
"mission-critical"
,
"collaborative"
,
"integrated"
};
private
static
String [] col3 = {
"methodologies"
,
"infomediaries"
,
"platforms"
,
"schemas"
,
"mindshare"
,
"paradigms"
,
"functionalities"
,
"web services"
,
"infrastructures"
};
private
static
String newline = System.getProperty (
"line.separator"
);
private
static
ByteBuffer [] utterBS ()
throws
Exception{
List<ByteBuffer> list =
new
LinkedList<ByteBuffer>( );
list.add (pickRandom (col1,
" "
));
list.add (pickRandom (col2,
" "
));
list.add (pickRandom (col3, newline));
ByteBuffer [] bufs =
new
ByteBuffer [list.size( )];
list.toArray (bufs);
return
(bufs);
}
private
static
Random rand =
new
Random();
private
static
ByteBuffer pickRandom (String [] strings, String suffix)
throws
Exception{
String string = strings [rand.nextInt (strings.length)];
int
total = string.length() + suffix.length( );
ByteBuffer buf = ByteBuffer.allocate (total);
buf.put (string.getBytes (
"US-ASCII"
));
buf.put (suffix.getBytes (
"US-ASCII"
));
buf.flip( );
return
(buf);
}
}
文件通道
Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。
打开FileChannel
在使用FileChannel之前,必须先打开它。但是,我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。下面是通过RandomAccessFile打开FileChannel的示例:
1 | RandomAccessFile aFile = new RandomAccessFile( "data/nio-data.txt" , "rw" ); |
2 | FileChannel inChannel = aFile.getChannel(); |
从FileChannel读取数据
调用多个read()方法之一从FileChannel中读取数据。如:
1 | ByteBuffer buf = ByteBuffer.allocate(48); |
2 | int bytesRead = inChannel.read(buf); |
首先,分配一个Buffer。从FileChannel中读取的数据将被读到Buffer中。然后,调用FileChannel.read()方法。该方法将数据从FileChannel读取到Buffer中。read()方法返回的int值表示了有多少字节被读到了Buffer中。如果返回-1,表示到了文件末尾。
向FileChannel写数据
使用FileChannel.write()方法向FileChannel写数据,该方法的参数是一个Buffer。如:
01 | String newData = "New String to write to file..." + System.currentTimeMillis(); |
02 |
|
03 | ByteBuffer buf = ByteBuffer.allocate(48); |
04 | buf.clear(); |
05 | buf.put(newData.getBytes()); |
06 |
|
07 | buf.flip(); |
08 |
|
09 | while(buf.hasRemaining()) { |
10 | channel.write(buf); |
11 | } |
注意FileChannel.write()是在while循环中调用的。因为无法保证write()方法一次能向FileChannel写入多少字节,因此需要重复调用write()方法,直到Buffer中已经没有尚未写入通道的字节。
关闭FileChannel
用完FileChannel后必须将其关闭。如:
1 | channel.close(); |
FileChannel的position方法
有时可能需要在FileChannel的某个特定位置进行数据的读/写操作。可以通过调用position()方法获取FileChannel的当前位置。
也可以通过调用position(long pos)方法设置FileChannel的当前位置。
1 | long pos = channel.position(); |
2 | channel.position(pos + 123 ); |
如果将位置设置在文件结束符之后,然后试图从文件通道中读取数据,读方法将返回-1 —— 文件结束标志。
如果将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置并写入数据。这可能导致“文件空洞”,磁盘上物理文件中写入的数据间有空隙。
FileChannel的size方法
FileChannel实例的size()方法将返回该实例所关联文件的大小。如:
1 | long fileSize = channel.size(); |
FileChannel的truncate方法
可以使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将指定长度后面的部分删除。如:
channel.truncate(
1024
);
这个例子截取文件的前1024个字节。
FileChannel的force方法
FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到FileChannel里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。
force()方法有一个boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。
下面的例子同时将文件数据和元数据强制写到磁盘上:
channel.force(
true
);
内存映射文件
public
class
MappedHttp {
private
static
final
String OUTPUT_FILE =
"MappedHttp.out"
;
private
static
final
String LINE_SEP =
"\r\n"
;
private
static
final
String SERVER_ID =
"Server: Ronsoft Dummy Server"
;
private
static
final
String HTTP_HDR =
"HTTP/1.0 200 OK"
+ LINE_SEP
+ SERVER_ID + LINE_SEP;
private
static
final
String HTTP_404_HDR =
"HTTP/1.0 404 Not Found"
+ LINE_SEP + SERVER_ID + LINE_SEP;
private
static
final
String MSG_404 =
"Could not open file: "
;
public
static
void
main(String[] argv)
throws
Exception {
String file =
"C:\\Users\\Administrator\\Desktop\\aa.TXT"
;
ByteBuffer header = ByteBuffer.wrap(HTTP_HDR.getBytes(
"US-ASCII"
));
ByteBuffer dynhdrs = ByteBuffer.allocate(
128
);
ByteBuffer[] gather = { header, dynhdrs,
null
};
String contentType =
"unknown/unknown"
;
long
contentLength = -
1
;
try
{
FileInputStream fis =
new
FileInputStream(file);
FileChannel fc = fis.getChannel();
MappedByteBuffer filedata = fc.map(MapMode.READ_ONLY,
0
, fc.size());
gather[
2
] = filedata;
contentLength = fc.size();
contentType = URLConnection.guessContentTypeFromName(file);
}
catch
(IOException e) {
ByteBuffer buf = ByteBuffer.allocate(
128
);
String msg = MSG_404 + e + LINE_SEP;
buf.put(msg.getBytes(
"US-ASCII"
));
buf.flip();
gather[
0
] = ByteBuffer.wrap(HTTP_404_HDR.getBytes(
"US-ASCII"
));
gather[
2
] = buf;
contentLength = msg.length();
contentType =
"text/plain"
;
}
StringBuffer sb =
new
StringBuffer();
sb.append(
"Content-Length: "
+ contentLength);
sb.append(LINE_SEP);
sb.append(
"Content-Type: "
).append(contentType);
sb.append(LINE_SEP).append(LINE_SEP);
dynhdrs.put(sb.toString().getBytes(
"US-ASCII"
));
dynhdrs.flip();
FileOutputStream fos =
new
FileOutputStream(OUTPUT_FILE);
FileChannel out = fos.getChannel();
while
(out.write(gather) >
0
);
out.close();
}
}
Socket通道
ServerSocketChannel ssc = ServerSocketChannel.open( );
ServerSocket serverSocket = ssc.socket( );
// Listen on port 1234
serverSocket.bind (
new
InetSocketAddress (
1234
));
同它的对等体 java.net.ServerSocket 一样,ServerSocketChannel 也有 accept( )方法。一旦您创建 了一个 ServerSocketChannel 并用对等 socket 绑定了它,然后您就可以在其中一个上调用 accept( )。 如果您选择在 ServerSocket 上调用 accept( )方法,那么它会同任何其他的 ServerSocket 表现一样的行为:总是阻塞并返回一个 java.net.Socket 对象。如果您选择在 ServerSocketChannel 上调用 accept( ) 方法则会返回 SocketChannel 类型的对象,返回的对象能够在非阻塞模式下运行。
如果以非阻塞模式被调用,当没有传入连接在等待时,ServerSocketChannel.accept( )会立即返 回 null。正是这种检查连接而不阻塞的能力实现了可伸缩性并低了复杂性。可选择性也因此得到实现。我们可以使用一个选择器实例来注册一个 ServerSocketChannel 对象以实现新连接到达时自 动通知的功能。
public
class
ChannelAccept {
public
static
final
String GREETING =
"Hello I must be going.\r\n"
;
public
static
void
main(String[] argv)
throws
Exception {
int
port =
1234
;
/*
* The new buffer will be backed by the given byte array; that is, modifications to
* the buffer will cause the array to be modified and vice versa. The new buffer's
* capacity and limit will be array.length, its position will be zero, and its mark will be undefined.
*/
ByteBuffer buffer = ByteBuffer.wrap(GREETING.getBytes());
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(
new
InetSocketAddress(port));
ssc.configureBlocking(
false
);
while
(
true
) {
System.out.println(
"Waiting for connections"
);
SocketChannel sc = ssc.accept();
if
(sc ==
null
) {
Thread.sleep(
2000
);
}
else
{
System.out.println(
"Incoming connection from: "
+ sc.socket().getRemoteSocketAddress());
buffer.rewind();
//应该也可以略
sc.write(buffer);
sc.close();
}
}
}
}
SocketChannel socketChannel =
SocketChannel.open (
new
InetSocketAddress (
"somehost"
, somePort));
SocketChannel socketChannel = SocketChannel.open( );
socketChannel.connect (
new
InetSocketAddress (
"somehost"
, somePort));
public
class
ConnectAsync {
public
static
void
main(String[] argv)
throws
Exception {
String host =
"localhost"
;
int
port =
1234
;
InetSocketAddress addr =
new
InetSocketAddress(host, port);
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(
false
);
System.out.println(
"initiating connection"
);
sc.connect(addr);
while
(!sc.finishConnect()) {
System.out.println(
"还没连接成功,可以做其它事情。。。"
);
}
System.out.println(
"connection established"
);
sc.close();
}
}
public
class
TimeServer {
private
static
final
int
DEFAULT_TIME_PORT =
12345
;
private
static
final
long
DIFF_1900 = 2208988800L;
protected
DatagramChannel channel;
public
TimeServer()
throws
Exception {
this
.channel = DatagramChannel.open();
this
.channel.socket().bind(
new
InetSocketAddress(DEFAULT_TIME_PORT));
System.out.println(
"Listening on port "
+ DEFAULT_TIME_PORT +
" for time requests"
);
}
public
void
listen()
throws
Exception {
// Allocate a buffer to hold a long value
ByteBuffer longBuffer = ByteBuffer.allocate(
8
);
// Assure big-endian (network) byte order
longBuffer.order(ByteOrder.BIG_ENDIAN);
// Zero the whole buffer to be sure
longBuffer.putLong(
0
,
0
);
// Position to first byte of the low-order 32 bits
longBuffer.position(
4
);
// Slice the buffer; gives view of the low-order 32 bits
ByteBuffer buffer = longBuffer.slice();
while
(
true
) {
buffer.clear();
SocketAddress sa =
this
.channel.receive(buffer);
if
(sa ==
null
) {
continue
;
}
System.out.println(
"Time request from "
+ sa);
buffer.clear();
// Set 64-bit value; slice buffer sees low 32 bits
longBuffer.putLong(
0
, (System.currentTimeMillis() /
1000
) + DIFF_1900);
this
.channel.send(buffer, sa);
// 只把低32位返回
}
}
public
static
void
main(String[] argv)
throws
Exception {
try
{
TimeServer server =
new
TimeServer();
server.listen();
}
catch
(SocketException e) {
System.out.println(
"Can't bind to port "
+ DEFAULT_TIME_PORT +
", try a different one"
);
}
}
}
public
class
TimeClient {
private
static
final
int
DEFAULT_TIME_PORT =
12345
;
private
static
final
long
DIFF_1900 = 2208988800L;
protected
List remoteHosts =
new
ArrayList();
protected
DatagramChannel channel;
public
TimeClient()
throws
Exception {
setServers();
this
.channel = DatagramChannel.open();
}
protected
InetSocketAddress receivePacket(DatagramChannel channel,ByteBuffer buffer)
throws
Exception {
buffer.clear();
// Receive an unsigned 32-bit, big-endian value
return
((InetSocketAddress) channel.receive(buffer));
}
// Send time requests to all the supplied hosts
protected
void
sendRequests()
throws
Exception {
ByteBuffer buffer = ByteBuffer.allocate(
1
);
Iterator it = remoteHosts.iterator();
while
(it.hasNext()) {
InetSocketAddress sa = (InetSocketAddress) it.next();
System.out.println(
"Requesting time from "
+ sa.getHostName() +
":"
+ sa.getPort());
buffer.clear().flip();
channel.send(buffer, sa);
}
}
// Receive any replies that arrive
public
void
getReplies()
throws
Exception {
// Allocate a buffer to hold a long value
ByteBuffer longBuffer = ByteBuffer.allocate(
8
);
// Assure big-endian (network) byte order
longBuffer.order(ByteOrder.BIG_ENDIAN);
// Zero the whole buffer to be sure
longBuffer.putLong(
0
,
0
);
// Position to first byte of the low-order 32 bits
longBuffer.position(
4
);
// Slice the buffer; gives view of the low-order 32 bits
ByteBuffer buffer = longBuffer.slice();
int
expect = remoteHosts.size();
int
replies =
0
;
System.out.println(
""
);
System.out.println(
"Waiting for replies..."
);
while
(
true
) {
InetSocketAddress sa;
sa = receivePacket(channel, buffer);
buffer.flip();
replies++;
printTime(longBuffer.getLong(
0
), sa);
if
(replies == expect) {
System.out.println(
"All packets answered"
);
break
;
}
// Some replies haven't shown up yet
System.out.println(
"Received "
+ replies +
" of "
+ expect +
" replies"
);
}
}
// Print info about a received time reply
protected
void
printTime(
long
remote1900, InetSocketAddress sa) {
// local time as seconds since Jan 1, 1970
long
local = System.currentTimeMillis() /
1000
;
// remote time as seconds since Jan 1, 1970
long
remote = remote1900 - DIFF_1900;
Date remoteDate =
new
Date(remote *
1000
);
Date localDate =
new
Date(local *
1000
);
long
skew = remote - local;
System.out.println(
"Reply from "
+ sa.getHostName() +
":"
+ sa.getPort());
System.out.println(
" there: "
+ remoteDate);
System.out.println(
" here: "
+ localDate);
System.out.print(
" skew: "
);
if
(skew ==
0
) {
System.out.println(
"none"
);
}
else
if
(skew >
0
) {
System.out.println(skew +
" seconds ahead"
);
}
else
{
System.out.println((-skew) +
" seconds behind"
);
}
}
protected
void
setServers() {
/*
* 设置把请求发给哪些服务器,这里指定的地址和端口号是服务器的地址和端口号,
* 如果要指定自己的端口号,则应该在DatagramChannel对象中调用bind绑定。
* 可以指定多个,这里只指定两个,而且这两个指向的还是同一个服务器。
*/
InetSocketAddress sa1 =
new
InetSocketAddress(
"localhost"
, DEFAULT_TIME_PORT);
InetSocketAddress sa2 =
new
InetSocketAddress(
"localhost"
, DEFAULT_TIME_PORT);
remoteHosts.add(sa1);
remoteHosts.add(sa2);
}
public
static
void
main(String[] argv)
throws
Exception {
TimeClient client =
new
TimeClient();
client.sendRequests();
client.getReplies();
}
}