Java NIO(六)--ServerSocketChannel与SocketChannel

6 篇文章 0 订阅

一、SocketChanel

1、创建SocketChannel

有两种方法可以获取一个SocketChannel实例 
1. 通过静态方法open打开一个
SocketChannel socketChannel = SocketChannel.open();

ServerSocketChannel接受一个连接请求后等到
SocketChannel socketChannel = serverSocketChannel.accept();

获取到实例后可通过connect方法与服务端建立连接:

socketChannel.connect(new InetSocketAddress(1234));

2、读写数据

SocketChannel的读写数据与其他通道没有区别,读数据使用多个read方法,将数据读取如一个buffer中,返回一个int值,表示成功读取的字节数,返回-1表示读取到了数据流的末尾了;
sizeBuffer.clear();
int read = socketChannel.read(sizeBuffer);
使用write方法将buffer中的数据写入到通道中,同样需要循环写入;

buffer.flip();
while (buffer.hasRemaining()) {
    socketChannel.write(dest);
}

3、非阻塞模式


非阻塞就是普通套接字的区别了,SocketChannel在非阻塞模式下,许多方法都是可能直接返回不等待的: 
1. connect方法,可能没有完成连接建立就已经返回,需要使用finishConnect,判断是否完成了连接; 
2. read方法,可能没有读取任何就返回了(不是到数据末尾),需要通过返回值判断; 
3. write方法,与阻塞模式一样,需要在循环中写入;


二、ServerSocketChannel

1、创建ServerSocketChannel

通过静态方法open获取一个实例,并使用bind方法绑定地址:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(1234));

完成绑定后,可以通过accept方法,接受客户端连接请求,与客户端建立连接后,获取到一个SocketChannel实例,通过这个实例与建立连接的客户端进行通信;

2、非阻塞模式

ServerSocketChannel同样有非阻塞模式,此时,accept方法在没有连接请求是,可能返回Null,需要做判断处理;

三 实例

服务端代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {
	
	public static   int flag = 1;
	public static void main(String[] args) throws IOException {

		// 1. 定义ServerSocketChannel 并监听本机指定的端口
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		// 2. 设置非阻塞模式
		serverSocketChannel.configureBlocking(false);

		// 3. 检索与此通道关联的服务器套接字
		ServerSocket serverSocket = serverSocketChannel.socket();
		// 4. 进行服务的绑定
		serverSocket.bind(new InetSocketAddress(8889));

		// 5. 定义Selector
		Selector selector = Selector.open();

		// 6. 注册selector
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

		// 7.保持Server运行
		while (true) {

			// 7.1 返回读事件已经就绪的那些通道。 并进行判断
			if (selector.select(1000 * 4) == 0) {
				log("服务端在等待....");
				continue;
			}

			// 7.2 一旦调用了select()方法,并且返回值表明有一个或更多个通道就绪了
			// 然后可以通过调用selector的selectedKeys()方法,访问“已选择键集(selected key
			// set)”中的就绪通道。

			// --- 返回此选择器的已选择键集。
			Set<SelectionKey> selectionKeySet = selector.selectedKeys();
			Iterator<SelectionKey> iterator = selectionKeySet.iterator();
			
			SocketChannel client =  null;
			
			// 7.3 遍历获取
			while (iterator.hasNext()) {

				SelectionKey myKey = iterator.next();

			
				// 7.4 此键的通道是否已准备好接受新的套接字连接。
				if (myKey.isAcceptable()) {
					// 7.4.1接受到此通道套接字的连接。
					// 此方法返回的套接字通道(如果有)将处于阻塞模式。 
					client = serverSocketChannel.accept();
					// 7.4.2配置为非阻塞
					client.configureBlocking(false);
					Socket socket = client.socket();
					SocketAddress remoteAddr = socket.getRemoteSocketAddress();
					log("Connected to: " + remoteAddr + "  \t Connection Accepted:   \n");

					// 7.4.3注册到selector,等待连接
					client.register(selector, SelectionKey.OP_READ);

					
				} 
				if (myKey.isReadable()) {
					
					// 7.5.1  返回为之创建此键的通道。
					client = (SocketChannel) myKey.channel();
					ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 3);
					 // 7.5.2 将缓冲区清空以备下次读取
					byteBuffer.clear();
					
					// 读取服务器发送来的数据到缓冲区中
					int readCount = -1;
					readCount = client.read(byteBuffer);

					// 如果没有数据 则关闭
					if (readCount == -1) {
						Socket socket = client.socket(); 
						SocketAddress remoteAddr = socket.getRemoteSocketAddress();
						log("Connection closed by client: " + remoteAddr);
						client.close();
						myKey.cancel();
						return;
					}
				   
					// 有数据 则打印输出
					String receiveText = new String( byteBuffer.array(),0,readCount);
					log("服务器端接受客户端数据--:"+receiveText);
				}
				
				// 移除
				iterator.remove();
			}
		}
	}


	private static void log(String str) {
		System.out.println(str);
	}
}



客户端代码:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
 

 
public class NIOClient {
 
	public static void main(String[] args) throws IOException, InterruptedException {
 
		// 1.在客户端定义ScoketChannel 连接指定的服务端
		InetSocketAddress crunchifyAddr = new InetSocketAddress("localhost", 8889);
		SocketChannel crunchifyClient = SocketChannel.open(crunchifyAddr);
 
		log("Connecting to Server on port 8889...");
 
		// 2.定义发送的数据
		ArrayList<String> companyDetails = new ArrayList<String>();
		companyDetails.add("Lenovo");
		companyDetails.add("Samsung");
		companyDetails.add("Huawei");
		companyDetails.add("Facebook");
		companyDetails.add("Twitter");
		companyDetails.add("IBM");
		companyDetails.add("Google");
 
		// 3.循环发送
		for (String companyName : companyDetails) {
 
			// 3.1 将字符转转换为字节
			byte[] message = new String(companyName).getBytes("UTF-8");
			
			// 3.2 定义Buffer 并将字节数组数据封装在Buffer中
			ByteBuffer buffer = ByteBuffer.wrap(message);
			
			// 3.3 Channel读取Buffer中的数据
			crunchifyClient.write(buffer);
 
			log("sending: " + companyName);
			// 清空Buffer
			buffer.clear();
 
			// 等待2000毫秒
			Thread.sleep(2000);
		}
		// 关闭Channel
		crunchifyClient.close();
	}
 
	private static void log(String str) {
		System.out.println(str);
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值