客户端使用Java的阻塞IO

服务端使用Java的非阻塞NIO

package com.nio.echo;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner; /**
* @author 作者 E-mail:
* @version 创建时间:2015-10-29 下午02:49:47 类说明
*/
public class EchoClient
{
public static final String REMOT_IP = "127.0.0.1"; public static final int REMOTE_PORT = 8080; public void connectServer() throws IOException
{
Socket socket = new Socket(); socket.connect(new InetSocketAddress(REMOT_IP, REMOTE_PORT)); if (socket.isConnected())
{
System.out.println("connect remote address success");
} // 启动线程监听server端消息
new Thread(new client2server(socket)).start();
Scanner scanner = new Scanner(System.in); OutputStream output = socket.getOutputStream();
while (true)
{
String str = scanner.nextLine(); if (str.equals("quit"))
{
socket.close();
break;
}
output.write(str.getBytes("UTF-8")); } } public static void main(String[] args) throws IOException
{
new EchoClient().connectServer();
}
} class client2server implements Runnable
{
private Socket socket = null; public client2server(Socket socket)
{
this.socket = socket;
} @Override
public void run()
{
InputStream inputStream;
try
{
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
while (true)
{
int num = inputStream.read(bytes);
if (num != -1)
{
System.out.print(num + " ");
}
else
{
System.out.println("server is shutup");
break;
} String str = new String(bytes, 0, num, "UTF-8");
System.out.println("get data: " + str); }
}
catch(IOException e)
{
e.printStackTrace();
} }
}
package com.nio.echo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.Set; /**
* @author 作者 E-mail:
* @version 创建时间:2015-10-29 下午02:49:12 类说明
*/
public class NIOEchoServer
{
private static ServerSocketChannel ssc = null; private static Selector selector = null; private static final int PORT = 8080; public static void startServer() throws IOException
{
ssc = ServerSocketChannel.open();
selector = Selector.open();
ssc.configureBlocking(false); // nio 对socket 和serverSocket进行了怎样封装
final ServerSocket serverSocket = ssc.socket(); serverSocket.bind(new InetSocketAddress(PORT));
serverSocket.setReuseAddress(true); final AcceptHandler acceptHandler = new AcceptHandler();
ssc.register(selector, SelectionKey.OP_ACCEPT, acceptHandler);
while (true)
{
int n = selector.select();
if (n == 0)
continue; final Set<SelectionKey> readyKeys = selector.selectedKeys();
final Iterator<SelectionKey> it = readyKeys.iterator();
while (it.hasNext())
{
final SelectionKey key = it.next();
final Handle handler = (Handle) key.attachment();
handler.doHandle(key);
it.remove();
}
}
} public static void main(String[] args) throws IOException
{
NIOEchoServer.startServer();
}
} interface Handle
{
void doHandle(SelectionKey key) throws IOException;
} class AcceptHandler implements Handle
{ @Override
public void doHandle(SelectionKey key) throws IOException
{ final ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
final SocketChannel sc = ssc.accept();
final IOHandler handler = new IOHandler(key.selector(), sc);
System.out.println("server: connect success");
} } class IOHandler implements Handle
{
private final ByteBuffer readBuffer = ByteBuffer.allocate(1024); private OutputBuffer outputBuffer = new OutputBuffer(); private SocketChannel socketChannel = null; // private Selector selector = null; private SelectionKey key = null; public IOHandler(Selector selector, SocketChannel sc) throws IOException
{
// this.selector = selector;
this.socketChannel = sc;
socketChannel.configureBlocking(false);
key = socketChannel.register(selector, SelectionKey.OP_READ, this);
} /**
* 增加输出缓存
*
* @param writeData
* 要写出的数据
* @throws IOException
* @return 返回处理的字节数
*/
private int addWriteBuffer(ByteBuffer bytebuffer, int num) throws IOException
{
int prevPositon = bytebuffer.position();
outputBuffer.size += num; outputBuffer.writeBuffer.put(bytebuffer).flip();
int nowPosition = bytebuffer.position(); this.interestOps(0, SelectionKey.OP_WRITE); return nowPosition - prevPositon;
} /**
* 增加删除相应事件
*
* @param remove
* @param add
*/
private void interestOps(int remove, int add)
{
int cur = key.interestOps();
int ops = (cur & ~remove) | add;
if (cur != ops)
{
key.interestOps(ops);
key.selector().wakeup();
}
} /**
* ByteBuffer 转换 String
*
* @param buffer
* @return
*/
public static String getString(ByteBuffer buffer)
{
Charset charset = null;
CharsetDecoder decoder = null;
CharBuffer charBuffer = null;
try
{
charset = Charset.forName("UTF-8");
decoder = charset.newDecoder();
// charBuffer = decoder.decode(buffer);//用这个的话,只能输出来一次结果,第二次显示为空
charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
return charBuffer.toString();
}
catch(Exception ex)
{
ex.printStackTrace();
return "";
}
} @Override
public void doHandle(SelectionKey key) throws IOException
{
if (key.isReadable())
{
System.out.print("server: meet read event ,before read position = " + readBuffer.position()); int num = socketChannel.read(readBuffer); // 关闭
if (num == -1)
{
System.out.println("close the channel ");
key.channel();
key.channel().close();
return;
} // 将position置为0
readBuffer.flip(); System.out.print(" reveive data " + getString(readBuffer)); int dealsize = addWriteBuffer(readBuffer, num); System.out.println(" write to writeBuffer size = " + dealsize + " nowPostion = " + readBuffer.position()); // 将处理过的数据清除
readBuffer.compact();
}
else if (key.isWritable())
{
System.out.print("meet write event"); long num = socketChannel.write(outputBuffer.writeBuffer);
outputBuffer.size -= num; System.out.print("deal size = " + num + "left buffer size = " + outputBuffer.size);
if (outputBuffer.size == 0)
{
System.out.println(" deal over,cancel write event");
interestOps(SelectionKey.OP_WRITE, 0);
}
// 清除已经处理过的数据
outputBuffer.writeBuffer.compact(); }
}
} class OutputBuffer
{
public int size; public final ByteBuffer writeBuffer = ByteBuffer.allocate(1024); }

ByteBuffer没有提供有用数据的相关方法,只能自己写一个OutputBuffer来辅助处理

之前OutputBuffer只是封装了一个ByteBuffer以及一个size变量用于标示可以数据量

下面对OutputBuffer进行了重构,将size变量的修改以及数据的写入和写出操作都封装到方法中,其中output(SocketChannel socketChannel)

方法利用回调的思想,将socketChannel对象传入,在OutputBuffer当中实现数据的write输出

package com.nio.echo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.Set; /**
* @author 作者 E-mail:
* @version 创建时间:2015-10-29 下午02:49:12 类说明
*/
public class NIOEchoServer
{
private static ServerSocketChannel ssc = null; private static Selector selector = null; private static final int PORT = 8080; public static void startServer() throws IOException
{
ssc = ServerSocketChannel.open();
selector = Selector.open();
ssc.configureBlocking(false); // nio 对socket 和serverSocket进行了怎样封装
final ServerSocket serverSocket = ssc.socket(); serverSocket.bind(new InetSocketAddress(PORT));
serverSocket.setReuseAddress(true); final AcceptHandler acceptHandler = new AcceptHandler();
ssc.register(selector, SelectionKey.OP_ACCEPT, acceptHandler);
while (true)
{
int n = selector.select();
if (n == 0)
continue; final Set<SelectionKey> readyKeys = selector.selectedKeys();
final Iterator<SelectionKey> it = readyKeys.iterator();
while (it.hasNext())
{
final SelectionKey key = it.next();
final Handle handler = (Handle) key.attachment();
handler.doHandle(key);
it.remove();
}
}
} public static void main(String[] args) throws IOException
{
NIOEchoServer.startServer();
}
} interface Handle
{
void doHandle(SelectionKey key) throws IOException;
} class AcceptHandler implements Handle
{ @Override
public void doHandle(SelectionKey key) throws IOException
{ final ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
final SocketChannel sc = ssc.accept();
final IOHandler handler = new IOHandler(key.selector(), sc);
System.out.println("server: connect success");
} } class IOHandler implements Handle
{
private final ByteBuffer readBuffer = ByteBuffer.allocate(1024); private OutputBuffer outputBuffer = new OutputBuffer(); private SocketChannel socketChannel = null; // private Selector selector = null; private SelectionKey key = null; public IOHandler(Selector selector, SocketChannel sc) throws IOException
{
// this.selector = selector;
this.socketChannel = sc;
socketChannel.configureBlocking(false);
key = socketChannel.register(selector, SelectionKey.OP_READ, this);
} /**
* 增加输出缓存
*
* @param writeData
* 要写出的数据
* @throws IOException
* @return 返回处理的字节数
*/
private int addWriteBuffer(ByteBuffer bytebuffer, int num) throws IOException
{
int prevPositon = bytebuffer.position(); outputBuffer.put(bytebuffer, num); int nowPosition = bytebuffer.position(); this.interestOps(0, SelectionKey.OP_WRITE); return nowPosition - prevPositon;
} /**
* 增加删除相应事件
*
* @param remove
* @param add
*/
private void interestOps(int remove, int add)
{
int cur = key.interestOps();
int ops = (cur & ~remove) | add;
if (cur != ops)
{
key.interestOps(ops);
key.selector().wakeup();
}
} /**
* ByteBuffer 转换 String
*
* @param buffer
* @return
*/
public static String getString(ByteBuffer buffer)
{
Charset charset = null;
CharsetDecoder decoder = null;
CharBuffer charBuffer = null;
try
{
charset = Charset.forName("UTF-8");
decoder = charset.newDecoder();
// charBuffer = decoder.decode(buffer);//用这个的话,只能输出来一次结果,第二次显示为空
charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
return charBuffer.toString();
}
catch(Exception ex)
{
ex.printStackTrace();
return "";
}
} @Override
public void doHandle(SelectionKey key) throws IOException
{
if (key.isReadable())
{
System.out.print("server: meet read event ,before read position = " + readBuffer.position()); int num = socketChannel.read(readBuffer); // 关闭
if (num == -1)
{
System.out.println("close the channel ");
key.channel();
key.channel().close();
return;
} // 将position置为0
readBuffer.flip(); System.out.println(" reveive data " + getString(readBuffer)); int dealsize = addWriteBuffer(readBuffer, num); // 将处理过的数据清除
readBuffer.compact();
}
else if (key.isWritable())
{
System.out.print("meet write event"); // 写数据
outputBuffer.output(socketChannel); if (outputBuffer.size() == 0)
{
System.out.println(" deal over,cancel write event");
interestOps(SelectionKey.OP_WRITE, 0);
} }
}
} class OutputBuffer
{
private int size; private final ByteBuffer writeBuffer = ByteBuffer.allocate(1024); public void output(SocketChannel socketChannel) throws IOException
{
int num = socketChannel.write(writeBuffer);
writeBuffer.compact();
size -= num;
} public void put(ByteBuffer b, int num)
{
writeBuffer.put(b).flip();
this.size += num;
} public int size()
{
return this.size;
}
}

  

事实上在NIO网络编程中,写出数据的操作需要加入缓存才能保证效率,目的是为了写操作发生的时候不影响业务继续send消息,首先将send消息发送过来的数据缓存到A中,在写事件发生的时候将A中数据写出(此时仅短暂锁住A,将A中引用拿出,重新赋值新引用给A),这样写事件的处理过程和业务消息的send就可以高并发的进行。

Java网络编程--echo服务器的更多相关文章

  1. 网络编程-echo服务器

    代码: #coding="utf-8" #name=echo服务器 from socket import * #1.创建套接字 udpSocket = socket(AF_INET ...

  2. Java网络编程客户端和服务器通信

    在java网络编程中,客户端和服务器的通信例子: 先来服务器监听的代码 package com.server; import java.io.IOException; import java.io.O ...

  3. java网络编程serversocket

    转载:http://www.blogjava.net/landon/archive/2013/07/24/401911.html Java网络编程精解笔记3:ServerSocket详解ServerS ...

  4. JAVA网络编程【转】出处不详

    网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在 学习网络编程以前,很多初学者可能觉得网络编 ...

  5. 【转】JAVA 网络编程

    网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在 学习网络编程以前,很多初学者可能觉得网络编 ...

  6. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  7. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

  8. 实验五 Java网络编程

    实验五 Java网络编程 实验五 Java网络编程 实验五所涉及的密码学算法及编程思路 ## Java对称加密-DES算法 (1) 获取密钥生成器 KeyGenerator kg=KeyGenerat ...

  9. Java 网络编程初探

    Java 网络编程 网络编程 网络编程:进行服务器端与客户端编程的开发操作实现. java.net:网络操作包 B/S结构: 浏览器/服务器模式(Browser/Server) 不在开发客户端代码 开 ...

随机推荐

  1. 圣诞节来了,雪花纷飞的CSS3动画,还不首页用起来

    圣诞节来了,冬天来了,怎么可以没有雪花纷飞效果,昨天下班前折腾了一会儿,弄了个雪花纷飞的实例,有兴趣的可以交流分享下. 原文链接:http://www.html5think.com/article/i ...

  2. 通过Navicat for MySQL远程连接的时候报错mysql 1130的解决方法

    在用本地的navicat连接服务器的mysql数据库时候出现下面的问题: 解决的方法: 解决方法: 1.改表法.可能是你的帐号不允许从远程登陆,只能在localhost.这个时候只要在localhos ...

  3. careercup-数组和字符串1.4

    1.4 编写一个方法,将字符串中的空格全部替换为“%20“.假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的”真实“长度. C++实现代码: #include<iostream> ...

  4. 学习PHP时的一些总结(二)

    类中的构造方法和析构方法: 构造方法是对象创建完成后第一个被对象自动调用的方法.析构方法是对象在销毁之前最后一个被对象自动调用的方法. 如果没有显示的声明构造方法,类中都会默认存在一个没有参数列表并且 ...

  5. SVN/GIT源代码泄露

    造成SVN源代码漏洞的主要原因是管理员操作不规范.在使用SVN管理本地代码过程中,会自动生成一个名为.svn的隐藏文件夹,其中包含重要的源代码信息.但一些网站管理员在发布代码时,不愿意使用‘导出’功能 ...

  6. Java基础知识强化之网络编程笔记04:UDP之发送端的数据来自于键盘录入案例

    1. 数据来自于键盘录入 键盘录入数据要自己控制录入结束. 2. 代码实现: (1)发送端: package com.himi.updDemo1; import java.io.IOException ...

  7. GUI编程笔记(java)09:GUI控制文本框只能输入数字字符案例

    1.首先我们看看我的需求,如下: 控制文本框只能输入数字字符   2.源代码: package cn.itcast_07; import java.awt.FlowLayout; import jav ...

  8. Unity3D中读取CSV文件

    直接上代码 Part1: using UnityEngine; using System.IO; using System.Collections.Generic; public class CSV ...

  9. MYSQL之高级查询

    PHP高级查询 分组查询.联合查询.连接查询.子查询 版权声明:本文为博主原创文章,未经博主允许不得转载.

  10. css 导航条 布局

    导航条(简单易用的导航) <!DOCTYPE html> <html> <head> <style> ul { list-style-type:none ...