您的位置:首页 > 职场人生

黑马程序员_O‘Reilly java nio学习笔记之通道_socket通道

2012-04-24 22:09 507 查看
---------------------- android培训java培训、期待与您交流! ----------------------

6.socket通道

新的 socket通道类可以运行非阻塞模式并且是可选择的。本节中我们会看到,再也没有为每个socket连接使用一个线程的必要了,也避免了管理大量线程所需的上下文交换总开销。借助新的NIO 类,一个或几个线程就可以管理成百上千的活动socket连接了并且只有很少甚至可能没有性能损失。注意 DatagramChannel和SocketChannel实现定义读和写功能的接口而ServerSocketChannel不实现。ServerSocketChannel负责监听传入的连接和创建新的SocketChannel对象,它本身从不传输数据。

每个socket通道(在java.nio.channels包中)都有一个关联的java.net socket对象。但是,并非所有的socket都有一个关联的通道。如果您用传统方式(直接实例化)创建了一个Socket对象,它就不会有关联的SocketChannel并且它的getChannel( )方法将总是返null。

6.1 非阻塞模式

Socket通道可以在非阻塞模式下运行。要把一个socket通道置于非阻塞模式,这要依靠所有socket通道类的公有超级类:SelectableChannel。下面的方法就是关于通道的阻塞模式的:

abstract Object blockingLock()

获取其 configureBlocking 和 register 方法实现同步的对象。

abstract SelectableChannel configureBlocking(boolean block)

设置或重新设置一个通道的阻塞模式,参数值为true 则设为阻塞模式,参数

值为false 值设为非阻塞模式。

abstract boolean isBlocking()

判断此通道上的每个 I/O 操作在完成前是否被阻塞。

以上只是部分API,其余的将在选择器中讨论。

如果要防止socket通道的阻塞模式被更改,可以用blockingLock( )方法,该方法会返回一个非透明的对象引用。示例:

Socket socket = null;

Object lockObj = serverChannel.blockingLock( );

synchronize (lockObj) {

boolean prevState = serverChannel.isBlocking( );

serverChannel.configureBlocking (false);

socket = serverChannel.accept( );

serverChannel.configureBlocking (prevState);

}

if (socket != null) {

doSomethingWithTheSocket (socket);

}

6.2 ServerSocketChannel

以下为ServerSocketChannel的方法:

abstract SocketChannel accept() 接受到此通道套接字的连接。

static ServerSocketChannel open() 创建通道

abstract ServerSocket socket() 获取与此通道关联的未绑定的服务器套接字。

int validOps() 返回一个操作集,标识此通道所支持的操作。

方法使用示例:

ServerSocketChannel ssc = ServerSocketChannel.open( );

ServerSocket serverSocket = ssc.socket( );

serverSocket.bind (new InetSocketAddress (1234));

以上代码创建了一个通道,与之关联的服务器socket监听在1234端口。

ServerSocketChannel也有accept( )方法。如果选择在ServerSocket上调用accept()方法,那么它会同任何其他的ServerSocket表现一样的行为:总是阻塞并返回一个java.net.Socket对象。如果您选择在ServerSocketChannel上调用accept()方法则会返回SocketChannel类型的对象,返回的对象能够在非阻塞模式下运行。如果以非阻塞模式被调用,当没有传入连接在等待时,ServerSocketChannel.accept( )会立即返回null。

6.3 SocketChannel

以下为SocketChannel的方法:

abstract boolean connect(SocketAddress remote) 将通道连接到指定的套接字。

abstract boolean finishConnect() 完成套接字通道的连接过程。

abstract boolean isConnected() 判断此通道是否已连接到指定的网络套接字。

abstract boolean isConnectionPending() 判断此通道上是否正在进行连接操作。

static SocketChannel open() 打开套接字通道。

static SocketChannel open(SocketAddress remote) 打开套接字通道并将其连接到远程地址。

abstract int read(ByteBuffer dst) 将字节序列从此通道中读入给定的缓冲区。

long read(ByteBuffer[] dsts) 将字节序列从此通道读入给定的缓冲区。

abstract long read(ByteBuffer[] dsts, int offset, int length)

将字节序列从此通道读入给定缓冲区的子序列中。

abstract Socket socket() 获取与此通道关联的套接字。

int validOps() 返回一个操作集,标识此通道所支持的操作。

abstract int write(ByteBuffer src) 将字节序列从给定的缓冲区中写入此通道。

long write(ByteBuffer[] srcs) 将字节序列从给定的缓冲区写入此通道。

abstract long write(ByteBuffer[] srcs, int offset, int length)

将字节序列从给定缓冲区的子序列写入此通道。

虽然每个 SocketChannel对象都会创建一个对等的Socket对象,反过来却不成立。直接创建的Socket对象不会关联SocketChannel对象,它们的getChannel( )方法只返回null。新创建的 SocketChannel虽已打开却是未连接的。在一个未连接的SocketChannel对象上尝试个I/O 操作会导致NotYetConnectedException异常。创建通道并连接:

SocketChannel socketChannel =

SocketChannel.open (new InetSocketAddress ("somehost", somePort));

等价于下面这段代码:

SocketChannel socketChannel = SocketChannel.open( );

socketChannel.connect (new InetSocketAddress ("somehost", somePort));

如果选择使用传统方式进行连接——通过在对等Socket对象上调用connect()方法,那么传

统的连接语义将适用于此,线程在连接建立好或超时过期之前都将保持阻塞。如果您选择通过在通道上直接调用connect()方法来建立连接(通道默认为阻塞模式),那么通道可用于阻塞模式。

在SocketChannel上并没有一种connect( )方法可以让您指定超时(timeout)值,当connect( )方法在非阻塞模式下被调用时SocketChannel提供连接时,它发起对请求地址的连接并且立即返回

值。如果返回值是true,说明连接立即建立了(这可能是本地环回连接);如果连接不能立即建立,connect()方法会返回false 且继续连接建立过程。 并且必须在以后通过调用 finishConnect 方法来完成该连接操作。

在一个非阻塞模式的SocketChannel对象上调用finishConnect( )方法,将可能出现下列情形之一:

l connect( )方法尚未被调用。那么将产生NoConnectionPendingException异常。

l 连接建立过程正在进行,尚未完成,那么,finishConnect( )方法会立即返回false 值。

l 在非阻塞模式下调用 connect( )方法之后,SocketChannel 又被切换回了阻塞模式。那么如果有必要的话,调用线程会阻塞直到连接建立完成,finishConnect( )方法接着就会返回true值。

l 连接已经建立。那么什么都不会发生,finishConnect( )方法会返回true 值。

如果此通道处于阻塞模式,则在连接完成或失败之前将阻塞此方法,并且总是返回 true 或抛出描述该失败的、经过检查的异常。可在任意时间调用此方法。如果正在调用此方法时在此通道上正在调用读取或写入操作,则在此调用完成前将首先阻塞读写操作,即使是在非阻塞模式下。

Socket通道是线程安全的。并发访问时无需特别措施来保护发起访问的多个线程,不过任何时候都只有一个读操作和一个写操作在进行中。请记住,sockets是面向流的而非包导向的。某个发送器可能给一个socket写入了20个字节而***调用read( )方法时却只收到了其中的3个字节。剩下的17个字节还是传输中,也可能一个字节都没读取到,在下面的示例中你将会看到。由于这个原因,让多个不配合的线程共享某个流socket的同一侧绝非一个好的设计选择。

ServerSocketChannel和SocketChannel示例:

服务器端:

public class Channel_Server {

public static final String GREETING = "Hello,client!oh...I love you!\r\n";

public static void main(String[] argv) throws Exception {

int port = 1234;

ByteBuffer sendbuffer = ByteBuffer.wrap(GREETING.getBytes());

ByteBuffer recbuffer = ByteBuffer.allocate(1024);

ServerSocketChannel ssc = ServerSocketChannel.open();

ssc.socket().bind(new InetSocketAddress(port));

ssc.configureBlocking(false);

while (true) {

SocketChannel sc = ssc.accept();

if (sc == null) {

System.out.println("没有连接,休息两秒后重新尝试连接!");

Thread.sleep(2000);

} else {

sc.read(recbuffer);

recbuffer.flip();

System.out.println(sc.socket().getRemoteSocketAddress()+

" says:"+new String(recbuffer.array()));

sc.write(sendbuffer);

sendbuffer.clear();

sc.close();

}

}

}

}

客户端:

public class Channel_Client {

public static void main(String[] argv) throws Exception {

String host = "localhost";

String greet="Hello,server!";

int port = 1234;

ByteBuffer sendbuffer = ByteBuffer.wrap(greet.getBytes());

ByteBuffer recbuffer = ByteBuffer.allocate(1024);

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.write(sendbuffer);

//保证客户端尽可能的接受到数据

Thread.sleep(1000);

sc.read(recbuffer);

recbuffer.flip();

System.out.println(sc.socket().getRemoteSocketAddress()+

" says:"+new String(recbuffer.array()));

sc.close();

}

}

以上程序客户端和服务器端进行一次简单的通讯。由于是非阻塞模式,客户端可能接受不到数据,多运行几次客户端可以看到这种情况。当将客户端和服务器端改为阻塞模式时,和传统C/S一样。

---------------------- android培训java培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net/heima
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: