客户端使用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. JavaEE自定义标签:标签类的创建、tld配置文件的创建(位置、如何创建)、Web-XML配置、JSP应用

    1.标签 以类似于html标签的方式实现的java代码的封装. 第一:形成了开发标签的技术标准---自定义标签的技术标准. 第二:java标准标签库(sun之前自己开发的一系列的标签的集合)jstl, ...

  2. 使用Java程序发送Email

        目前很多大型的网站忘记登录密码常见的一种形式是使用邮箱找回密码  最近做项目也有这个需求  现在总结一下  以便以后查看 使用到的包有 mailapi.jar smtp.jar   封装发送邮 ...

  3. windows 下一个 easy_install 设备

    下载安装python安装工具 1,方法是下载ez_setup.py后 2,在cmd下运行 python ez_setup.py.就可以自己主动安装setuptools 3,环境变量设置将 C:\Pro ...

  4. 聊聊 iOS 中的网络加密

    介绍下 公司的接口一般会两种协议的,一种HTTP,一种HTTPS的,HTTP 只要请求,服务器就会响应,如果我们不对请求和响应做出加密处理,所有信息都是会被检测劫持到的,是很不安全的,客户端加密可以使 ...

  5. .NET 解析HTML代码——NSoup

    NSoup是一个开源框架,是JSoup(Java)的.NET移植版本 1.直接用起来 NSoup.Nodes.Document htmlDoc = NSoup.NSoupClient.Parse(HT ...

  6. Java基础知识强化之网络编程笔记03:UDP之UDP协议发送数据 和 接收数据

    1. UDP协议发送数据 和 接收数据 UDP协议发送数据: • 创建发送端的Socket对象 • 创建数据,并把数据打包 • 调用Socket对象的发送方法,发送数据包 • 释放资源  UDP协议接 ...

  7. Js判断对象是否为空,Js判断字符串是否为空

    Js判断对象是否为空,Js判断字符串是否为空,JS检查字符串是否为空字符串 >>>>>>>>>>>>>>>&g ...

  8. Video事件、方法-- DOM

    <video> 元素同样拥有方法.属性和事件.其中的方法用于播放.暂停以及加载等.其中的属性(比如时长.音量等)可以被读取或设置.其中的 DOM 事件能够通知您,比方说,<video ...

  9. VS2010打不开VS2012 .NET MVC 工程,及打开后部分模块加载不正确的解决办法

    转自http://www.xuebuyuan.com/2042634.html 首先,如果sln打开不正确,用(notepad++)打开sln 比如 VS2010的前两行为: Microsoft Vi ...

  10. Android学习之旅:五子棋

    在学完了Android的基础之后,我开始尝试着写一些小项目练练手,同时进一步巩固自己的基础知识,而我选的的第一个项目就是做一个简单的人人对战的五子棋小游戏. 首先,我们要新建一个自定义控件类Panel ...