JAVA-NIO实现(二)
来源:互联网 发布:淘宝网艾莱依羽绒服 编辑:程序博客网 时间:2024/04/29 05:52
在上篇文章中,JAVA-NIO的实现用浏览器能访问到,但是自己写了HTTP的JAVA代码来访问,发现了一个问题,得不到服务端传回来的数据。所以从新改动了服务端的代码。扩充了使用线程来处理请求。
打开火狐进行请求的跟踪,发现没有响应码,而我再JAVA代码中却写了相关的响应码验证,导致返回失败。
客户端代码如下:红色部分为出现问题的地方
String clientContent = "THIS IS MY TEST DATA";
BufferedReader br = null;
URL url = null;
HttpURLConnection connection = null;
try {
url = new URL(urlPath);
connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod(type);
connection.setRequestProperty("Content-type",
"application/x-www-form-urlencoded");
connection.setRequestProperty("User-Agent", "liuceshi");
connection.setRequestProperty("Content-Type", "text/plain; charset=UTF-8");
connection.setRequestProperty("Connection", "keep-alive");
connection.connect();
OutputStreamWriter out = new OutputStreamWriter(
connection.getOutputStream(), charset);
out.write(clientContent+" ===序列号"+threadName);
out.flush();
System.out.println(threadName+"-----send Data");
if(connection.getResponseCode() == 200){
br = new BufferedReader(new InputStreamReader(
connection.getInputStream(), charset));
StringBuffer resBuffer = new StringBuffer();
String resTemp = "";
while ((resTemp = br.readLine()) != null) {
resBuffer.append(resTemp);
}
System.out.println("服务器端返回的结果:"+resBuffer.toString());
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
connection.disconnect();
}
于是改动服务端代码,添加响应头:红色部分为新添加内容
package org.com.liu.reactor;
import java.io.IOException;
import java.net.InetSocketAddress;
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.util.Iterator;
import java.util.Set;
/*
* Selector事件注册机要保证是单例模式的,
* ServerSocketChannel也要保证是单例模式的
*
*
* 当然也可以想办法来对其进行多线程的扩展
*/
public class Reactor implements Runnable{
private final static int SERVER_PORT = 9000;
private final static String SERVER_URI = "127.0.0.1";
//事件监听
private static Selector selector = null;
//服务器端套接字通道对象
private static ServerSocketChannel serverSocketChannel = null;
//缓存块大小
private int BLOCK = 4096;
/*******************这里一定要区分的是,服务器端的Channel和客户端的Cchannel事件区别*******************************/
public Reactor() {
try {
//打开服务器端的通道
serverSocketChannel = ServerSocketChannel.open();
//实例化单利模式的selector注册机
selector = Selector.open();
//服务器通道设置为非阻塞,如果为true 那么就为传统的阻塞方式
serverSocketChannel.configureBlocking(false);
/*
* 通过这个选项,可以使多个Socket对象绑定在同一个端口上。其实这样做并没有多大意义,但当使用close方法关闭Socket连接后,
* Socket对象所绑定的端口并不一定马上释放;系统有时在Socket连接关闭才会再确认一下是否有因为延迟面未到达的数据包,
* 这完全是在底层处理的,也就是说对用户是透明的;因此,在使用Socket类时完全不会感觉到。
* 这种处理机制对于随机绑定端口的Socket对象没有什么影响,
* 但对于绑定在固定端口的Socket对象就可能会抛出“Address already in use: JVM_Bind”例外。
* 因此,使用这个选项可以避免个例外的发生。
*/
serverSocketChannel.socket().setReuseAddress(true);
//绑定端口
serverSocketChannel.socket().bind(new InetSocketAddress(SERVER_URI,SERVER_PORT));
/*
* 这里要注意:这里是将服务器端的Channel注册为可接听,而且服务器端的Channel也只能注册为OP_ACCEPT事件
*/
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
/*
* 绑定处理事件:如果服务器端监听到有事件到来,交给Acceptor来处理
*/
selectionKey.attach(new Acceptor());
System.out.println("N-AServer is starting at " + SERVER_PORT + "......");
} catch (IOException e) {
}
}
@Override
public void run() {
while(true){
try {
if(selector.select() > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
System.out.println("目前已经接受的key的 size : " + selectionKeys.size());
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
iterator.remove();
if(!selectionKey.isValid() || !selectionKey.channel().isOpen()){
continue;
}
//这里打印判断没回接受到的事件的类型
System.out.println("表示是否可连接"+selectionKey.isAcceptable());
System.out.println("表示是否可读入"+selectionKey.isReadable());
System.out.println("表示是否可写出"+selectionKey.isWritable());
//将之前绑定的OP_ACCEPT事件的对象取出来,调用该对象中的方法,基于接口调用
dispatch(selectionKey);
}
}
} catch (Exception e) {
System.out.println("N-AServer cannot select events.....");
e.printStackTrace();
}
}
}
public void dispatch (SelectionKey selectionKey){
Runnable runnable = (Runnable) selectionKey.attachment();
if(runnable != null){
runnable.run();
}
}
//请求接受事件的处理类,Reactor模式中的accept
class Acceptor implements Runnable{
@Override
public void run() {
SocketChannel socketChannel = null;
try {
/*
* 通过服务器端的Channel的接受,来接受一个客户端的Channel通道,
* 这里的设计是,服务器端只有一个serverSocketChannel通道来连接请求
*/
socketChannel = serverSocketChannel.accept();
if(socketChannel != null){
System.out.println("=======》接收到来自客户端("
+ socketChannel.socket().getInetAddress().getHostAddress()
+ ")的连接《======");
System.out.println();
new RequestHandler(socketChannel,selector);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//这是处理客户端Channel数据的线程,客户端通道也要在Selector注册机中注册时间
class RequestHandler implements Runnable{
//客户端通道
private SocketChannel socketChannel;
private SelectionKey selectionKey;
private String MIMEType = "application/json;charset=UTF-8";
public RequestHandler(SocketChannel socketChannel , Selector selector) {
System.out.println("启动线程来处理客户端Channel。。。。。。====>进入客户端通道的处理:");
this.socketChannel = socketChannel;
try {
//这里将"客户端"的阻塞方式设置为非阻塞,
socketChannel.configureBlocking(false);
//为客户端通道注册读事件
selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);
//如果客户端发生了读事件,那么在run本类方法中来解析读事件,或者单独为客户端的时间处理写个handler
selectionKey.attach(this);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
if(selectionKey.isReadable()) {
read();
selectionKey.interestOps(SelectionKey.OP_WRITE);
}else if (selectionKey.isWritable()){
process();
selectionKey.cancel();
//采用流的方式向客户端输出内容,而不采用通道的方式
System.out.println("服务器发送消息成功!");
}
}catch (IOException e) {
e.printStackTrace();
}
}
public void read() throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(BLOCK);
socketChannel.read(buffer);
System.out.println("接收到来自客户端(" + socketChannel.socket().getInetAddress().getHostAddress()
+ ")的消息:" );
System.out.println(new String(buffer.array(),"UTF-8"));
}
public void process() throws IOException {
String content = "这是我的测试数据";
//生成HTTP 相应头
String header="HTTP/1.0 200 OK\r\n"+
"Server: N-AServer 1.0\r\n"+
"Content-length: "+content.length()+"\r\n"+
"Content-type: "+MIMEType+"\r\n\r\n";
ByteBuffer sendBuffer = ByteBuffer.allocate(2048);
//想向客户端写入响应头
sendBuffer.put(header.getBytes("GBK"));
sendBuffer.put(content.getBytes());
sendBuffer.flip();
socketChannel.write(sendBuffer);
socketChannel.close();
}
}
public static void main(String[] args) {
Runnable runnable = new Reactor();
runnable.run();
}
}
- JAVA-NIO实现(二)
- Java NIO(二)
- Java NIO(二)
- (二)Java NIO Channel
- Java NIO 学习(二)
- java nio学习(二)
- Java NIO 详解(二)
- JAVA NIO 服务器(二)
- Java NIO 详解(二)
- Java NIO学习(二)
- java nio学习(二)
- Java NIO 详解(二)
- Java NIO 详解(二)
- Java NIO 详解(二)
- 【转】Java NIO(二)
- Java NIO 详解(二)
- Java NIO (二) 通道(Channels)
- Java NIO系列教程(二) Channel
- 了解sota字符界面(章节4.1)
- ??
- 数控学习----伺服
- Android项目(二)-- 创建Hello World
- eclipse 找不到application选项
- JAVA-NIO实现(二)
- 嵌入式Linux中initrd的应用--浅析ramdisk、ramfs、initrd和initramfs
- 关于把struts2项目修改为开发模式,项目无法启动问题说明
- 一种石头,在某一高度扔下就会碎,在这个高度以下不会碎,高度以上一定碎。现在有4个石头,1000层的楼房,需要测定这个石头破碎的高度。求最少多少次一定可以测出来。
- DES原理分析
- asp.net Xml绑定到数据控件的两种简单方法
- VC 判断文件是否存在的几种方法
- jQuery EasyUI DataGrid 禁用某行Checkbox
- Java层与Jni层的字节数组传递