使用NIO实现非阻塞Socket通信
2012-03-07 20:39
302 查看
服务器端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
public class ServerNio {
public static void main(String[] args) throws IOException{
//打开一个多路复用器对象,作用是监控多个SelectableChannel的IO状况。
Selector selector = Selector.open();
//使用open()方法打开一个未绑定的服务器插口通道,支持非阻塞操作
ServerSocketChannel serverChannel = ServerSocketChannel.open();
//将服务器插口绑定到指定ip和端口号
serverChannel.socket().bind( new InetSocketAddress("127.0.0.1",55555));
//调整服务器插口通道的阻塞模式为非阻塞模式
serverChannel.configureBlocking(false);
//将服务器插口通道注册到多路复用器(selector选择容器中)
//SelectionKey表示注册通道时产生的选择键
serverChannel.register(selector , SelectionKey.OP_ACCEPT);
//创建一个1字节的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//指定编码集
Charset charset = Charset.forName("UTF-8");
//服务器不断地调用select()方法,若大于0表示有多少个Channel通道具有可用的I/O操作
while(selector.select() > 0){
//遍历已经注册到selector容器的通道
for(SelectionKey sk: selector.selectedKeys()){
//把正在处理的sk从集合中移除
selector.selectedKeys().remove(sk);
//如果sk对应的通道包含客户端的连接请求
if(sk.isAcceptable()){
//返回创建此键的通道
SelectableChannel sc1 = sk.channel();
//如果这个通道是服务器插口通道类型的
if(sc1 instanceof ServerSocketChannel){
//将其强制转换成服务器插口通道类型的对象
ServerSocketChannel ssc = (ServerSocketChannel)sc1;
//调用accept()方法接受连接并返回普通插口通道类型的I/O接口
SocketChannel socketChannel1 = ssc.accept();
//设置采用非阻塞模式
socketChannel1.configureBlocking(false);
//将插口通道也注册到selector
socketChannel1.register(selector,SelectionKey.OP_READ);
//将sk对应的Channel设置成准备接受其他请求
sk.interestOps(SelectionKey.OP_ACCEPT);
}
}
//如果sk对应的通道有数据需要读取
if(sk.isReadable()){
//获取该SelectionKey对应的Channel,该Channel中有可读得数据
SelectableChannel sc2 = sk.channel();
if(sc2 instanceof SocketChannel){
SocketChannel socketChannel2 = (SocketChannel)sc2;
String msg = "";
//开始读取数据
while(socketChannel2.read(buffer)>0){
buffer.flip(); //重绕(锁定)缓冲区(pos=0,limit=pos)
msg+=charset.decode(buffer);
System.out.println(msg);
buffer.clear(); //重置缓冲区(pos=0,limit=capacity)
}
//将sk对应的Channel设置成准备下一次读取
sk.interestOps(SelectionKey.OP_READ);
//发送给客户端
//如果msg的长度大于0,即聊天信息不为空
if(msg.length()>0){
//遍历该selector里注册的所有SelectKey
for(SelectionKey key:selector.keys()){
//获取该key对应的Channel
SelectableChannel targetChannel = key.channel();
if(targetChannel instanceof SocketChannel){
//将读到的内容写入该Channel中
SocketChannel dest =(SocketChannel)targetChannel;
dest.write(charset.encode(msg));
}
}
}
}
}
}
}
}
}
复制代码
客户端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
public class ClientNio {
public static void main(String[] args) throws IOException{
//定义检测SocketChannel的Selector对象
Selector selector = Selector.open();
InetSocketAddress server = new InetSocketAddress("127.0.0.1",55555);
//调用open静态方法创建连接到指定主机的SocketChannel
SocketChannel clientChannel = SocketChannel.open(server);
//设置该客户端插口通道为非阻塞模式
clientChannel.configureBlocking(false);
//注册插口到selector容器
clientChannel.register(selector, SelectionKey.OP_READ);
//定义编码和解码的字符集
Charset charset = Charset.forName("UTF-8");
//启动一个线程用于读取服务器发送来的数据
new ClientNioThread(selector,charset);
//创建键盘输入流
Scanner scanner = new Scanner(System.in);
//向服务器发送数据
while( scanner.hasNextLine()){
//读取键盘输入
String message = scanner.nextLine();
//将键盘输入的内容输出到SocketChannel
clientChannel.write(charset.encode(message));
}
}
}
复制代码
读取服务器端数据的线程:
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
public class ClientNioThread extends Thread{
private Selector selector;
private Charset charset;
public ClientNioThread(Selector selector,Charset charset){
this.selector = selector;
this.charset = charset;
this.start(); //启动线程
}
@Override
public void run() {
try{
ByteBuffer buffer = ByteBuffer.allocate(1024);
while( selector.select() > 0 ){
//遍历每个有可用I/O操作Channel对应的SelectionKey
for( SelectionKey sk : selector.selectedKeys() ){
//删除正在处理的SelectionKey
selector.selectedKeys().remove(sk);
SelectableChannel sc = sk.channel();
if( sc instanceof SocketChannel ){
SocketChannel socketChannel = (SocketChannel) sc;
StringBuilder sb = new StringBuilder();
while( socketChannel.read( buffer ) > 0){
buffer.flip();
sb.append( charset.decode( buffer ));
buffer.clear();
}
//打印输出读取的内容
System.out.println( sb.toString());
}
//为下一次读取做准备
sk.interestOps( SelectionKey.OP_READ);
}
}
}catch(IOException e){
e.printStackTrace();
}
}
}
复制代码
总结:
服务器端:
1-获得Selector对象
2 获得ServerSocketChannel 对象serverChannel
3 把serverChannel跟指定的ip和端口(serverChannel对应的ServerSocket)
4 设置非阻塞模式
5 把serverChannel 注册到selector,指定模式(SelectionKey.OP_ACCEPT)
6 反复执行
1> select()选择可以选择的通道
2> 遍历所有的selectedKeys
isAcceptable:打开监听 SocketChannel channel = serverChannel.accept();
把通过监听获得到的channel设置为无阻塞模式
注册channel到selector上,同时指定模式(OP_READ)
为sk的interest集合插入一个值(为下次操作做准备)
sk.interestOps(SelectionKey)
sk.isReadable:根据sk获得它对应的通道:SelectableChannel sc = sk.channel();
类型检查if(sc.instanceof SocketChannel)
如果满足条件做类型转换:SocketChannel socketChannel = (SocketChannel)sc
读取数据(读取ByteBuffer)socketChannel.read(buffer);
向客户端发送:
if(msg.length()>0){
//遍历keys
for(SelectionKey key:selector keys()){
SelectableChannel selectChannel = key.channel();
if(selectChannel instance of SocketChannel){
SocketChannel sssccc = (SocketChannel)selectChannel
sssccc.write(charset.encode(msg));
}
}
}
客户端:
1 获得一个Selector对象selector;
2 获得SocketChannel同时指定要连接的服务器的InetSocketAddress对象
3 设置为非阻塞模式
4 注册SocketChannel到selector同时指定模式为SelectionKey.OP_READ
5 使用主线程向服务器发送数据
6 使用单独的一个线程读取服务器发送来的数据
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
public class ServerNio {
public static void main(String[] args) throws IOException{
//打开一个多路复用器对象,作用是监控多个SelectableChannel的IO状况。
Selector selector = Selector.open();
//使用open()方法打开一个未绑定的服务器插口通道,支持非阻塞操作
ServerSocketChannel serverChannel = ServerSocketChannel.open();
//将服务器插口绑定到指定ip和端口号
serverChannel.socket().bind( new InetSocketAddress("127.0.0.1",55555));
//调整服务器插口通道的阻塞模式为非阻塞模式
serverChannel.configureBlocking(false);
//将服务器插口通道注册到多路复用器(selector选择容器中)
//SelectionKey表示注册通道时产生的选择键
serverChannel.register(selector , SelectionKey.OP_ACCEPT);
//创建一个1字节的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//指定编码集
Charset charset = Charset.forName("UTF-8");
//服务器不断地调用select()方法,若大于0表示有多少个Channel通道具有可用的I/O操作
while(selector.select() > 0){
//遍历已经注册到selector容器的通道
for(SelectionKey sk: selector.selectedKeys()){
//把正在处理的sk从集合中移除
selector.selectedKeys().remove(sk);
//如果sk对应的通道包含客户端的连接请求
if(sk.isAcceptable()){
//返回创建此键的通道
SelectableChannel sc1 = sk.channel();
//如果这个通道是服务器插口通道类型的
if(sc1 instanceof ServerSocketChannel){
//将其强制转换成服务器插口通道类型的对象
ServerSocketChannel ssc = (ServerSocketChannel)sc1;
//调用accept()方法接受连接并返回普通插口通道类型的I/O接口
SocketChannel socketChannel1 = ssc.accept();
//设置采用非阻塞模式
socketChannel1.configureBlocking(false);
//将插口通道也注册到selector
socketChannel1.register(selector,SelectionKey.OP_READ);
//将sk对应的Channel设置成准备接受其他请求
sk.interestOps(SelectionKey.OP_ACCEPT);
}
}
//如果sk对应的通道有数据需要读取
if(sk.isReadable()){
//获取该SelectionKey对应的Channel,该Channel中有可读得数据
SelectableChannel sc2 = sk.channel();
if(sc2 instanceof SocketChannel){
SocketChannel socketChannel2 = (SocketChannel)sc2;
String msg = "";
//开始读取数据
while(socketChannel2.read(buffer)>0){
buffer.flip(); //重绕(锁定)缓冲区(pos=0,limit=pos)
msg+=charset.decode(buffer);
System.out.println(msg);
buffer.clear(); //重置缓冲区(pos=0,limit=capacity)
}
//将sk对应的Channel设置成准备下一次读取
sk.interestOps(SelectionKey.OP_READ);
//发送给客户端
//如果msg的长度大于0,即聊天信息不为空
if(msg.length()>0){
//遍历该selector里注册的所有SelectKey
for(SelectionKey key:selector.keys()){
//获取该key对应的Channel
SelectableChannel targetChannel = key.channel();
if(targetChannel instanceof SocketChannel){
//将读到的内容写入该Channel中
SocketChannel dest =(SocketChannel)targetChannel;
dest.write(charset.encode(msg));
}
}
}
}
}
}
}
}
}
复制代码
客户端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
public class ClientNio {
public static void main(String[] args) throws IOException{
//定义检测SocketChannel的Selector对象
Selector selector = Selector.open();
InetSocketAddress server = new InetSocketAddress("127.0.0.1",55555);
//调用open静态方法创建连接到指定主机的SocketChannel
SocketChannel clientChannel = SocketChannel.open(server);
//设置该客户端插口通道为非阻塞模式
clientChannel.configureBlocking(false);
//注册插口到selector容器
clientChannel.register(selector, SelectionKey.OP_READ);
//定义编码和解码的字符集
Charset charset = Charset.forName("UTF-8");
//启动一个线程用于读取服务器发送来的数据
new ClientNioThread(selector,charset);
//创建键盘输入流
Scanner scanner = new Scanner(System.in);
//向服务器发送数据
while( scanner.hasNextLine()){
//读取键盘输入
String message = scanner.nextLine();
//将键盘输入的内容输出到SocketChannel
clientChannel.write(charset.encode(message));
}
}
}
复制代码
读取服务器端数据的线程:
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
public class ClientNioThread extends Thread{
private Selector selector;
private Charset charset;
public ClientNioThread(Selector selector,Charset charset){
this.selector = selector;
this.charset = charset;
this.start(); //启动线程
}
@Override
public void run() {
try{
ByteBuffer buffer = ByteBuffer.allocate(1024);
while( selector.select() > 0 ){
//遍历每个有可用I/O操作Channel对应的SelectionKey
for( SelectionKey sk : selector.selectedKeys() ){
//删除正在处理的SelectionKey
selector.selectedKeys().remove(sk);
SelectableChannel sc = sk.channel();
if( sc instanceof SocketChannel ){
SocketChannel socketChannel = (SocketChannel) sc;
StringBuilder sb = new StringBuilder();
while( socketChannel.read( buffer ) > 0){
buffer.flip();
sb.append( charset.decode( buffer ));
buffer.clear();
}
//打印输出读取的内容
System.out.println( sb.toString());
}
//为下一次读取做准备
sk.interestOps( SelectionKey.OP_READ);
}
}
}catch(IOException e){
e.printStackTrace();
}
}
}
复制代码
总结:
服务器端:
1-获得Selector对象
2 获得ServerSocketChannel 对象serverChannel
3 把serverChannel跟指定的ip和端口(serverChannel对应的ServerSocket)
4 设置非阻塞模式
5 把serverChannel 注册到selector,指定模式(SelectionKey.OP_ACCEPT)
6 反复执行
1> select()选择可以选择的通道
2> 遍历所有的selectedKeys
isAcceptable:打开监听 SocketChannel channel = serverChannel.accept();
把通过监听获得到的channel设置为无阻塞模式
注册channel到selector上,同时指定模式(OP_READ)
为sk的interest集合插入一个值(为下次操作做准备)
sk.interestOps(SelectionKey)
sk.isReadable:根据sk获得它对应的通道:SelectableChannel sc = sk.channel();
类型检查if(sc.instanceof SocketChannel)
如果满足条件做类型转换:SocketChannel socketChannel = (SocketChannel)sc
读取数据(读取ByteBuffer)socketChannel.read(buffer);
向客户端发送:
if(msg.length()>0){
//遍历keys
for(SelectionKey key:selector keys()){
SelectableChannel selectChannel = key.channel();
if(selectChannel instance of SocketChannel){
SocketChannel sssccc = (SocketChannel)selectChannel
sssccc.write(charset.encode(msg));
}
}
}
客户端:
1 获得一个Selector对象selector;
2 获得SocketChannel同时指定要连接的服务器的InetSocketAddress对象
3 设置为非阻塞模式
4 注册SocketChannel到selector同时指定模式为SelectionKey.OP_READ
5 使用主线程向服务器发送数据
6 使用单独的一个线程读取服务器发送来的数据
相关文章推荐
- 使用NIO实现非阻塞Socket通信原理
- Java网络编程——使用NIO实现非阻塞Socket通信
- Java网络编程——使用NIO实现非阻塞Socket通信
- java的nio之:java的bio流下实现的socket服务器同步阻塞模型和socket的伪异步的socket服务器的通信模型
- java分布式开发TCP/IP NIO无阻塞 Socket((基于消息方式实现系统间的通信) )(转)
- Java简单实现Socket非阻塞通信
- 对Socket的理解,Socket使用TCP/IP如何实现通信
- android端和pc端使用usb进行socket通信,其中android是服务器端,pc是客户端。如何实现安卓端输入的数据通过按钮发送到pc端?
- 使用 Socket 通信实现 FTP 客户端程序
- Socket 使用select()非阻塞方式实现
- Java网络编程——使用NIO实现非阻塞Socket通信
- windows下使用php socket 和 html5 websocket实现服务器和客户端之间通信
- 使用android进行Socket通信实现多人聊天应用
- python网络编程-使用socket实现S/C之间的UDP通信
- NIO实现的简单的客户端与服务端通信(非阻塞)
- 使用 Socket 通信实现 FTP 客户端程序
- 12-使用java5条件阻塞condition实现线程间通信-实现线程间通信方式(2)
- 使用 Socket 通信实现 FTP 客户端程序
- android使用Socket通信实现多人聊天应用
- C++与Java使用SOCKET通信遇到的一种阻塞问题的解决