非阻塞模式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 ...
随机推荐
- Part10-C语言环境初始化-C与汇编混合编程lesson4
1.为什么要混合编程 汇编语言:执行效率高:编写繁琐: 执行效率高:能够更直接地控制处理器. c语言:可读性强,移植性好,调试方便. 1.汇编调用c函数 2.c调用汇编函数 汇编语言定义的函数(标号) ...
- vi常用快捷键汇总
第一部分:光标移动.复制粘贴.查找替换 [Ctrl] + [f] 下一页 [Ctrl] + [b] 上一页 0或[home] 到行首 $或[end] 到行尾 G 到最后一行 gg ...
- samba服务器共享开发【windows下开发linux网站】
//@author:yuan<turing_zhy@163.com> //@date:2018-08-05 //注:码字不易转载请注明出处 //环境准备:ubuntu1~16.04.4 1 ...
- 那些年我们追过的SQL
SQL是大学必修课程之一二维表结构,看着就是一种美感. 针对近期感情,聊一聊,在平时容易犯的一个错误,看看你是不是中枪了. 我们还是选用传统的student表(请不要考虑表的结构是否合理)ID ...
- WebApi跨域问题解决
因为第一次用webapi,并且还是前后台分离,所以设置到了跨域,在百度上找了很多解决办法,但是基本都存在缺陷,我这里分享一下我自己的经验 1.首先配置Web.config 这样配置发布到服务器就可以跨 ...
- DDD模式
http://www.cnblogs.com/landeanfen/p/4816706.html https://www.cnblogs.com/malaoko/p/8732552.html
- WPF MultiSelect模式下ListBox 实现多个ListBoxItem拖拽
WPF 的ListBox不支持很多常见的用户习惯,如在Explorer中用鼠标可以选择多项Item,并且点击已经选择的Item,按住鼠标左键可以将所有已选择Item拖拽到指定的位置.本文简单的实现了这 ...
- Linux 内核list_head 学习
Linux 内核list_head 学习(一) http://www.cnblogs.com/zhuyp1015/archive/2012/06/02/2532240.html 在Linux内核中,提 ...
- ubuntu14.04,安装Gnome 15.10 (桌面)
Linux:ubuntu14.04 Gnome:15.10 更新最新版Gnome的一个好处:更新了ubuntu的软件源,我们可以使用ubuntu的软件中心获取更多需要的软件!! ubuntu默认的桌面 ...
- NSURLConnection 网络请求
前言 DEPRECATED: The NSURLConnection class should no longer be used. NSURLSession is the replacement f ...