`
qq_24665727
  • 浏览: 121258 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

非阻塞模式ServerSocketChannel 聊天室服务器端

阅读更多
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
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;
import java.util.Iterator;
import java.util.Set;

/**
 * 非阻塞的Echoserver
 *  在这个非阻塞的模式下,Echoserver只用启动一个主线程就能同事处理其他三件事: 
 * 1.接收客户连接
 * 2.接收客户发送的数据 
 * 3.向客户发回响应数据
 * 
 * @author Administrator
 *
 */
public class NZSEchoServer {
	private Selector selector = null;
	private ServerSocketChannel serverSocketChannel = null;
	private int port = 8000;

	private Charset charset = Charset.forName("GBK");

	/**
	 * 构造方法负责启动服务器,绑定端口
	 * 
	 * @throws IOException
	 */
	private NZSEchoServer() throws IOException {
		// 创建一个selector 对象
		selector = Selector.open();
		// 创建serverSocketChannel对象
		serverSocketChannel = ServerSocketChannel.open();
		// 设置参数 使得在同一个主机上关闭了服务器程序,紧接着再启动该服务器程序时可以顺利绑定到相同的端口
		serverSocketChannel.socket().setReuseAddress(true);
		// 使erverSocketChannel工作处于非阻塞模式
		serverSocketChannel.configureBlocking(false);
		// 把服务器进程与一个本地端口绑定
		serverSocketChannel.socket().bind(new InetSocketAddress(port));
		System.out.println("服务器启动!");
	}

	/**
	 * 负责开头说的三件事 * 1.接收客户连接 2.接收客户发送的数据 3.向客户发回响应数据
	 * 
	 * @throws IOException
	 */
	public void service() throws IOException {
		// serverSocketChannel向Selector注册接收连接就绪事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		while (selector.select() > 0) {
			// 获得Selector的selected-keys集合
			Set readyKeys = selector.selectedKeys();
			Iterator it = readyKeys.iterator();
			
			//从集合中依次取出SelectionKey对象,判断是那种事件发生,然后进行处理
			while (it.hasNext()) {
				SelectionKey key = null;
				try {
					// 处理selectionkey 取出第一个selectionkey
					key = (SelectionKey) it.next();
					// 把selectionKey从selected-key集合中删除
					it.remove();

					// 这个key 标识连接就绪事件 处理
					if (key.isAcceptable()) {
						//获得与SelectionKey相连的ServerSocketChannel
						ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
						//获得与客户端连接的SocketChannel
						SocketChannel sockChannel = ssc.accept();
						System.out.println("接收到客户端连接,来自:" + sockChannel.socket().getInetAddress() + ":"
								+ sockChannel.socket().getPort());

						//设置SocketChannel为非阻塞模式
						sockChannel.configureBlocking(false);
						//创建一个用于存放用户发送来的数据的缓冲区
						ByteBuffer buffer = ByteBuffer.allocate(1024);
						//SocketChannel向Selector注册读就绪事件和写就绪事件   关联了一个buffer
						//这个byteBuffer将作为附件与新建的selectionKey对象关联
						sockChannel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE,buffer);
						
					}
					// 这个key 标识读就绪事件 处理
					if (key.isReadable()) {
						receive(key);
					}
					// 这个key 标识写就绪事件 处理
					if (key.isWritable()) {
						send(key);
					}

				} catch (Exception e) {
					e.printStackTrace();
					try {
					//使得这个selctionkey失效
					//使得selectory不再监控这个selectionkey感兴趣的事件
						if (key != null) {
							key.cancel();
							key.channel().close();
						}
					} catch (Exception e2) {
						e.printStackTrace();
					}

				}

			}

		}

	}
	/**
	 * 处理写就绪事件
	 * @param key
	 * @throws IOException
	 */
	private void send(SelectionKey key) throws IOException {
		//获取与selectionKey相关联的附件
		ByteBuffer buffer = (ByteBuffer) key.attachment();
		SocketChannel socketChannel = (SocketChannel) key.channel();
//		flip的作用有两个:
//		1. 把limit设置为当前的position值 
//		2. 把position设置为0
//		然后处理的数据就是从position到limit直接的数据,也就是你刚刚读取过来的数据
		buffer.flip();
		//按照gbk编码,把buffer中的字节转换成字符串
		String data = decode(buffer);
		//如果还没有读到一行数据,之间返回
		if (data.indexOf("\r\n") == -1)
			return;
		
		//已经有一行以上数据,截取一行数据
		String outputData = data.substring(0, data.indexOf("\n") + 1);
		System.out.println(outputData);
		//把输出的字符串按照gbk编码,转换成字节,把他反正outputBuffer中
		ByteBuffer outputBuffer = encode("echo" + outputData);
		
		//outputBuffer.hasRemaining()判断是否还有未处理的字节
		//非阻塞模式下不确保write方法一次就把outputBuffer所有字节发送完,只是奉行能发多少就发送多少的原则,所以我们要采用循环
		while (outputBuffer.hasRemaining()) {
			socketChannel.write(outputBuffer);
		}
			
			//我觉得  相当于移动 栈指针 然后删除没有指向的数据段
			ByteBuffer temp = encode(outputData);
			//设置buffer的位置为temp的极限
			buffer.position(temp.limit());
			//删除buffer中已经处理的数据
			buffer.compact();
			//如果已经输出了字符串  "bye\r\n" ,就使selectionKet失效,并关闭SocketChannel
			if (outputData.equals("bye\r\n")) {
				key.cancel();
				socketChannel.close();
				System.out.println("关闭与客户端的连接!");
			}
		
	}

	/**
	 * 
	 * 处理读就绪事件
	 * 把收到的数据放入buffer
	 * 
	 * @param key
	 * @throws IOException
	 */
	public void receive(SelectionKey key) throws IOException {
		//获得与SelectionKey关联的附件
		ByteBuffer buffer = (ByteBuffer) key.attachment();
		//获得与SelectionKey关联的Sockethannel
		SocketChannel socketChannel = (SocketChannel) key.channel();
		//创建一个byteBuffer,用于存放读到的数据
		ByteBuffer readBuffer = ByteBuffer.allocate(32);
		socketChannel.read(readBuffer);
		readBuffer.flip();
		
		
		//把buffer的极限设为容量
		buffer.limit(buffer.capacity());
		//把readBuffer中的内容拷贝到buffer中
		//假定buffer的容量足够大,不会出现缓冲区溢出异常
		buffer.put(readBuffer); // 把读到的数据放到buffer中
	}

	/**
	 * 编码  把字符串转换成自己序列
	 * 
	 * @param string
	 * @return
	 */
	private ByteBuffer encode(String string) {
		return charset.encode(string);
	}

	/**
	 * 解码  把字节序列转换为字符串
	 * 
	 * @param buffer
	 * @return
	 */
	private String decode(ByteBuffer buffer) {
		CharBuffer charBuffer = charset.decode(buffer);
		return charBuffer.toString();
	}
	/**
	 * 主程序
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		NZSEchoServer server=new NZSEchoServer();
		server.service();
	}
}

 

0
1
分享到:
评论

相关推荐

    java聊天室(绝对经典)

    一个简单的聊天室通常由服务器端和客户端两部分组成。服务器端负责接收和广播消息,而客户端则是用户交互的界面,用于发送和接收消息。在这个项目中,你将看到如何利用Java的Socket类来建立客户端和服务器之间的连接...

    NIO聊天室代码

    3. **设置非阻塞**:为了支持并发,服务器端需要将`SocketChannel`设置为非阻塞模式。 4. **创建缓冲区**:创建`ByteBuffer`实例,用于存储接收到的数据。 5. **读取和写入**:使用`read()`方法从通道读取数据到缓冲...

    基于nio的简易聊天室

    在这个聊天室项目中,服务器端可能会使用ServerSocketChannel监听新的客户端连接,客户端则使用SocketChannel与服务器建立连接。 2. **缓冲区(Buffers)**: 缓冲区是NIO中数据存储的主要方式,它们是固定大小的...

    java nio聊天室源码

    7. **安全性**:考虑到网络安全,源码可能包含了SSL/TLS支持,通过SocketChannel的configureBlocking()方法设置为非阻塞模式,并使用SSLEngine处理加密通信。 这个聊天室项目不仅展示了Java NIO的基本用法,还可能...

    java nio 聊天室源码

    - 聊天室系统可能运用了观察者模式,让服务器端能广播消息给所有在线用户。 - 可能还使用了工厂模式来创建通道和缓冲区,以及单例模式管理选择器。 9. **测试与调试** - 对聊天室的功能进行单元测试,确保消息的...

    java基于NIO选择器Selector的多人聊天室

    Java NIO(非阻塞I/O)是一种在Java中处理I/O操作的高效方式,它引入了选择器(Selector)的概念,使得一个单独的线程可以监控多个输入输出通道(Channels),大大提高了并发处理能力。在这个"java基于NIO选择器...

    基于NIO的聊天室

    `NioUtil.java`可能是包含了一些NIO相关的工具函数,比如设置SocketChannel为非阻塞模式,或者读写缓冲区的管理。在NIO中,Buffer(缓冲区)是数据传输的核心,它们可以高效地存储和操作数据。例如,`NioUtil`可能会...

    基于NIO的Java聊天室2

    在本项目"基于NIO的Java聊天室2"中,我们深入探讨了如何利用Java的非阻塞I/O(New Input/Output,NIO)框架来实现一个多人在线聊天室。NIO是一种高效的I/O模型,它允许程序在不阻塞主线程的情况下处理输入和输出操作...

    Java NIO实战之聊天室功能详解

    下面是一个聊天室服务器端的实现代码: ```java public class ChatServer implements Runnable { //选择器 private Selector selector; //注册ServerSocketChannel后的选择键 private SelectionKey serverKey; ...

    java NIO 学习 聊天室程序 (3)

    1. **服务器端**:服务器端使用NIO的ServerSocketChannel监听客户端连接。当新的客户端连接请求到达时,服务器会创建一个新的SocketChannel来处理这个连接,而不是像阻塞IO那样阻塞等待。这样服务器就可以继续监听...

    Java NIO Selector用法详解【含多人聊天室实例】

    服务器端创建一个ServerSocketChannel,监听客户端的连接请求。每当有新的连接建立或已有连接有数据可读时,Selector会通知服务器。服务器根据Selector的结果处理每个连接,读取客户端发送的数据,并将响应广播回...

    Java NIO 聊天室 JSwing

    // 设置通道为非阻塞 channel.configureBlocking(false); // 获得一个通道管理器 this.selector = Selector.open(); // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调 //用...

    java Socket通信实现

    - 即时聊天应用:基于Socket的实时通信系统,如简单的聊天室。 - 远程控制:通过Socket实现远程服务器的控制和监控。 8. **NIO(非阻塞I/O)**: - Java NIO(New I/O)库提供了非阻塞的Socket通信方式,提高了...

    Java NIO入门

    1. **高并发**:NIO的非阻塞特性使得它在处理大量并发连接时表现出色,特别适合于服务器端编程,例如网络聊天室、分布式系统等。 2. **大数据传输**:对于大文件或大量数据的传输,NIO的缓冲区和非阻塞特性能够减少...

    java socket编程实例

    - Java NIO(New I/O)提供了非阻塞的Socket编程接口,可以更高效地处理多个连接。`java.nio.channels`包下的`ServerSocketChannel`和`SocketChannel`可以替代传统的Socket和ServerSocket。 通过这个压缩包中的...

    Java网络高级编程

    10. **实战项目**:通过实际的网络应用开发,如聊天室、文件传输等,巩固理论知识并提升实践能力。 综上所述,“Java网络高级编程”涵盖了Java网络编程的各个方面,无论你是初学者还是经验丰富的开发者,都能从中...

    java 的socket编程

    `java.nio.channels`包下的`SocketChannel`和`ServerSocketChannel`可以替代传统的Socket,实现非阻塞I/O操作。 7. **套接字选项** Java的Socket类提供了多种设置选项,例如设置超时时间、启用/禁用套接字选项等,...

    NIO网络通讯编程

    6. NIO在实际应用中的场景:NIO常用于服务器端的高并发场景,如大型Web服务器、游戏服务器、聊天室等。此外,NIO也适用于需要高效读写文件或网络流的场合,例如大数据处理、日志收集系统等。 7. Java NIO库的其他...

    Java网络Socket

    Java NIO(Non-blocking Input/Output)提供了新的Socket API,如`java.nio.channels.SocketChannel`和`java.nio.channels.ServerSocketChannel`,支持非阻塞模式,提高了网络通信效率。 总之,Java网络Socket是...

    NIO学习总结

    1. **服务器端的高并发处理**:在需要同时处理大量客户端连接的场景,如聊天室、在线游戏服务器等,NIO能有效提高系统的并发性能。 2. **大文件传输**:由于NIO的缓冲区机制,处理大文件时可以减少磁盘I/O次数,...

Global site tag (gtag.js) - Google Analytics