非阻塞模式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(); } }
非阻塞模式ServerSocketChannel 聊天室服务器端的更多相关文章
- Python编写基于socket的非阻塞多人聊天室程序(单线程&多线程)
前置知识:socket非阻塞函数(socket.setblocking(False)),简单多线程编程 代码示例: 1. 单线程非阻塞版本: 服务端: #!/usr/bin/env python # ...
- NIO Socket非阻塞模式
NIO主要原理和适用 NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有 事件发生时,他会通知我们 ...
- 深入 CSocket 编程之阻塞和非阻塞模式
有时,花上几个小时阅读.调试.跟踪优秀的源码程序,能够更快地掌握某些技术关键点和精髓.当然,前提是对这些技术大致上有一个了解. 我通过几个采用 CSocket 类编写并基于 Client/Server ...
- JavaNIO非阻塞模式
package com.java.NIO; import java.io.IOException; import java.net.InetSocketAddress; import java.nio ...
- 非阻塞模式(ioctlsocket)
//Server.cpp #include <stdio.h> #include <winsock2.h> //winsock.h (2种套接字版本) #pragma comm ...
- 使用命名管道的OVERLAPPED方式实现非阻塞模式编程 .
命令管道是进程间通讯的一种常用方式,对于命令管道的介绍可以参考别的资料和书籍,这里推荐一个<VC++下命名管道编程的原理及实现>这篇博文,写得比较清楚.但是都是介绍了阻塞模式的编程,我这里 ...
- PHP非阻塞模式 (转自 尘缘)
让PHP不再阻塞当PHP作为后端处理需要完成一些长时间处理,为了快速响应页面请求,不作结果返回判断的情况下,可以有如下措施: 一.若你使用的是FastCGI模式,使用fastcgi_finish_re ...
- TCP同步与异步及阻塞模式,多线程+阻塞模式,非阻塞模式简单介绍
首先我简单介绍一下同步TCP编程 与异步TCP编程. 在服务端我们通常用一个TcpListener来监听一个IP和端口.客户端来一个请求的连接,在服务端可以用同步的方式来接收,也可以用异步的方式去接收 ...
- UDP socket 设置为的非阻塞模式
UDP socket 设置为的非阻塞模式 Len = recvfrom(SocketFD, szRecvBuf, sizeof(szRecvBuf), MSG_DONTWAIT, (struct so ...
随机推荐
- rsync 服务器架设方法
作者: 北南南北 来自:Linuxsir.Org 摘要: rsync 是一个快速增量文件传输工具,它可以用于在同一主机备份内部的备分,我们还可以把它作为不同主机网络备份工具之用.本文主要讲述的是如何自 ...
- Lambda02 函数式接口
1 java8默认提供的函数式接口 1.1 Predicate /* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rig ...
- 重置CentOS6.5的登录口令
早先在虚拟机Vmware里安装了一台CentOS6.5,现在想登录,发现无论怎么输入登录口令都不正确,以至于无法登录. 查阅网上资料,可用下面步骤里的方法重置登录口令,在此记录. 1.启动机器,出现下 ...
- 面试题:ConcurrentHashMap实现线程安全的原理
在ConcurrentHashMap没有出现以前,jdk使用hashtable来实现线程安全,但是hashtable是将整个hash表锁住,所以效率很低下. ConcurrentHashMap将数据分 ...
- scala _ parameter
Given that sequence, use reduceLeft to determine different properties about the collection. The foll ...
- Spring Boot 启动失败,描述/Description: Cannot determine embedded database driver class for database type NONE
异常截图====> 快速解决方式==> 在SpringBoot的启动类上面添加注解:@EnableAutoConfiguration(exclude={DataSourceAutoConf ...
- 《the art of software testing》第四章 测试用例的设计
白盒测试 逻辑覆盖测试: 逻辑覆盖是以程序内部的逻辑结构为基础的设计测试用例的技术.它属白盒测试.白盒测试的测试方法有代码检查法.静态结构分析法.静态质量度量法.逻辑覆盖法.基本路径测试法.域测试.符 ...
- Max Sum(动态规划)
原创 http://acm.hdu.edu.cn/showproblem.php?pid=1003 题目要求求出一个序列里面的最大序列和,序列要求是连续的,给出最大序列和,序列首元素下标和尾元素下标, ...
- 递增三元数组——第九届蓝桥杯C语言B组(省赛)第六题
原创 标题:递增三元组 给定三个整数数组A = [A1, A2, ... AN], B = [B1, B2, ... BN], C = [C1, C2, ... CN],请你统计有多少个三元组(i, ...
- Java集合类总结 (五)
集合框架 为了避免进行不必要的随机访问操作,Java引入了一种标签接口RandomAccess, 这个接口没有任何方法,只是一个标签,用来标记一个集合是否应该进行随机访问操作: if (c insta ...