基于 JAVA NIO 的socket通信

转载自http://blog.csdn.net/z69183787/article/details/52945585

基于NIO创建的客户端,只是发送数据,基于NIO创建的服务端只是接受数据

一个简单入门案例,贴代码:

1.客户端

package cn.itcast.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

public class TcpNioClient extends Thread {
		private InetSocketAddress address;
		private SocketChannel channel;
		private String msg;
		public TcpNioClient(String hostname,int port,String msg){
			address = new InetSocketAddress(hostname, port);
			this.msg = msg;
			System.out.println("------客户端发送数据----"+msg);
		}
		@Override
		public void run() {
			try {
				channel = SocketChannel.open();
				channel.configureBlocking(false);
				channel.connect(address);
				if(channel.finishConnect()){
					//两个buffer,区分用,异步的
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					if(buffer.hasRemaining()){
						channel.write(ByteBuffer.wrap(msg.getBytes()));
					}
					//程序会在这里阻塞,因为服务端没有写回数据
					while(true){
						buffer.clear();
						int readnum = channel.read(buffer);
						if(readnum>0){
							buffer.flip();
							System.out.println("客户端 ---receive data:"+new String(buffer.array(), 0, readnum));
							channel.close();
							break;
						}
					}
					
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		public static void main(String[] args) {
			for(int i=0;i<5;i++){
				TcpNioClient client = new TcpNioClient("127.0.0.1", 23000, "hello world"+i);
				client.start();
			}
			
		}
}

2. 服务端

package cn.itcast.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

import javax.rmi.CORBA.Tie;

public class NioTcpServer extends Thread {
	private final InetSocketAddress socketAddress;
	private Handler handler = new ServerHandler();
	public NioTcpServer(String localname,int port){
		socketAddress = new InetSocketAddress(localname, port);
	}
	@Override
	public void run() {
		try {
			ServerSocketChannel serverChannel = ServerSocketChannel.open();
			serverChannel.bind(socketAddress);
			serverChannel.configureBlocking(false);
			
			Selector select = Selector.open();
			serverChannel.register(select, SelectionKey.OP_ACCEPT);
			while(true){
                                     //选择信道,开始操作
                               int num =select.select();
				if(num>0){
					Iterator<SelectionKey> iterator = select.selectedKeys().iterator();
					while(iterator.hasNext()){
						SelectionKey ky = iterator.next();
						if(ky.isAcceptable()){
							handler.handleAccept(ky);
						}else if(ky.isReadable()){
							handler.readMsg(ky);
						}else if(ky.isWritable()){
							handler.writerMsg(ky);
						}
						//将键从键集中移除,新连接会阻塞,无法连接上服务器
						iterator.remove();
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		NioTcpServer tcp = new NioTcpServer("127.0.0.1", 23000);
		tcp.start();
	}
}

3.服务端,对不同兴趣集操作信道的接口

package cn.itcast.nio;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;

public interface Handler {
	void readMsg(SelectionKey key) throws IOException;
	void handleAccept(SelectionKey key) throws IOException;
	void writerMsg(SelectionKey key) throws IOException;
}	

package cn.itcast.nio;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import javax.print.attribute.standard.Severity;

public class ServerHandler implements Handler {

	@Override
	public void handleAccept(SelectionKey key) throws IOException {
		//接受请求的连接,并获取新的连接,并将新的连接注册到信道上,兴趣集为读
		ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
		SocketChannel acceptChannel = serverChannel.accept();
		if(acceptChannel!=null){
			acceptChannel.configureBlocking(false);
			//再注册前,信道必须设置为非阻塞的
			acceptChannel.register(key.selector(), SelectionKey.OP_READ);
		}
	}
	@Override
	public void readMsg(SelectionKey key) throws IOException {
		SocketChannel channel = (SocketChannel)key.channel();
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		buffer.clear();
		int readNum ;
		if((readNum=channel.read(buffer))>0){
			buffer.flip();
			System.out.println("服务端 receive :"+readNum+"bytes");
			System.out.println("服务端 接受到的数据 data:"+new String(buffer.array(), 0, readNum));
			buffer.flip();
			channel.write(buffer);
		}
		channel.close();
	}


	@Override
	public void writerMsg(SelectionKey key) throws IOException {
		//获取附件信息
		Object obj =  key.attachment();
		SocketChannel sc = (SocketChannel) key.channel();
		if(obj!=null){
			ByteBuffer buffer = ByteBuffer.wrap(obj.toString().getBytes());
			sc.write(buffer);
			key.interestOps(SelectionKey.OP_READ);
			buffer.compact();
		}
	}

}
几点需要注意的:

1. 在注册信道前,将信道配置为非阻塞,否则会报异常IllegalBlockingModeException

2. 写数据,读数据时候,注意要操作使用clear,flip方法操作缓冲区的limit,position

3. 由于是异步的,读取数据时候需要判断,返回的int的返回值,写数据时候,要判断缓冲区是否有数据

上述实现,NioTcpServer服务线程启动后,监听指定端口,等待客户端请求的到来,然后NioTcpClient客户端进程启动并发送请求数据,服务端接收到请求数据后,响应客户端(将请求的数据作为响应数据写回到客户端通道SocketChannel,并等待客户端处理)。

实际上,客户端和服务端可以采用同样轮询的非阻塞模式来实现,为简单实现在这个例子中我们把客户端角色简化了,而实际上它可能在另一个系统通信中充当服务端角色。

另外,上面对于不同事件是采用非线程的方式来处理,只是简单地调用处理的方法。在实际中,如果存在大量连接、读写请求,可以考虑使用线程池来更大程度地并发处理,提高服务端处理的速度和吞吐量,提升系统性能。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值