Java NIO编写Socket服务器的一个例子
来源:互联网 发布:后台调用前台js方法 编辑:程序博客网 时间:2024/06/06 01:20
Java中编写Socket服务器,通常有一下几种模式:
1. 一个链接一个线程;优点:程序编写简单; 缺点:如果链接非常多,分配的线程会非常多,机器可能资源耗尽而崩溃。
2.把每一个新链接,交接给一个拥有固定数量线程的连接池;优点:程序编写相对简单,可以处理大量的链接。确定:线程的开销非常大,链接很多的情况,排队现象会比较严重。
3. 使用Java中NIO,用异步IO方式处理。这种模式,可以用一个线程,处理大量的链接。
下面使用java中NIO,编写一个Socket服务器程序。要使用java中NIO,必须掌握下面几个概念: ByteBuffer, Channel, Selector和SelectionKey。
这里就不介绍这些基本概念了,网上资料很多。
下面程序接收客户端输入一行文本(以“\r\n”)结束,并向客户端回显输入的文本。如果输入的文本是 “get file:xxxx”模式,则把“xxxx” 解析为服务器class path下的一个文件,如果找到该文件,则回显该文件的内容,如果找不到文件,回显文件不能找到的消息。
import java.io.IOException;import java.net.InetSocketAddress;import java.net.URL;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.nio.charset.Charset;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import java.util.Iterator;import java.util.Set;public class NIOServer {private static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;private static final int BUFFER_SIZE = 1024;private int port = 8081;public NIOServer(int port) {this.port = port;}public NIOServer() {}public void start() {ServerSocketChannel ssc = null;try {ssc = ServerSocketChannel.open();ssc.configureBlocking(false);ssc.bind(new InetSocketAddress(this.port));Selector sel = Selector.open();ssc.register(sel, SelectionKey.OP_ACCEPT);while (true) {Set<SelectionKey> keySet = null;try {sel.select();keySet = sel.selectedKeys();} catch (Exception e) {e.printStackTrace();break;}for (Iterator<SelectionKey> it = keySet.iterator(); it.hasNext();) {SelectionKey sKey = it.next();it.remove();try {if (sKey.isAcceptable()) {ServerSocketChannel serChannel = (ServerSocketChannel) sKey.channel();SocketChannel clientChannel = serChannel.accept();clientChannel.configureBlocking(false);SelectionKey k2 = clientChannel.register(sel, SelectionKey.OP_READ);k2.attach(ByteBuffer.allocate(BUFFER_SIZE));} else if (sKey.isWritable()) {SocketChannel clientChannel = (SocketChannel) sKey.channel();ByteBuffer[] bfs = (ByteBuffer[]) sKey.attachment();if (bfs[bfs.length - 1].hasRemaining()) {clientChannel.write(bfs);} else {clientChannel.close();}} else if (sKey.isReadable()) {SocketChannel clientChannel = (SocketChannel) sKey.channel();ByteBuffer bf = (ByteBuffer) sKey.attachment();String msg = "";boolean clientEnd = false;if (bf.hasRemaining()) {int len = clientChannel.read(bf);if (len != -1 && bf.position() > 1) {char lastChar = (char) bf.get(bf.position() - 1);char last2Char = (char) bf.get(bf.position() - 2);if (String.valueOf(new char[] { last2Char, lastChar }).equals("\r\n")) {System.out.println("client inupt end.");clientEnd = true;}}if (len == -1) {System.out.println("client closed.");clientEnd = true;}} else {System.out.println("buff is full.");msg = "You can only enter " + BUFFER_SIZE + " chars\r\n";clientEnd = true;}if (clientEnd) {ByteBuffer[] att = processInput(bf, msg);clientChannel.register(sel, SelectionKey.OP_WRITE, att);}}} catch (Exception e) {sKey.cancel();e.printStackTrace();}}}} catch (IOException e) {e.printStackTrace();} finally {if (ssc != null) {try {ssc.close();} catch (IOException e) {e.printStackTrace();}}}}private ByteBuffer[] processInput(ByteBuffer bf, String msg) throws Exception {bf.flip();ByteBuffer promptMsg = ByteBuffer.wrap((msg + "You just input:\r\n").getBytes(DEFAULT_CHARSET));String inputMsg = new String(bf.array(), bf.position(), bf.limit(), DEFAULT_CHARSET).trim();ByteBuffer[] att = new ByteBuffer[] { promptMsg, bf };if (inputMsg.indexOf("get file:") >= 0) {String fileName = inputMsg.substring("get file:".length()).trim();System.out.println("fileName=" + fileName);URL fileURL = this.getClass().getClassLoader().getResource(fileName);if (fileURL != null) {Path path = Paths.get(fileURL.toURI());System.out.println(path);ByteBuffer fileData = ByteBuffer.wrap(Files.readAllBytes(path));ByteBuffer info = ByteBuffer.wrap(("The content of file " + fileName + ":\r\n").getBytes(DEFAULT_CHARSET));att = new ByteBuffer[] { promptMsg, bf, info, fileData };} else {String errMsg = "fileName: " + fileName + " not found in the classpath.";System.out.println(errMsg);att = new ByteBuffer[] { promptMsg, bf, ByteBuffer.wrap(errMsg.getBytes(DEFAULT_CHARSET)) };}}return att;}public static void main(String[] args) {new NIOServer().start();}}
运行上面的程序。
然后在 window的命令行,用telnet 测试:
telnet localhost 8081
然后 输入:
get file:test.txt
注意:在我的class path 下有 test.txt 这个文件(放到eclipse src/test.txt).
然后看到下面的输出:
You just input:get file:test.txtThe content of file test.txt:Hello World 1!Hello World 2!Hello World 3!Hello World 4!Connection to host lost.
常见错误1:
在写缓冲区的时候这样:
if (sKey.isWritable()){ while((buffer.hasRemaining())) {clientChannel.write(bfs); } }
读缓冲区的时候这样:
if (sKey.isReadable()) {while(buffer.hasRemaining()) {int len = clientChannel.read(bf);if (len != -1 ) break;}}
上面的的操作,只有一个channel数据处理完后,才会处理其他channel。这样就会阻塞其他channel。
常见错误2:
把所有代码都放到,一个try-catch中,如下代码:
try {ssc = ServerSocketChannel.open();ssc.configureBlocking(false);ssc.bind(new InetSocketAddress(this.port));Selector sel = Selector.open();ssc.register(sel, SelectionKey.OP_ACCEPT);while (true) {Set<SelectionKey> keySet = null;sel.select(); keySet = sel.selectedKeys();for (Iterator<SelectionKey> it = keySet.iterator(); it.hasNext();) {SelectionKey sKey = it.next();it.remove();if (sKey.isAcceptable()) {....} else if (sKey.isWritable()) {....} else if (sKey.isReadable()) {....}}}} catch (Exception e) {e.printStackTrace();} finally {if (ssc != null) {try {ssc.close();} catch (IOException e) {e.printStackTrace();}}}
这样,在处理某一客户端请求,如果出现了运行时异常,就会让整个服务器程序挂掉。
0 0
- Java NIO编写Socket服务器的一个例子
- Java NIO编写Socket服务器的一个例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- 一个 Java 的 Socket 服务器和客户端通信的例子
- java.nio的一个小例子
- 一个基于java NIO的Http服务器
- java的nio例子
- 使用Java NIO编写高性能的服务器
- 使用Java NIO编写高性能的服务器
- 使用Java NIO编写高性能的服务器
- 二叉树的后序遍历(非递归)
- jndi 数据源配置密码加密
- 多线程开发(上)
- 如何脱离Android源码环境编译aapt
- Navicat Report Viewer 连接到 MySQL 数据库的方法
- Java NIO编写Socket服务器的一个例子
- 自定义评论Dialog
- 第八周项目二—学生类
- Ubuntu 14.04 LTS 下升级 gcc 到 gcc-4.9、gcc-5版本
- android make编译 no rule to make "xxx",need "xxx"
- EMI Music Data Science Hackathon冠军团队的技术报告
- hadoop 编译native包
- XCode 7.3.1(dmg) 官方直接下载地址(离线下载)
- spring定时器分析