标签:
了解Socket看这里:Socket是什么
了解 SocketChannel看这里:Socket、SocketChannel有什么区别
使用SocketChannel的最大好处就是可以进行非阻塞IO,每次链接后都会直接返回,不会阻塞线程。将需要多个线程的任务通过几个线程就能完成,降低了了性能消耗。
了解阻塞、非阻塞看这里:阻塞、非阻塞有什么区别
要编写SocketChannel,需要了解java.nio包中如下几个类:
1. ServerSocketChannel
ServerSocket的替代类, 支持阻塞通信与非阻塞通信。
SocketChannel
Socket的替代类, 支持阻塞通信与非阻塞通信。
Selector
为ServerSocketChannel监控接收客户端连接就绪事件, 为SocketChannel监控连接服务器读就绪和写就绪事件。
SelectionKey
代表ServerSocketChannel及SocketChannel向Selector注册事件的句柄。当一个 SelectionKey对象位于Selector对象的selected-keys集合中时,就表示与这个SelectionKey对象相关的事件发生了。
我们的通过客户端和服务端两部分代码来介绍。
服务端代码:
public class SocketChannelServer {
private int port = 8000;// 端口
public SocketChannelServer() throws Exception {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 设置为非阻塞方式,如果为true 那么就为传统的阻塞方式
serverChannel.socket().bind(new InetSocketAddress(port)); // 绑定IP 及 端口
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册
while (true) {
System.out.println("Waiting accept!");
Thread.sleep(1000);
selector.select();// 刚启动时,没有客户端连接时,会堵塞在这里
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();// 为了防止重复迭代
if (key.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();// 新的连接
System.out.println("Client accept!" + socketChannel);
socketChannel.configureBlocking(false);
// socketChannel.register(selector,
// SelectionKey.OP_WRITE);// 注册write
socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));// 注册read
} else if (key.isWritable()) {// 写入
SocketChannel socketChannel = (SocketChannel) key.channel();// 获得与客户端通信的信道
String sendMsg = "hello world!";
ByteBuffer writeBuffer = ByteBuffer.wrap(sendMsg.getBytes());
System.out.println("server send msg===" + sendMsg);
socketChannel.write(writeBuffer);
key.cancel();
} else if (key.isReadable()) {// 读取
SocketChannel socketChannel = (SocketChannel) key.channel();// 获得与客户端通信的信道
ByteBuffer readbuffer = (ByteBuffer) key.attachment();// 得到并清空缓冲区
readbuffer.clear();
long bytesRead = socketChannel.read(readbuffer); // 读取信息获得读取的字节数
if (bytesRead != -1) {
readbuffer.flip();// 准备读取
String receiveMsg = "";// 将字节转化为字符串
while (readbuffer.hasRemaining()) {
receiveMsg += String.valueOf((char) readbuffer.get());
}
??????Thread.sleep(5000);// 服务端等待5秒再打印,但是客户端不会等待
System.out.println("server receive msg===" + receiveMsg);
}
key.cancel();
}
}
}
}
public static void main(String[] args) throws Exception {
new SocketChannelServer();
}
}
运行服务端代码后,程序会进行监听,直到接收到客户端请求为止。结果如下:
waitting connet…
客户端代码:
public class SocketChannelClient {
private String host = "127.0.0.1";// 要发送给服务端的ip
private int port = 8000;// 要发送给服务端的端口
public SocketChannelClient() throws IOException {
SocketChannel sc = SocketChannel.open(new InetSocketAddress(host, port));// 打开一个SocketChannel并连接到服务器
sc.configureBlocking(false);
// 从server接受消息
ByteBuffer readbuffer = ByteBuffer.allocate(20);
sc.read(readbuffer);
readbuffer.flip();// 准备读取
String receiveMsg = "";// 将字节转化为字符串
while (readbuffer.hasRemaining()) {
receiveMsg += String.valueOf((char) readbuffer.get());
}
System.out.println("client receive msg===" + receiveMsg);
// 发送消息给server
String sendMsg = "I am a coder.";
ByteBuffer writeBuffer = ByteBuffer.wrap(sendMsg.getBytes());
System.out.println("client send msg===" + sendMsg);
sc.write(writeBuffer);
sc.close();
}
public static void main(String[] args) throws IOException {
new SocketChannelClient();
}
}
我们客户端做了2件事情,一是接受服务端消息,一是给服务端发送消息。由于我们要测试多个客户端连接同一个服务端,所以我们需要多次运行客户端代码。这里我们运行两次之后(称为客户端1、客户端2),查看服务端的Console,未超过5秒时会出现以下结果,说明已经连接成功。这里需要注意,最后一句再次打印了Waiting accept!,说明并没有一直等待客户端发送请求,而是继续监听请求,即没有被阻塞:
Waiting accept!
Client accept!java.nio.channels.SocketChannel[connected local=/127.0.0.1:8000 remote=/127.0.0.1:59481]
Waiting accept!
这时,每个客户端Console如下,成功接收到服务端的消息,并发出给服务端的消息,之后便立刻释放了线程,并没有一直等待服务端的执行:
client receive msg===
client send msg===I am a coder.
再回到服务端Console,5秒后,会打印我们收到的请求:
Waiting accept!
Client accept!java.nio.channels.SocketChannel[connected local=/127.0.0.1:8000 remote=/127.0.0.1:59481]
Waiting accept!
server receive msg===I am a coder.
Waiting accept!
Client accept!java.nio.channels.SocketChannel[connected local=/127.0.0.1:8000 remote=/127.0.0.1:59485]
Waiting accept!
server receive msg===I am a coder.
Waiting accept!
这里我们可以看到处理了2个客户端的请求。在服务端代码中,我们可以注册write,达到向客户端写数据的需求。
Java千百问_02基本使用(012)_如何编写非阻塞SocketChannel程序
标签:
原文地址:http://blog.csdn.net/ooppookid/article/details/51813796