客户端:
package netty3.socket.client;
import static org.jboss.netty.channel.Channels.pipeline;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpClientCodec;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import org.jboss.netty.handler.codec.http.multipart.HttpDataFactory;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData;
import org.jboss.netty.handler.stream.ChunkedWriteHandler;
import org.jboss.netty.util.CharsetUtil;
public class UploadFileClient
{
private ClientBootstrap bootstrap = null;
private ChannelFuture future = null;
private HttpDataFactory factory = null;
// 服务端处理完成后返回的消息
private StringBuffer retMsg = new StringBuffer();
public UploadFileClient()
{
bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
bootstrap.setPipelineFactory(new UploadChannelFactory());
// 连接超时时间为3s
bootstrap.setOption("connectTimeoutMillis", 3000);
future = bootstrap.connect(new InetSocketAddress("127.0.0.1", 2777));
// 获得一个阈值,它是来控制上传文件时内存/硬盘的比值,防止出现内存溢出
factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
}
/**
* 方法描述:关闭文件发送通道(为阻塞式)
*/
public void shutdownClient()
{
// 等待数据的传输通道关闭
future.getChannel().getCloseFuture().awaitUninterruptibly();
bootstrap.releaseExternalResources();
// Really clean all temporary files if they still exist
factory.cleanAllHttpDatas();
}
/**
* 方法描述:获取发送文件过程中服务端反馈的消息
* @return 服务端反馈的消息
*/
public String getRetMsg()
{
return retMsg.toString();
}
/**
* 方法描述:将文件上传到服务端
* @param file 待上传的文件
*/
public void uploadFile(File file)
{
if (!file.canRead())
{
return;
}
// Simple Post form: factory used for big attributes
List<InterfaceHttpData> bodylist = formpost(file);
if (bodylist == null)
{
return;
}
// Multipart Post form: factory used
uploadFileToServer(file.getName(), factory, bodylist);
}
/**
* @param file
* @return
*/
private List<InterfaceHttpData> formpost(File file)
{
// Prepare the HTTP request.
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "");
// Use the PostBody encoder
HttpPostRequestEncoder bodyRequestEncoder = null;
try
{
bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, false);
bodyRequestEncoder.addBodyAttribute("getform", "POST");
bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false);
}
catch(Exception e)
{
// should not be since args are not null
e.printStackTrace();
return null;
}
// Create the bodylist to be reused on the last version with Multipart support
List<InterfaceHttpData> bodylist = bodyRequestEncoder.getBodyListAttributes();
return bodylist;
}
/**
* Multipart example
*/
private void uploadFileToServer(String fileName, HttpDataFactory factory, List<InterfaceHttpData> bodylist)
{
// Wait until the connection attempt succeeds or fails.
Channel channel = future.awaitUninterruptibly().getChannel();
if (!future.isSuccess())
{
future.getCause().printStackTrace();
bootstrap.releaseExternalResources();
return;
}
// Prepare the HTTP request.
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, fileName);
// 设置该属性表示服务端文件接收完毕后会关闭发送通道
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
// Use the PostBody encoder
HttpPostRequestEncoder bodyRequestEncoder = null;
try
{
bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, true);
bodyRequestEncoder.setBodyHttpDatas(bodylist);
bodyRequestEncoder.finalizeRequest();
}
catch(Exception e)
{
// should not be since no null args
e.printStackTrace();
}
System.out.println("开始时间:"+System.currentTimeMillis());
// send request
channel.write(request);
// test if request was chunked and if so, finish the write
if (bodyRequestEncoder.isChunked())
{
channel.write(bodyRequestEncoder).awaitUninterruptibly();
}
// Now no more use of file representation (and list of HttpData)
bodyRequestEncoder.cleanFiles();
}
private class UploadChannelFactory implements ChannelPipelineFactory
{
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = pipeline();
pipeline.addLast("decoder", new HttpResponseDecoder());
pipeline.addLast("encoder", new HttpRequestEncoder());
pipeline.addLast("codec", new HttpClientCodec());
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
pipeline.addLast("handler", new UploadClientHandler());
return pipeline;
}
}
private class UploadClientHandler extends SimpleChannelUpstreamHandler
{
private boolean readingChunks;
/**
* 方法描述:接收服务端返回的消息
* @param ctx 发送消息的通道对象
* @param e 消息发送事件对象
*/
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
if (!readingChunks)
{
HttpResponse response = (HttpResponse)e.getMessage();
// 收到服务端反馈的消息,并且链接正常、且还有后续消息
if (response.getStatus().getCode() == 200 && response.isChunked())
{
readingChunks = true;
}
else
{
// 服务端有反馈消息,但没有后续的消息了
ChannelBuffer content = response.getContent();
if (content.readable())
{
retMsg.append(content.toString(CharsetUtil.UTF_8));
}
}
}
else
{
HttpChunk chunk = (HttpChunk)e.getMessage();
if (chunk.isLast())
{
// 服务端的消息接收完毕
readingChunks = false;
}
else
{
// 连续接收服务端发过来的消息
retMsg.append(chunk.getContent().toString(CharsetUtil.UTF_8));
}
}
}
/**
* 方法描述:消息接收或发送过程中出现异常
* @param ctx 发送消息的通道对象
* @param e 异常事件对象
*/
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
{
System.out.println("异常--:" + e.getCause());
e.getChannel().close();
// 有异常后释放客户端占用的通道资源
shutdownClient();
}
}
}
服务端:
package netty3.socket.server;
import static org.jboss.netty.channel.Channels.pipeline;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.stream.ChunkedWriteHandler;
public class InitServer
{
private static InitServer sockServer = null;
private static ServerBootstrap bootstrap = null;
public static InitServer getInstance()
{
if (sockServer == null)
{
sockServer = new InitServer();
}
return sockServer;
}
public InitServer()
{
bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
bootstrap.setPipelineFactory(new ChannelPipelineFactory()
{
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
pipeline.addLast("handler", new ServerHandler());
return pipeline;
}
});
bootstrap.bind(new InetSocketAddress("127.0.0.1", 2777));
}
public void shutdownServer()
{
bootstrap.releaseExternalResources();
}
}
package netty3.socket.server;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CACHE_CONTROL;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.DATE;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.EXPIRES;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LAST_MODIFIED;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.TimeZone;
import javax.activation.MimetypesFileTypeMap;
import netty3.socket.client.SendMsgClient;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelFutureProgressListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.DefaultFileRegion;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.FileRegion;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.codec.frame.TooLongFrameException;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.multipart.Attribute;
import org.jboss.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import org.jboss.netty.handler.codec.http.multipart.DiskFileUpload;
import org.jboss.netty.handler.codec.http.multipart.FileUpload;
import org.jboss.netty.handler.codec.http.multipart.HttpDataFactory;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData;
import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.handler.stream.ChunkedFile;
import org.jboss.netty.util.CharsetUtil;
public class ServerHandler extends SimpleChannelHandler
{
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
public static final int HTTP_CACHE_SECONDS = 60;
private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if size exceed MINSIZE
private HttpPostRequestDecoder decoder;
private HttpRequest request;
private String receiveFileName = "";
private Map<String, String> msgMap = new HashMap<String, String>();
private boolean readingChunks = false;
static
{
DiskFileUpload.baseDirectory = "/home/build1/file_test/";
}
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
if (e.getMessage() instanceof HttpRequest)
{
HttpRequest request = (DefaultHttpRequest)e.getMessage();
String uri = sanitizeUri(request.getUri());
System.out.println(request.isChunked());
if (request.getMethod() == HttpMethod.POST)
{
// 接收客户端上传的文件
receiveFileName = uri;
this.request = request;
// clean previous FileUpload if Any
if (decoder != null)
{
decoder.cleanFiles();
decoder = null;
}
// if GET Method: should not try to create a HttpPostRequestDecoder
try
{
decoder = new HttpPostRequestDecoder(factory, request);
}
catch(Exception e1)
{
e1.printStackTrace();
writeResponse(e.getChannel(), "接收文件信息时出现异常:" + e1.toString());
Channels.close(e.getChannel());
return;
}
if (!request.isChunked())
{
readHttpDataAllReceive(e.getChannel());
writeResponse(e.getChannel(), "服务端文件接收完毕!");
}
}
}
else
{
// New chunk is received
HttpChunk chunk = (HttpChunk)e.getMessage();
// example of reading only if at the end
if (!chunk.isLast())
{
try
{
decoder.offer(chunk);
}
catch(Exception e1)
{
e1.printStackTrace();
writeResponse(e.getChannel(), "接收文件数据时出现异常:" + e1.toString());
Channels.close(e.getChannel());
return;
}
// example of reading chunk by chunk (minimize memory usage due to Factory)
readHttpDataChunkByChunk();
} else {
readHttpDataAllReceive(e.getChannel());
//writeResponse(e.getChannel(), "服务端数据接收完毕!");
String sendMsg = msgMap.get("sendMsg");
System.out.println("服务端收到消息:" + sendMsg);
sendReturnMsg(ctx, HttpResponseStatus.OK, "服务端返回的消息!");
}
}
}
/**
* Example of reading all InterfaceHttpData from finished transfer
*/
private void readHttpDataAllReceive(Channel channel)
{
List<InterfaceHttpData> datas;
try
{
datas = decoder.getBodyHttpDatas();
}
catch(Exception e1)
{
e1.printStackTrace();
writeResponse(channel, "接收文件数据时出现异常:" + e1.toString());
Channels.close(channel);
return;
}
for (InterfaceHttpData data : datas)
{
writeHttpData(data);
}
}
/**
* Example of reading request by chunk and getting values from chunk to chunk
*/
private void readHttpDataChunkByChunk()
{
try
{
while(decoder.hasNext())
{
InterfaceHttpData data = decoder.next();
if (data != null)
{
// new value
writeHttpData(data);
}
}
}
catch(EndOfDataDecoderException e1)
{
e1.printStackTrace();
}
}
private void writeHttpData(InterfaceHttpData data)
{
if (data.getHttpDataType() == HttpDataType.FileUpload)
{
FileUpload fileUpload = (FileUpload)data;
if (fileUpload.isCompleted())
{
try
{
Random r = new Random();
StringBuffer fileNameBuf = new StringBuffer();
fileNameBuf.append(DiskFileUpload.baseDirectory).append("U").append(System.currentTimeMillis());
fileNameBuf.append(String.valueOf(r.nextInt(10))).append(String.valueOf(r.nextInt(10)));
fileNameBuf.append(receiveFileName.substring(receiveFileName.lastIndexOf(".")));
fileUpload.renameTo(new File(fileNameBuf.toString()));
}
catch(IOException e)
{
e.printStackTrace();
}
System.out.println("结束时间:"+System.currentTimeMillis());
}
else
{
System.out.println("\tFile to be continued but should not!\r\n");
}
}
else if (data.getHttpDataType() == HttpDataType.Attribute)
{
Attribute attribute = (Attribute)data;
try
{
msgMap.put(attribute.getName(), attribute.getString());
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
private void writeResponse(Channel channel, String retMsg)
{
// Convert the response content to a ChannelBuffer.
ChannelBuffer buf = ChannelBuffers.copiedBuffer(retMsg, CharsetUtil.UTF_8);
// Decide whether to close the connection or not.
boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION))
|| request.getProtocolVersion().equals(HttpVersion.HTTP_1_0)
&& !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION));
// Build the response object.
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.setContent(buf);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
if (!close)
{
// There's no need to add 'Content-Length' header
// if this is the last response.
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
}
// Write the response.
ChannelFuture future = channel.write(response);
// Close the connection after the write operation is done if necessary.
if (close)
{
future.addListener(ChannelFutureListener.CLOSE);
}
}
private String sanitizeUri(String uri)
{
try
{
uri = URLDecoder.decode(uri, "UTF-8");
}
catch(UnsupportedEncodingException e)
{
try
{
uri = URLDecoder.decode(uri, "ISO-8859-1");
}
catch(UnsupportedEncodingException e1)
{
throw new Error();
}
}
return uri;
}
/**
* 方法描述:设置请求响应的header信息
* @param response 请求响应对象
* @param fileToCache 下载文件
*/
private static void setContentTypeHeader(HttpResponse response, File fileToCache)
{
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
response.setHeader(CONTENT_TYPE, mimeTypesMap.getContentType(fileToCache.getPath()));
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
// Date header
Calendar time = new GregorianCalendar();
response.setHeader(DATE, dateFormatter.format(time.getTime()));
// Add cache headers
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
response.setHeader(EXPIRES, dateFormatter.format(time.getTime()));
response.setHeader(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
response.setHeader(LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
}
/**
* 方法描述:给客户端发送反馈消息
* @param ctx 发送消息的通道
* @param status 状态
* @param retMsg 反馈消息
*/
private static void sendReturnMsg(ChannelHandlerContext ctx, HttpResponseStatus status, String retMsg)
{
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
response.setContent(ChannelBuffers.copiedBuffer(retMsg, CharsetUtil.UTF_8));
// 信息发送成功后,关闭连接通道
ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
}
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
if (decoder != null)
{
decoder.cleanFiles();
}
System.out.println("连接断开:" + e.getChannel().getRemoteAddress().toString());
}
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
String remoteIp = e.getChannel().getRemoteAddress().toString();
System.out.println(remoteIp.substring(1, remoteIp.indexOf(":")));
System.out.println("收到连接:" + e.getChannel().getRemoteAddress().toString());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
{
Channel ch = e.getChannel();
Throwable cause = e.getCause();
if (cause instanceof TooLongFrameException)
{
return;
}
System.err.println("连接的通道出现异常:" + cause.toString());
if (ch.isConnected())
{
System.out.println("连接还没有关闭!");
ch.close();
}
}
}