正在显示
93 个修改的文件
包含
4237 行增加
和
0 行删除
netty4-demos.iml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> | ||
| 3 | + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7"> | ||
| 4 | + <output url="file://$MODULE_DIR$/target/classes" /> | ||
| 5 | + <output-test url="file://$MODULE_DIR$/target/test-classes" /> | ||
| 6 | + <content url="file://$MODULE_DIR$"> | ||
| 7 | + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> | ||
| 8 | + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> | ||
| 9 | + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> | ||
| 10 | + <excludeFolder url="file://$MODULE_DIR$/target" /> | ||
| 11 | + </content> | ||
| 12 | + <orderEntry type="inheritedJdk" /> | ||
| 13 | + <orderEntry type="sourceFolder" forTests="false" /> | ||
| 14 | + <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" /> | ||
| 15 | + <orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" /> | ||
| 16 | + <orderEntry type="library" name="Maven: io.netty:netty-all:4.1.30.Final" level="project" /> | ||
| 17 | + <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.6.3" level="project" /> | ||
| 18 | + <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.6.3" level="project" /> | ||
| 19 | + <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.6.0" level="project" /> | ||
| 20 | + </component> | ||
| 21 | +</module> |
pom.xml
0 → 100644
| 1 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 2 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 3 | + <modelVersion>4.0.0</modelVersion> | ||
| 4 | + | ||
| 5 | + <groupId>com.waylau</groupId> | ||
| 6 | + <artifactId>netty4-demos</artifactId> | ||
| 7 | + <version>1.0.0</version> | ||
| 8 | + <packaging>jar</packaging> | ||
| 9 | + | ||
| 10 | + <name>netty4-demos</name> | ||
| 11 | + <url>http://www.waylau.com</url> | ||
| 12 | + | ||
| 13 | + <properties> | ||
| 14 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 15 | + <version.jackson.core>2.6.3</version.jackson.core> | ||
| 16 | + </properties> | ||
| 17 | + <build> | ||
| 18 | + <plugins> | ||
| 19 | + <plugin> | ||
| 20 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 21 | + <artifactId>maven-compiler-plugin</artifactId> | ||
| 22 | + <version>3.2</version> | ||
| 23 | + <configuration> | ||
| 24 | + <optimize>true</optimize> | ||
| 25 | + <source>1.7</source> | ||
| 26 | + <target>1.7</target> | ||
| 27 | + </configuration> | ||
| 28 | + </plugin> | ||
| 29 | + | ||
| 30 | + </plugins> | ||
| 31 | + </build> | ||
| 32 | + <dependencies> | ||
| 33 | + <dependency> | ||
| 34 | + <groupId>junit</groupId> | ||
| 35 | + <artifactId>junit</artifactId> | ||
| 36 | + <version>4.12</version> | ||
| 37 | + <scope>test</scope> | ||
| 38 | + </dependency> | ||
| 39 | + | ||
| 40 | + <dependency> | ||
| 41 | + <groupId>io.netty</groupId> | ||
| 42 | + <artifactId>netty-all</artifactId> | ||
| 43 | + <version>4.1.30.Final</version> | ||
| 44 | + </dependency> | ||
| 45 | + <dependency> | ||
| 46 | + <groupId>com.fasterxml.jackson.core</groupId> | ||
| 47 | + <artifactId>jackson-core</artifactId> | ||
| 48 | + <version>${version.jackson.core}</version> | ||
| 49 | + </dependency> | ||
| 50 | + <dependency> | ||
| 51 | + <groupId>com.fasterxml.jackson.core</groupId> | ||
| 52 | + <artifactId>jackson-databind</artifactId> | ||
| 53 | + <version>${version.jackson.core}</version> | ||
| 54 | + </dependency> | ||
| 55 | + </dependencies> | ||
| 56 | +</project> |
src/main/java/com/waylau/netty/App.java
0 → 100644
| 1 | +package com.waylau.netty; | ||
| 2 | + | ||
| 3 | +import java.io.IOException; | ||
| 4 | +import java.io.OutputStream; | ||
| 5 | +import java.net.Socket; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 测试用的 TCP 客户端 | ||
| 9 | + * | ||
| 10 | + * @author waylau.com | ||
| 11 | + * @date 2015-2-26 | ||
| 12 | + */ | ||
| 13 | +public class TcpClient { | ||
| 14 | + | ||
| 15 | + public static void main(String[] args) throws IOException { | ||
| 16 | + Socket socket = null; | ||
| 17 | + OutputStream out = null; | ||
| 18 | + | ||
| 19 | + try { | ||
| 20 | + | ||
| 21 | + socket = new Socket("localhost", 8023); | ||
| 22 | + out = socket.getOutputStream(); | ||
| 23 | + | ||
| 24 | + // 请求服务器 | ||
| 25 | + String lines = "床前明月光\r\n疑是地上霜\r\n举头望明月\r\n低头思故乡\r\n"; | ||
| 26 | + byte[] outputBytes = lines.getBytes("UTF-8"); | ||
| 27 | + out.write(outputBytes); | ||
| 28 | + out.flush(); | ||
| 29 | + | ||
| 30 | + } finally { | ||
| 31 | + // 关闭连接 | ||
| 32 | + out.close(); | ||
| 33 | + socket.close(); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import java.util.List; | ||
| 4 | +import java.util.Map; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 说明:一个用户 POJO | ||
| 8 | + * | ||
| 9 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日 | ||
| 10 | + */ | ||
| 11 | +public class JacksonBean { | ||
| 12 | + | ||
| 13 | + private int age; | ||
| 14 | + private String name; | ||
| 15 | + private List<String> sons; | ||
| 16 | + private Map<String, String> addrs; | ||
| 17 | + | ||
| 18 | + public int getAge() { | ||
| 19 | + return age; | ||
| 20 | + } | ||
| 21 | + public void setAge(int age) { | ||
| 22 | + this.age = age; | ||
| 23 | + } | ||
| 24 | + public String getName() { | ||
| 25 | + return name; | ||
| 26 | + } | ||
| 27 | + public void setName(String name) { | ||
| 28 | + this.name = name; | ||
| 29 | + } | ||
| 30 | + public List<String> getSons() { | ||
| 31 | + return sons; | ||
| 32 | + } | ||
| 33 | + public void setSons(List<String> sons) { | ||
| 34 | + this.sons = sons; | ||
| 35 | + } | ||
| 36 | + public Map<String, String> getAddrs() { | ||
| 37 | + return addrs; | ||
| 38 | + } | ||
| 39 | + public void setAddrs(Map<String, String> addrs) { | ||
| 40 | + this.addrs = addrs; | ||
| 41 | + } | ||
| 42 | + /** | ||
| 43 | + * | ||
| 44 | + */ | ||
| 45 | + public JacksonBean() { | ||
| 46 | + // TODO Auto-generated constructor stub | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import java.util.ArrayList; | ||
| 4 | +import java.util.HashMap; | ||
| 5 | +import java.util.List; | ||
| 6 | +import java.util.Map; | ||
| 7 | + | ||
| 8 | +import io.netty.bootstrap.Bootstrap; | ||
| 9 | +import io.netty.channel.Channel; | ||
| 10 | +import io.netty.channel.EventLoopGroup; | ||
| 11 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 12 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 说明:Jackson json-客户端 | ||
| 17 | + * | ||
| 18 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 19 | + */ | ||
| 20 | +public class JacksonClient { | ||
| 21 | + | ||
| 22 | + public static void main(String[] args) throws Exception{ | ||
| 23 | + new JacksonClient("localhost", 8082).run(); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + private final String host; | ||
| 27 | + private final int port; | ||
| 28 | + | ||
| 29 | + public JacksonClient(String host, int port){ | ||
| 30 | + this.host = host; | ||
| 31 | + this.port = port; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public void run() throws Exception{ | ||
| 35 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 36 | + try { | ||
| 37 | + Bootstrap bootstrap = new Bootstrap() | ||
| 38 | + .group(group) | ||
| 39 | + .channel(NioSocketChannel.class) | ||
| 40 | + .handler(new JacksonClientHandlerInitializer()); | ||
| 41 | + | ||
| 42 | + Channel channel = bootstrap.connect(host, port).sync().channel(); | ||
| 43 | + | ||
| 44 | + // 发送对象 | ||
| 45 | + JacksonBean user = new JacksonBean(); | ||
| 46 | + user.setAge(27); | ||
| 47 | + user.setName("waylau"); | ||
| 48 | + List<String> sons = new ArrayList<String>(); | ||
| 49 | + for (int i = 0;i <10; i++) { | ||
| 50 | + sons.add("Lucy"+i); | ||
| 51 | + sons.add("Lily"+i); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + user.setSons(sons); | ||
| 55 | + Map<String, String> addrs = new HashMap<String, String>(); | ||
| 56 | + for (int i = 0;i <10; i++) { | ||
| 57 | + addrs.put("001"+i, "18998366112"); | ||
| 58 | + addrs.put("002"+i, "15014965012"); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + user.setAddrs(addrs); | ||
| 62 | + channel.write(user); | ||
| 63 | + channel.flush(); | ||
| 64 | + | ||
| 65 | + // 等待连接关闭 | ||
| 66 | + channel.closeFuture().sync(); | ||
| 67 | + } catch (Exception e) { | ||
| 68 | + e.printStackTrace(); | ||
| 69 | + } finally { | ||
| 70 | + group.shutdownGracefully(); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 5 | + | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 说明:处理器 | ||
| 11 | + * | ||
| 12 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 13 | + */ | ||
| 14 | +public class JacksonClientHandler extends | ||
| 15 | + SimpleChannelInboundHandler<Object> { | ||
| 16 | + | ||
| 17 | + @Override | ||
| 18 | + protected void channelRead0(ChannelHandlerContext ctx, Object obj) | ||
| 19 | + throws Exception { | ||
| 20 | + String jsonString = ""; | ||
| 21 | + if (obj instanceof JacksonBean) { | ||
| 22 | + JacksonBean user = (JacksonBean) obj; | ||
| 23 | + | ||
| 24 | + jsonString = JacksonMapper.getInstance().writeValueAsString(user); // 对象转为json字符串 | ||
| 25 | + } else { | ||
| 26 | + jsonString = JacksonMapper.getInstance().writeValueAsString(obj); // 对象转为json字符串 | ||
| 27 | + } | ||
| 28 | + System.out.println("Client get msg form Server -" + jsonString); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelInitializer; | ||
| 5 | +import io.netty.channel.ChannelPipeline; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 说明:处理器初始化 | ||
| 9 | + * | ||
| 10 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 11 | + */ | ||
| 12 | +public class JacksonClientHandlerInitializer extends | ||
| 13 | + ChannelInitializer<Channel> { | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + @Override | ||
| 17 | + protected void initChannel(Channel ch) throws Exception { | ||
| 18 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 19 | + pipeline.addLast(new JacksonDecoder<JacksonBean>(JacksonBean.class)); | ||
| 20 | + pipeline.addLast(new JacksonEncoder()); | ||
| 21 | + pipeline.addLast(new JacksonClientHandler()); | ||
| 22 | + } | ||
| 23 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import java.util.List; | ||
| 4 | + | ||
| 5 | +import io.netty.buffer.ByteBuf; | ||
| 6 | +import io.netty.buffer.ByteBufInputStream; | ||
| 7 | +import io.netty.channel.ChannelHandlerContext; | ||
| 8 | +import io.netty.handler.codec.ByteToMessageDecoder; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 说明:Jackson json 解码器 | ||
| 12 | + * | ||
| 13 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日 | ||
| 14 | + */ | ||
| 15 | +public class JacksonDecoder<T> extends ByteToMessageDecoder { | ||
| 16 | + | ||
| 17 | + private final Class<T> clazz; | ||
| 18 | + /** | ||
| 19 | + * | ||
| 20 | + */ | ||
| 21 | + public JacksonDecoder(Class<T> clazz) { | ||
| 22 | + this.clazz = clazz; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + /* | ||
| 26 | + * (non-Javadoc) | ||
| 27 | + * | ||
| 28 | + * @see io.netty.handler.codec.ByteToMessageDecoder#decode(io.netty.channel. | ||
| 29 | + * ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List) | ||
| 30 | + */ | ||
| 31 | + @Override | ||
| 32 | + protected void decode(ChannelHandlerContext ctx, ByteBuf in, | ||
| 33 | + List<Object> out) throws Exception { | ||
| 34 | + ByteBufInputStream byteBufInputStream = new ByteBufInputStream(in); | ||
| 35 | + out.add(JacksonMapper.getInstance().readValue(byteBufInputStream, clazz)); | ||
| 36 | + | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 8 | + | ||
| 9 | +import io.netty.buffer.ByteBuf; | ||
| 10 | +import io.netty.buffer.ByteBufOutputStream; | ||
| 11 | +import io.netty.channel.ChannelHandlerContext; | ||
| 12 | +import io.netty.handler.codec.MessageToByteEncoder; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 说明:Jackson json 编码器 | ||
| 16 | + * | ||
| 17 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日 | ||
| 18 | + */ | ||
| 19 | +public class JacksonEncoder extends MessageToByteEncoder<Object> { | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) | ||
| 23 | + throws Exception { | ||
| 24 | + | ||
| 25 | + ObjectMapper mapper = JacksonMapper.getInstance(); // create once, reuse | ||
| 26 | +// byte[] body = mapper.writeValueAsBytes(msg); // 将对象转换为byte | ||
| 27 | +// out.writeBytes(body); // 消息体中包含我们要发送的数据 | ||
| 28 | + ByteBufOutputStream byteBufOutputStream = new ByteBufOutputStream(out); | ||
| 29 | + mapper.writeValue(byteBufOutputStream, msg); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | +} | ||
| 33 | + |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 说明:ObjectMapper 单例。create once, reuse | ||
| 7 | + * | ||
| 8 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日 | ||
| 9 | + */ | ||
| 10 | +public class JacksonMapper { | ||
| 11 | + | ||
| 12 | + private static final ObjectMapper MAPPER = new ObjectMapper(); | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * create once, reuse | ||
| 16 | + * @return ObjectMapper 单例 | ||
| 17 | + */ | ||
| 18 | + public static ObjectMapper getInstance() { | ||
| 19 | + | ||
| 20 | + return MAPPER; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelOption; | ||
| 6 | +import io.netty.channel.EventLoopGroup; | ||
| 7 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 8 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 9 | +import io.netty.handler.logging.LogLevel; | ||
| 10 | +import io.netty.handler.logging.LoggingHandler; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 说明:序列化服务器 | ||
| 14 | + * | ||
| 15 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 16 | + */ | ||
| 17 | +public final class JacksonServer { | ||
| 18 | + | ||
| 19 | + static final int PORT = 8082; | ||
| 20 | + | ||
| 21 | + public static void main(String[] args) throws Exception { | ||
| 22 | + | ||
| 23 | + // Configure the server. | ||
| 24 | + EventLoopGroup bossGroup = new NioEventLoopGroup(1); | ||
| 25 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 26 | + try { | ||
| 27 | + ServerBootstrap b = new ServerBootstrap(); | ||
| 28 | + b.group(bossGroup, workerGroup) | ||
| 29 | + .channel(NioServerSocketChannel.class) | ||
| 30 | + .option(ChannelOption.SO_BACKLOG, 100) | ||
| 31 | + .childOption(ChannelOption.SO_KEEPALIVE, true) | ||
| 32 | + .handler(new LoggingHandler(LogLevel.INFO)) | ||
| 33 | + .childHandler(new JacksonServerHandlerInitializer()); | ||
| 34 | + | ||
| 35 | + // Start the server. | ||
| 36 | + ChannelFuture f = b.bind(PORT).sync(); | ||
| 37 | + | ||
| 38 | + // Wait until the server socket is closed. | ||
| 39 | + f.channel().closeFuture().sync(); | ||
| 40 | + } finally { | ||
| 41 | + // Shut down all event loops to terminate all threads. | ||
| 42 | + bossGroup.shutdownGracefully(); | ||
| 43 | + workerGroup.shutdownGracefully(); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 说明:处理器 | ||
| 9 | + * | ||
| 10 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 11 | + */ | ||
| 12 | +public class JacksonServerHandler extends SimpleChannelInboundHandler<Object> { | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + protected void channelRead0(ChannelHandlerContext ctx, Object obj) | ||
| 17 | + throws Exception { | ||
| 18 | + String jsonString = ""; | ||
| 19 | + if (obj instanceof JacksonBean) { | ||
| 20 | + JacksonBean user = (JacksonBean)obj; | ||
| 21 | + | ||
| 22 | + ctx.writeAndFlush(user); | ||
| 23 | + | ||
| 24 | + jsonString = JacksonMapper.getInstance().writeValueAsString(user); // 对象转为json字符串 | ||
| 25 | + | ||
| 26 | + } else { | ||
| 27 | + ctx.writeAndFlush(obj); | ||
| 28 | + jsonString = JacksonMapper.getInstance().writeValueAsString(obj); // 对象转为json字符串 | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + System.out.println("Server get msg form Client -" + jsonString); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + @Override | ||
| 35 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 36 | + Channel incoming = ctx.channel(); | ||
| 37 | + System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常"); | ||
| 38 | + // 当出现异常就关闭连接 | ||
| 39 | + cause.printStackTrace(); | ||
| 40 | + ctx.close(); | ||
| 41 | + } | ||
| 42 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelInitializer; | ||
| 5 | +import io.netty.channel.ChannelPipeline; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 说明:序列化服务器初始化 | ||
| 9 | + * | ||
| 10 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 11 | + */ | ||
| 12 | +public class JacksonServerHandlerInitializer extends | ||
| 13 | + ChannelInitializer<Channel> { | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + @Override | ||
| 17 | + protected void initChannel(Channel ch) throws Exception { | ||
| 18 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 19 | + pipeline.addLast(new JacksonDecoder<JacksonBean>(JacksonBean.class)); | ||
| 20 | + pipeline.addLast(new JacksonEncoder()); | ||
| 21 | + pipeline.addLast(new JacksonServerHandler()); | ||
| 22 | + } | ||
| 23 | +} |
| 1 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 2 | + | ||
| 3 | +import java.io.File; | ||
| 4 | +import java.io.IOException; | ||
| 5 | + | ||
| 6 | +import com.fasterxml.jackson.core.JsonEncoding; | ||
| 7 | +import com.fasterxml.jackson.core.JsonFactory; | ||
| 8 | +import com.fasterxml.jackson.core.JsonGenerationException; | ||
| 9 | +import com.fasterxml.jackson.core.JsonGenerator; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 说明: | ||
| 13 | + * | ||
| 14 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月8日 | ||
| 15 | + */ | ||
| 16 | +public class JsonGeneratorDemo { | ||
| 17 | + | ||
| 18 | + private static final String FILE_PATH = "d:\\user.json"; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * | ||
| 22 | + */ | ||
| 23 | + public JsonGeneratorDemo() { | ||
| 24 | + // TODO Auto-generated constructor stub | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * @param args | ||
| 29 | + * @throws IOException | ||
| 30 | + */ | ||
| 31 | + public static void main(String[] args) throws IOException { | ||
| 32 | + try { | ||
| 33 | + | ||
| 34 | + JsonFactory jfactory = new JsonFactory(); | ||
| 35 | + | ||
| 36 | + JsonGenerator jGenerator = jfactory.createJsonGenerator(new File( | ||
| 37 | + FILE_PATH), JsonEncoding.UTF8); | ||
| 38 | + jGenerator.writeStartObject(); // { | ||
| 39 | + | ||
| 40 | + jGenerator.writeStringField("name", "mkyong"); // "name" : "mkyong" | ||
| 41 | + jGenerator.writeNumberField("age", 29); // "age" : 29 | ||
| 42 | + | ||
| 43 | + jGenerator.writeFieldName("messages"); // "messages" : | ||
| 44 | + jGenerator.writeStartArray(); // [ | ||
| 45 | + | ||
| 46 | + jGenerator.writeString("msg 1"); // "msg 1" | ||
| 47 | + jGenerator.writeString("msg 2"); // "msg 2" | ||
| 48 | + jGenerator.writeString("msg 3"); // "msg 3" | ||
| 49 | + | ||
| 50 | + jGenerator.writeEndArray(); // ] | ||
| 51 | + | ||
| 52 | + jGenerator.writeEndObject(); // } | ||
| 53 | + | ||
| 54 | + jGenerator.close(); | ||
| 55 | + | ||
| 56 | + } catch (JsonGenerationException e) { | ||
| 57 | + e.printStackTrace(); | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.codec.jackcon; | ||
| 5 | + | ||
| 6 | +import java.io.File; | ||
| 7 | +import java.io.IOException; | ||
| 8 | + | ||
| 9 | +import com.fasterxml.jackson.core.JsonFactory; | ||
| 10 | +import com.fasterxml.jackson.core.JsonParseException; | ||
| 11 | +import com.fasterxml.jackson.core.JsonParser; | ||
| 12 | +import com.fasterxml.jackson.core.JsonToken; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 说明: | ||
| 16 | + * | ||
| 17 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月8日 | ||
| 18 | + */ | ||
| 19 | +public class JsonParserDemo { | ||
| 20 | + | ||
| 21 | + private static final String FILE_PATH = "d:\\user.json"; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | + public JsonParserDemo() { | ||
| 27 | + // TODO Auto-generated constructor stub | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * @param args | ||
| 32 | + * @throws IOException | ||
| 33 | + * @throws JsonParseException | ||
| 34 | + */ | ||
| 35 | + public static void main(String[] args) throws JsonParseException, | ||
| 36 | + IOException { | ||
| 37 | + | ||
| 38 | + JsonFactory jfactory = new JsonFactory(); | ||
| 39 | + | ||
| 40 | + JsonParser jParser = jfactory.createJsonParser(new File(FILE_PATH)); | ||
| 41 | + | ||
| 42 | + // loop until token equal to "}" | ||
| 43 | + while (jParser.nextToken() != JsonToken.END_OBJECT) { | ||
| 44 | + | ||
| 45 | + String fieldname = jParser.getCurrentName(); | ||
| 46 | + if ("name".equals(fieldname)) { | ||
| 47 | + | ||
| 48 | + // current token is "name", | ||
| 49 | + // move to next, which is "name"'s value | ||
| 50 | + jParser.nextToken(); | ||
| 51 | + System.out.println(jParser.getText()); // display mkyong | ||
| 52 | + | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + if ("age".equals(fieldname)) { | ||
| 56 | + | ||
| 57 | + // current token is "age", | ||
| 58 | + // move to next, which is "name"'s value | ||
| 59 | + jParser.nextToken(); | ||
| 60 | + System.out.println(jParser.getIntValue()); // display 29 | ||
| 61 | + | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + if ("messages".equals(fieldname)) { | ||
| 65 | + | ||
| 66 | + jParser.nextToken(); // current token is "[", move next | ||
| 67 | + | ||
| 68 | + // messages is array, loop until token equal to "]" | ||
| 69 | + while (jParser.nextToken() != JsonToken.END_ARRAY) { | ||
| 70 | + | ||
| 71 | + // display msg1, msg2, msg3 | ||
| 72 | + System.out.println(jParser.getText()); | ||
| 73 | + | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + } | ||
| 79 | + jParser.close(); | ||
| 80 | + | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | +} |
| 1 | +package com.waylau.netty.demo.codec.serialization; | ||
| 2 | + | ||
| 3 | +import java.io.Serializable; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 说明:一个用户 POJO | ||
| 7 | + * | ||
| 8 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 9 | + */ | ||
| 10 | +public class SerializationBean implements Serializable{ | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * | ||
| 14 | + */ | ||
| 15 | + private static final long serialVersionUID = 3235432002462705915L; | ||
| 16 | + private int age; | ||
| 17 | + private String name; | ||
| 18 | + | ||
| 19 | + public int getAge() { | ||
| 20 | + return age; | ||
| 21 | + } | ||
| 22 | + public void setAge(int age) { | ||
| 23 | + this.age = age; | ||
| 24 | + } | ||
| 25 | + public String getName() { | ||
| 26 | + return name; | ||
| 27 | + } | ||
| 28 | + public void setName(String name) { | ||
| 29 | + this.name = name; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * | ||
| 34 | + */ | ||
| 35 | + public SerializationBean() { | ||
| 36 | + // TODO Auto-generated constructor stub | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | +} |
| 1 | +package com.waylau.netty.demo.codec.serialization; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.Bootstrap; | ||
| 4 | +import io.netty.channel.Channel; | ||
| 5 | +import io.netty.channel.EventLoopGroup; | ||
| 6 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 7 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 说明:序列化-客户端 | ||
| 12 | + * | ||
| 13 | + * @author <a href="https://waylau.com">waylau.com</a> 2015年11月7日 | ||
| 14 | + */ | ||
| 15 | +public class SerializationClient { | ||
| 16 | + | ||
| 17 | + public static void main(String[] args) throws Exception{ | ||
| 18 | + new SerializationClient("localhost", 8082).run(); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + private final String host; | ||
| 22 | + private final int port; | ||
| 23 | + | ||
| 24 | + public SerializationClient(String host, int port){ | ||
| 25 | + this.host = host; | ||
| 26 | + this.port = port; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public void run() throws Exception{ | ||
| 30 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 31 | + try { | ||
| 32 | + Bootstrap bootstrap = new Bootstrap() | ||
| 33 | + .group(group) | ||
| 34 | + .channel(NioSocketChannel.class) | ||
| 35 | + .handler(new SerializationClientHandlerInitializer()); | ||
| 36 | + | ||
| 37 | + Channel channel = bootstrap.connect(host, port).sync().channel(); | ||
| 38 | + | ||
| 39 | + SerializationBean user = new SerializationBean(); | ||
| 40 | + | ||
| 41 | + for (int i = 0; i < 100; i++) { | ||
| 42 | + user = new SerializationBean(); | ||
| 43 | + user.setAge(i); | ||
| 44 | + user.setName("waylau"); | ||
| 45 | + channel.write(user); | ||
| 46 | + } | ||
| 47 | + channel.flush(); | ||
| 48 | + | ||
| 49 | + // 等待连接关闭 | ||
| 50 | + channel.closeFuture().sync(); | ||
| 51 | + } catch (Exception e) { | ||
| 52 | + e.printStackTrace(); | ||
| 53 | + } finally { | ||
| 54 | + group.shutdownGracefully(); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.codec.serialization; | ||
| 5 | + | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 说明:处理器 | ||
| 11 | + * | ||
| 12 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 13 | + */ | ||
| 14 | +public class SerializationClientHandler extends | ||
| 15 | + SimpleChannelInboundHandler<Object> { | ||
| 16 | + | ||
| 17 | + @Override | ||
| 18 | + protected void channelRead0(ChannelHandlerContext ctx, Object obj) | ||
| 19 | + throws Exception { | ||
| 20 | + if (obj instanceof SerializationBean) { | ||
| 21 | + SerializationBean user = (SerializationBean) obj; | ||
| 22 | + System.out.println("Client get msg form Server - name:" | ||
| 23 | + + user.getName() + ";age:" + user.getAge()); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | +} |
src/main/java/com/waylau/netty/demo/codec/serialization/SerializationClientHandlerInitializer.java
0 → 100644
| 1 | +package com.waylau.netty.demo.codec.serialization; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelInitializer; | ||
| 5 | +import io.netty.channel.ChannelPipeline; | ||
| 6 | +import io.netty.handler.codec.serialization.ClassResolvers; | ||
| 7 | +import io.netty.handler.codec.serialization.ObjectDecoder; | ||
| 8 | +import io.netty.handler.codec.serialization.ObjectEncoder; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 说明:处理器初始化 | ||
| 12 | + * | ||
| 13 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 14 | + */ | ||
| 15 | +public class SerializationClientHandlerInitializer extends | ||
| 16 | + ChannelInitializer<Channel> { | ||
| 17 | + | ||
| 18 | + private final static int MAX_OBJECT_SIZE = 1024 * 1024; | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + protected void initChannel(Channel ch) throws Exception { | ||
| 22 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 23 | + pipeline.addLast(new ObjectDecoder(MAX_OBJECT_SIZE, | ||
| 24 | + ClassResolvers.weakCachingConcurrentResolver(this.getClass() | ||
| 25 | + .getClassLoader()))); | ||
| 26 | + pipeline.addLast(new ObjectEncoder()); | ||
| 27 | + pipeline.addLast(new SerializationClientHandler()); | ||
| 28 | + } | ||
| 29 | +} |
| 1 | +package com.waylau.netty.demo.codec.serialization; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelOption; | ||
| 6 | +import io.netty.channel.EventLoopGroup; | ||
| 7 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 8 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 9 | +import io.netty.handler.logging.LogLevel; | ||
| 10 | +import io.netty.handler.logging.LoggingHandler; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 说明:序列化服务器 | ||
| 14 | + * | ||
| 15 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 16 | + */ | ||
| 17 | +public final class SerializationServer { | ||
| 18 | + | ||
| 19 | + static final int PORT = 8082; | ||
| 20 | + | ||
| 21 | + public static void main(String[] args) throws Exception { | ||
| 22 | + | ||
| 23 | + // Configure the server. | ||
| 24 | + EventLoopGroup bossGroup = new NioEventLoopGroup(1); | ||
| 25 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 26 | + try { | ||
| 27 | + ServerBootstrap b = new ServerBootstrap(); | ||
| 28 | + b.group(bossGroup, workerGroup) | ||
| 29 | + .channel(NioServerSocketChannel.class) | ||
| 30 | + .option(ChannelOption.SO_BACKLOG, 100) | ||
| 31 | + .childOption(ChannelOption.SO_KEEPALIVE, true) | ||
| 32 | + .handler(new LoggingHandler(LogLevel.INFO)) | ||
| 33 | + .childHandler(new SerializationServerHandlerInitializer()); | ||
| 34 | + | ||
| 35 | + // Start the server. | ||
| 36 | + ChannelFuture f = b.bind(PORT).sync(); | ||
| 37 | + | ||
| 38 | + // Wait until the server socket is closed. | ||
| 39 | + f.channel().closeFuture().sync(); | ||
| 40 | + } finally { | ||
| 41 | + // Shut down all event loops to terminate all threads. | ||
| 42 | + bossGroup.shutdownGracefully(); | ||
| 43 | + workerGroup.shutdownGracefully(); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | +} |
| 1 | +package com.waylau.netty.demo.codec.serialization; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelHandlerContext; | ||
| 4 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 说明:处理器 | ||
| 8 | + * | ||
| 9 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 10 | + */ | ||
| 11 | +public class SerializationServerHandler extends SimpleChannelInboundHandler<Object> { | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + @Override | ||
| 15 | + protected void channelRead0(ChannelHandlerContext ctx, Object obj) | ||
| 16 | + throws Exception { | ||
| 17 | + if (obj instanceof SerializationBean) { | ||
| 18 | + SerializationBean user = (SerializationBean)obj; | ||
| 19 | + ctx.writeAndFlush(user); | ||
| 20 | + System.out.println("Server get msg form Client - name:"+ user.getName() + ";age:" + user.getAge()); | ||
| 21 | + } | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | +} |
src/main/java/com/waylau/netty/demo/codec/serialization/SerializationServerHandlerInitializer.java
0 → 100644
| 1 | +package com.waylau.netty.demo.codec.serialization; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelInitializer; | ||
| 5 | +import io.netty.channel.ChannelPipeline; | ||
| 6 | +import io.netty.handler.codec.serialization.ClassResolvers; | ||
| 7 | +import io.netty.handler.codec.serialization.ObjectDecoder; | ||
| 8 | +import io.netty.handler.codec.serialization.ObjectEncoder; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 说明:序列化服务器初始化 | ||
| 12 | + * | ||
| 13 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 14 | + */ | ||
| 15 | +public class SerializationServerHandlerInitializer extends | ||
| 16 | + ChannelInitializer<Channel> { | ||
| 17 | + | ||
| 18 | + private final static int MAX_OBJECT_SIZE = 1024 * 1024; | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + protected void initChannel(Channel ch) throws Exception { | ||
| 22 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 23 | + pipeline.addLast(new ObjectDecoder(MAX_OBJECT_SIZE, | ||
| 24 | + ClassResolvers.weakCachingConcurrentResolver(this.getClass() | ||
| 25 | + .getClassLoader()))); | ||
| 26 | + pipeline.addLast(new ObjectEncoder()); | ||
| 27 | + pipeline.addLast(new SerializationServerHandler()); | ||
| 28 | + } | ||
| 29 | +} |
| 1 | +package com.waylau.netty.demo.discard; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | + | ||
| 5 | +import io.netty.channel.ChannelFuture; | ||
| 6 | +import io.netty.channel.ChannelInitializer; | ||
| 7 | +import io.netty.channel.ChannelOption; | ||
| 8 | +import io.netty.channel.EventLoopGroup; | ||
| 9 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 10 | +import io.netty.channel.socket.SocketChannel; | ||
| 11 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 丢弃任何进入的数据 | ||
| 15 | + */ | ||
| 16 | +public class DiscardServer { | ||
| 17 | + | ||
| 18 | + private int port; | ||
| 19 | + | ||
| 20 | + public DiscardServer(int port) { | ||
| 21 | + this.port = port; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public void run() throws Exception { | ||
| 25 | + EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) | ||
| 26 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 27 | + try { | ||
| 28 | + ServerBootstrap b = new ServerBootstrap(); // (2) | ||
| 29 | + b.group(bossGroup, workerGroup) | ||
| 30 | + .channel(NioServerSocketChannel.class) // (3) | ||
| 31 | + .childHandler(new ChannelInitializer<SocketChannel>() { // (4) | ||
| 32 | + @Override | ||
| 33 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 34 | + ch.pipeline().addLast(new DiscardServerHandler()); | ||
| 35 | + } | ||
| 36 | + }) | ||
| 37 | + .option(ChannelOption.SO_BACKLOG, 128) // (5) | ||
| 38 | + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) | ||
| 39 | + | ||
| 40 | + // 绑定端口,开始接收进来的连接 | ||
| 41 | + ChannelFuture f = b.bind(port).sync(); // (7) | ||
| 42 | + | ||
| 43 | + // 等待服务器 socket 关闭 。 | ||
| 44 | + // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 | ||
| 45 | + f.channel().closeFuture().sync(); | ||
| 46 | + } finally { | ||
| 47 | + workerGroup.shutdownGracefully(); | ||
| 48 | + bossGroup.shutdownGracefully(); | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + public static void main(String[] args) throws Exception { | ||
| 53 | + int port; | ||
| 54 | + if (args.length > 0) { | ||
| 55 | + port = Integer.parseInt(args[0]); | ||
| 56 | + } else { | ||
| 57 | + port = 8080; | ||
| 58 | + } | ||
| 59 | + new DiscardServer(port).run(); | ||
| 60 | + } | ||
| 61 | +} |
| 1 | +package com.waylau.netty.demo.discard; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.channel.ChannelInboundHandlerAdapter; | ||
| 6 | +import io.netty.util.ReferenceCountUtil; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 处理服务端 channel. | ||
| 10 | + */ | ||
| 11 | +public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1) | ||
| 12 | + | ||
| 13 | + @Override | ||
| 14 | + public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2) | ||
| 15 | + /* | ||
| 16 | + // 默默地丢弃收到的数据 | ||
| 17 | + ((ByteBuf) msg).release(); // (3) | ||
| 18 | + */ | ||
| 19 | + | ||
| 20 | + /* | ||
| 21 | + try { | ||
| 22 | + // Do something with msg | ||
| 23 | + } finally { | ||
| 24 | + ReferenceCountUtil.release(msg); | ||
| 25 | + } | ||
| 26 | + */ | ||
| 27 | + | ||
| 28 | + ByteBuf in = (ByteBuf) msg; | ||
| 29 | + try { | ||
| 30 | + while (in.isReadable()) { // (1) | ||
| 31 | + System.out.print((char) in.readByte()); | ||
| 32 | + System.out.flush(); | ||
| 33 | + } | ||
| 34 | + } finally { | ||
| 35 | + ReferenceCountUtil.release(msg); // (2) | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4) | ||
| 42 | + // 当出现异常就关闭连接 | ||
| 43 | + cause.printStackTrace(); | ||
| 44 | + ctx.close(); | ||
| 45 | + } | ||
| 46 | +} |
| 1 | +package com.waylau.netty.demo.echo; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.Bootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelInitializer; | ||
| 6 | +import io.netty.channel.ChannelOption; | ||
| 7 | +import io.netty.channel.ChannelPipeline; | ||
| 8 | +import io.netty.channel.EventLoopGroup; | ||
| 9 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 10 | +import io.netty.channel.socket.SocketChannel; | ||
| 11 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 12 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 13 | +import io.netty.handler.codec.Delimiters; | ||
| 14 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 15 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * Sends one message when a connection is open and echoes back any received | ||
| 19 | + * data to the server. Simply put, the echo client initiates the ping-pong | ||
| 20 | + * traffic between the echo client and server by sending the first message to | ||
| 21 | + * the server. | ||
| 22 | + */ | ||
| 23 | +public final class EchoClient { | ||
| 24 | + | ||
| 25 | + static final String HOST = System.getProperty("host", "127.0.0.1"); | ||
| 26 | + static final int PORT = Integer.parseInt(System.getProperty("port", "8040")); | ||
| 27 | + | ||
| 28 | + public static void main(String[] args) throws Exception { | ||
| 29 | + | ||
| 30 | + // Configure the client. | ||
| 31 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 32 | + try { | ||
| 33 | + Bootstrap b = new Bootstrap(); | ||
| 34 | + b.group(group) | ||
| 35 | + .channel(NioSocketChannel.class) | ||
| 36 | + .option(ChannelOption.TCP_NODELAY, true) | ||
| 37 | + .handler(new ChannelInitializer<SocketChannel>() { | ||
| 38 | + @Override | ||
| 39 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 40 | + ChannelPipeline p = ch.pipeline(); | ||
| 41 | + p.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 42 | + p.addLast("decoder", new StringDecoder()); | ||
| 43 | + p.addLast("encoder", new StringEncoder()); | ||
| 44 | + p.addLast(new EchoClientHandler()); | ||
| 45 | + } | ||
| 46 | + }); | ||
| 47 | + | ||
| 48 | + // Start the client. | ||
| 49 | + ChannelFuture f = b.connect(HOST, PORT).sync(); | ||
| 50 | + | ||
| 51 | + // Wait until the connection is closed. | ||
| 52 | + f.channel().closeFuture().sync(); | ||
| 53 | + } finally { | ||
| 54 | + // Shut down the event loop to terminate all threads. | ||
| 55 | + group.shutdownGracefully(); | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | +} |
| 1 | +package com.waylau.netty.demo.echo; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +import io.netty.buffer.ByteBuf; | ||
| 6 | +import io.netty.buffer.Unpooled; | ||
| 7 | +import io.netty.channel.ChannelHandlerContext; | ||
| 8 | +import io.netty.channel.ChannelInboundHandlerAdapter; | ||
| 9 | + | ||
| 10 | +public class EchoClientHandler extends ChannelInboundHandlerAdapter { | ||
| 11 | + | ||
| 12 | + private final String firstMessage; | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * Creates a client-side handler. | ||
| 16 | + */ | ||
| 17 | + public EchoClientHandler() { | ||
| 18 | +// firstMessage = Unpooled.buffer(EchoClient.SIZE); | ||
| 19 | +// for (int i = 0; i < firstMessage.capacity(); i ++) { | ||
| 20 | +// firstMessage.writeByte((byte) i); | ||
| 21 | +// } | ||
| 22 | +//// firstMessage.writeByte('\n'); | ||
| 23 | + firstMessage = "hello\n"; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public void channelActive(ChannelHandlerContext ctx) { | ||
| 28 | + ctx.writeAndFlush(firstMessage); | ||
| 29 | + System.out.println("channel active."); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public void channelRead(ChannelHandlerContext ctx, Object msg) { | ||
| 34 | + System.out.println("channel read from server: " + msg); | ||
| 35 | + ctx.write(msg + "\n"); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + public void channelReadComplete(ChannelHandlerContext ctx) { | ||
| 40 | + ctx.flush(); | ||
| 41 | + System.out.println("channel read complete"); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @Override | ||
| 45 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 46 | + // Close the connection when an exception is raised. | ||
| 47 | + cause.printStackTrace(); | ||
| 48 | + ctx.close(); | ||
| 49 | + } | ||
| 50 | + } |
| 1 | +package com.waylau.netty.demo.echo; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelInitializer; | ||
| 6 | +import io.netty.channel.ChannelOption; | ||
| 7 | +import io.netty.channel.EventLoopGroup; | ||
| 8 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 9 | +import io.netty.channel.socket.SocketChannel; | ||
| 10 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 11 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 12 | +import io.netty.handler.codec.Delimiters; | ||
| 13 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 14 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * 应答服务器 | ||
| 18 | + */ | ||
| 19 | +public class EchoServer { | ||
| 20 | + | ||
| 21 | + private int port; | ||
| 22 | + | ||
| 23 | + public EchoServer(int port) { | ||
| 24 | + this.port = port; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public void run() throws Exception { | ||
| 28 | + EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) | ||
| 29 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 30 | + try { | ||
| 31 | + ServerBootstrap b = new ServerBootstrap(); // (2) | ||
| 32 | + b.group(bossGroup, workerGroup) | ||
| 33 | + .channel(NioServerSocketChannel.class) // (3) | ||
| 34 | + .childHandler(new ChannelInitializer<SocketChannel>() { // (4) | ||
| 35 | + @Override | ||
| 36 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 37 | + ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 38 | + ch.pipeline().addLast("decoder", new StringDecoder()); | ||
| 39 | + ch.pipeline().addLast("encoder", new StringEncoder()); | ||
| 40 | + ch.pipeline().addLast(new EchoServerHandler()); | ||
| 41 | + } | ||
| 42 | + }) | ||
| 43 | + .option(ChannelOption.SO_BACKLOG, 128) // (5) | ||
| 44 | + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) | ||
| 45 | + | ||
| 46 | + // 绑定端口,开始接收进来的连接 | ||
| 47 | + ChannelFuture f = b.bind(port).sync(); // (7) | ||
| 48 | + | ||
| 49 | + System.out.println("Server start listen at " + port ); | ||
| 50 | + // 等待服务器 socket 关闭 。 | ||
| 51 | + // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 | ||
| 52 | + f.channel().closeFuture().sync(); | ||
| 53 | + } finally { | ||
| 54 | + workerGroup.shutdownGracefully(); | ||
| 55 | + bossGroup.shutdownGracefully(); | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public static void main(String[] args) throws Exception { | ||
| 60 | + int port; | ||
| 61 | + if (args.length > 0) { | ||
| 62 | + port = Integer.parseInt(args[0]); | ||
| 63 | + } else { | ||
| 64 | + port = 8040; | ||
| 65 | + } | ||
| 66 | + new EchoServer(port).run(); | ||
| 67 | + } | ||
| 68 | +} |
| 1 | +package com.waylau.netty.demo.echo; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelFuture; | ||
| 4 | +import io.netty.channel.ChannelFutureListener; | ||
| 5 | +import io.netty.channel.ChannelHandlerContext; | ||
| 6 | +import io.netty.channel.ChannelInboundHandlerAdapter; | ||
| 7 | + | ||
| 8 | +import java.util.concurrent.TimeUnit; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 处理服务端 channel. | ||
| 12 | + */ | ||
| 13 | +public class EchoServerHandler extends ChannelInboundHandlerAdapter { | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + public void channelRead(ChannelHandlerContext ctx, Object msg) { | ||
| 17 | + System.out.println(ctx.channel().remoteAddress()+"->Server :"+ msg.toString()); | ||
| 18 | +// ctx.write(msg); // (1) | ||
| 19 | +// ctx.flush(); // (2) | ||
| 20 | +// final ChannelFuture future = ctx.writeAndFlush(msg); | ||
| 21 | + final ChannelFuture future = ctx.write(msg + "\n"); | ||
| 22 | +// future.addListener(ChannelFutureListener.CLOSE); | ||
| 23 | +// future.addListener(new ChannelFutureListener() { | ||
| 24 | +// @Override | ||
| 25 | +// public void operationComplete(ChannelFuture future) throws Exception { | ||
| 26 | +// System.out.println("close channel"); | ||
| 27 | +// future.channel().close(); | ||
| 28 | +// } | ||
| 29 | +// }); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { | ||
| 34 | + System.out.println("server read complete"); | ||
| 35 | + ctx.flush(); | ||
| 36 | + TimeUnit.MILLISECONDS.sleep(200); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + @Override | ||
| 40 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 41 | + // 当出现异常就关闭连接 | ||
| 42 | + cause.printStackTrace(); | ||
| 43 | + ctx.close(); | ||
| 44 | + } | ||
| 45 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.handler.codec.ByteToMessageDecoder; | ||
| 6 | +import io.netty.handler.codec.CorruptedFrameException; | ||
| 7 | + | ||
| 8 | +import java.math.BigInteger; | ||
| 9 | +import java.util.List; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Decodes the binary representation of a {@link BigInteger} prepended | ||
| 13 | + * with a magic number ('F' or 0x46) and a 32-bit integer length prefix into a | ||
| 14 | + * {@link BigInteger} instance. For example, { 'F', 0, 0, 0, 1, 42 } will be | ||
| 15 | + * decoded into new BigInteger("42"). | ||
| 16 | + */ | ||
| 17 | +public class BigIntegerDecoder extends ByteToMessageDecoder { | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { | ||
| 21 | + // Wait until the length prefix is available. | ||
| 22 | + if (in.readableBytes() < 5) { | ||
| 23 | + return; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + in.markReaderIndex(); | ||
| 27 | + | ||
| 28 | + // Check the magic number. | ||
| 29 | + int magicNumber = in.readUnsignedByte(); | ||
| 30 | + if (magicNumber != 'F') { | ||
| 31 | + in.resetReaderIndex(); | ||
| 32 | + throw new CorruptedFrameException("Invalid magic number: " + magicNumber); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + // Wait until the whole data is available. | ||
| 36 | + int dataLength = in.readInt(); | ||
| 37 | + if (in.readableBytes() < dataLength) { | ||
| 38 | + in.resetReaderIndex(); | ||
| 39 | + return; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + // Convert the received data into a new BigInteger. | ||
| 43 | + byte[] decoded = new byte[dataLength]; | ||
| 44 | + in.readBytes(decoded); | ||
| 45 | + | ||
| 46 | + out.add(new BigInteger(decoded)); | ||
| 47 | + } | ||
| 48 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.Bootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.EventLoopGroup; | ||
| 6 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 7 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 8 | +import io.netty.handler.ssl.SslContext; | ||
| 9 | +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Sends a sequence of integers to a {@link FactorialServer} to calculate | ||
| 13 | + * the factorial of the specified integer. | ||
| 14 | + */ | ||
| 15 | +public final class FactorialClient { | ||
| 16 | + | ||
| 17 | + static final boolean SSL = System.getProperty("ssl") != null; | ||
| 18 | + static final String HOST = System.getProperty("host", "127.0.0.1"); | ||
| 19 | + static final int PORT = Integer.parseInt(System.getProperty("port", "8322")); | ||
| 20 | + static final int COUNT = Integer.parseInt(System.getProperty("count", "1000")); | ||
| 21 | + | ||
| 22 | + public static void main(String[] args) throws Exception { | ||
| 23 | + // Configure SSL. | ||
| 24 | + final SslContext sslCtx; | ||
| 25 | + if (SSL) { | ||
| 26 | + sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); | ||
| 27 | + } else { | ||
| 28 | + sslCtx = null; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 32 | + try { | ||
| 33 | + Bootstrap b = new Bootstrap(); | ||
| 34 | + b.group(group) | ||
| 35 | + .channel(NioSocketChannel.class) | ||
| 36 | + .handler(new FactorialClientInitializer(sslCtx)); | ||
| 37 | + | ||
| 38 | + // Make a new connection. | ||
| 39 | + ChannelFuture f = b.connect(HOST, PORT).sync(); | ||
| 40 | + | ||
| 41 | + // Get the handler instance to retrieve the answer. | ||
| 42 | + FactorialClientHandler handler = | ||
| 43 | + (FactorialClientHandler) f.channel().pipeline().last(); | ||
| 44 | + | ||
| 45 | + // Print out the answer. | ||
| 46 | + System.err.format("Factorial of %,d is: %,d", COUNT, handler.getFactorial()); | ||
| 47 | + } finally { | ||
| 48 | + group.shutdownGracefully(); | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelFuture; | ||
| 4 | +import io.netty.channel.ChannelFutureListener; | ||
| 5 | +import io.netty.channel.ChannelHandlerContext; | ||
| 6 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 7 | + | ||
| 8 | +import java.math.BigInteger; | ||
| 9 | +import java.util.concurrent.BlockingQueue; | ||
| 10 | +import java.util.concurrent.LinkedBlockingQueue; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Handler for a client-side channel. This handler maintains stateful | ||
| 14 | + * information which is specific to a certain channel using member variables. | ||
| 15 | + * Therefore, an instance of this handler can cover only one channel. You have | ||
| 16 | + * to create a new handler instance whenever you create a new channel and insert | ||
| 17 | + * this handler to avoid a race condition. | ||
| 18 | + */ | ||
| 19 | +public class FactorialClientHandler extends SimpleChannelInboundHandler<BigInteger> { | ||
| 20 | + | ||
| 21 | + private ChannelHandlerContext ctx; | ||
| 22 | + private int receivedMessages; | ||
| 23 | + private int next = 1; | ||
| 24 | + final BlockingQueue<BigInteger> answer = new LinkedBlockingQueue<BigInteger>(); | ||
| 25 | + | ||
| 26 | + public BigInteger getFactorial() { | ||
| 27 | + boolean interrupted = false; | ||
| 28 | + try { | ||
| 29 | + for (;;) { | ||
| 30 | + try { | ||
| 31 | + return answer.take(); | ||
| 32 | + } catch (InterruptedException ignore) { | ||
| 33 | + interrupted = true; | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | + } finally { | ||
| 37 | + if (interrupted) { | ||
| 38 | + Thread.currentThread().interrupt(); | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Override | ||
| 44 | + public void channelActive(ChannelHandlerContext ctx) { | ||
| 45 | + this.ctx = ctx; | ||
| 46 | + sendNumbers(); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public void messageReceived(ChannelHandlerContext ctx, final BigInteger msg) { | ||
| 50 | + receivedMessages ++; | ||
| 51 | + if (receivedMessages == FactorialClient.COUNT) { | ||
| 52 | + // Offer the answer after closing the connection. | ||
| 53 | + ctx.channel().close().addListener(new ChannelFutureListener() { | ||
| 54 | + @Override | ||
| 55 | + public void operationComplete(ChannelFuture future) { | ||
| 56 | + boolean offered = answer.offer(msg); | ||
| 57 | + assert offered; | ||
| 58 | + } | ||
| 59 | + }); | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Override | ||
| 64 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 65 | + cause.printStackTrace(); | ||
| 66 | + ctx.close(); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + private void sendNumbers() { | ||
| 70 | + // Do not send more than 4096 numbers. | ||
| 71 | + ChannelFuture future = null; | ||
| 72 | + for (int i = 0; i < 4096 && next <= FactorialClient.COUNT; i++) { | ||
| 73 | + future = ctx.write(Integer.valueOf(next)); | ||
| 74 | + next++; | ||
| 75 | + } | ||
| 76 | + if (next <= FactorialClient.COUNT) { | ||
| 77 | + assert future != null; | ||
| 78 | + future.addListener(numberSender); | ||
| 79 | + } | ||
| 80 | + ctx.flush(); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + private final ChannelFutureListener numberSender = new ChannelFutureListener() { | ||
| 84 | + @Override | ||
| 85 | + public void operationComplete(ChannelFuture future) throws Exception { | ||
| 86 | + if (future.isSuccess()) { | ||
| 87 | + sendNumbers(); | ||
| 88 | + } else { | ||
| 89 | + future.cause().printStackTrace(); | ||
| 90 | + future.channel().close(); | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + }; | ||
| 94 | + | ||
| 95 | + @Override | ||
| 96 | + protected void channelRead0(ChannelHandlerContext arg0, BigInteger arg1) | ||
| 97 | + throws Exception { | ||
| 98 | + // TODO Auto-generated method stub | ||
| 99 | + | ||
| 100 | + } | ||
| 101 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.compression.ZlibCodecFactory; | ||
| 7 | +import io.netty.handler.codec.compression.ZlibWrapper; | ||
| 8 | +import io.netty.handler.ssl.SslContext; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Creates a newly configured {@link ChannelPipeline} for a client-side channel. | ||
| 12 | + */ | ||
| 13 | +public class FactorialClientInitializer extends ChannelInitializer<SocketChannel> { | ||
| 14 | + | ||
| 15 | + private final SslContext sslCtx; | ||
| 16 | + | ||
| 17 | + public FactorialClientInitializer(SslContext sslCtx) { | ||
| 18 | + this.sslCtx = sslCtx; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + public void initChannel(SocketChannel ch) { | ||
| 23 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 24 | + | ||
| 25 | + if (sslCtx != null) { | ||
| 26 | + pipeline.addLast(sslCtx.newHandler(ch.alloc(), FactorialClient.HOST, FactorialClient.PORT)); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + // Enable stream compression (you can remove these two if unnecessary) | ||
| 30 | + pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP)); | ||
| 31 | + pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); | ||
| 32 | + | ||
| 33 | + // Add the number codec first, | ||
| 34 | + pipeline.addLast(new BigIntegerDecoder()); | ||
| 35 | + pipeline.addLast(new NumberEncoder()); | ||
| 36 | + | ||
| 37 | + // and then business logic. | ||
| 38 | + pipeline.addLast(new FactorialClientHandler()); | ||
| 39 | + } | ||
| 40 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.EventLoopGroup; | ||
| 5 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 6 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 7 | +import io.netty.handler.logging.LogLevel; | ||
| 8 | +import io.netty.handler.logging.LoggingHandler; | ||
| 9 | +import io.netty.handler.ssl.SslContext; | ||
| 10 | +import io.netty.handler.ssl.util.SelfSignedCertificate; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Receives a sequence of integers from a {@link FactorialClient} to calculate | ||
| 14 | + * the factorial of the specified integer. | ||
| 15 | + */ | ||
| 16 | +public final class FactorialServer { | ||
| 17 | + | ||
| 18 | + static final boolean SSL = System.getProperty("ssl") != null; | ||
| 19 | + static final int PORT = Integer.parseInt(System.getProperty("port", "8322")); | ||
| 20 | + | ||
| 21 | + public static void main(String[] args) throws Exception { | ||
| 22 | + // Configure SSL. | ||
| 23 | + final SslContext sslCtx; | ||
| 24 | + if (SSL) { | ||
| 25 | + SelfSignedCertificate ssc = new SelfSignedCertificate(); | ||
| 26 | + sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); | ||
| 27 | + } else { | ||
| 28 | + sslCtx = null; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + EventLoopGroup bossGroup = new NioEventLoopGroup(1); | ||
| 32 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 33 | + try { | ||
| 34 | + ServerBootstrap b = new ServerBootstrap(); | ||
| 35 | + b.group(bossGroup, workerGroup) | ||
| 36 | + .channel(NioServerSocketChannel.class) | ||
| 37 | + .handler(new LoggingHandler(LogLevel.INFO)) | ||
| 38 | + .childHandler(new FactorialServerInitializer(sslCtx)); | ||
| 39 | + | ||
| 40 | + b.bind(PORT).sync().channel().closeFuture().sync(); | ||
| 41 | + } finally { | ||
| 42 | + bossGroup.shutdownGracefully(); | ||
| 43 | + workerGroup.shutdownGracefully(); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelHandlerContext; | ||
| 4 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 5 | + | ||
| 6 | +import java.math.BigInteger; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * Handler for a server-side channel. This handler maintains stateful | ||
| 10 | + * information which is specific to a certain channel using member variables. | ||
| 11 | + * Therefore, an instance of this handler can cover only one channel. You have | ||
| 12 | + * to create a new handler instance whenever you create a new channel and insert | ||
| 13 | + * this handler to avoid a race condition. | ||
| 14 | + */ | ||
| 15 | +public class FactorialServerHandler extends SimpleChannelInboundHandler<BigInteger> { | ||
| 16 | + | ||
| 17 | + private BigInteger lastMultiplier = new BigInteger("1"); | ||
| 18 | + private BigInteger factorial = new BigInteger("1"); | ||
| 19 | + | ||
| 20 | + public void messageReceived(ChannelHandlerContext ctx, BigInteger msg) { | ||
| 21 | + // Calculate the cumulative factorial and send it to the client. | ||
| 22 | + lastMultiplier = msg; | ||
| 23 | + factorial = factorial.multiply(msg); | ||
| 24 | + ctx.writeAndFlush(factorial); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { | ||
| 29 | + System.err.printf("Factorial of %,d is: %,d%n", lastMultiplier, factorial); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { | ||
| 34 | + cause.printStackTrace(); | ||
| 35 | + ctx.close(); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + protected void channelRead0(ChannelHandlerContext arg0, BigInteger arg1) | ||
| 40 | + throws Exception { | ||
| 41 | + // TODO Auto-generated method stub | ||
| 42 | + | ||
| 43 | + } | ||
| 44 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.compression.ZlibCodecFactory; | ||
| 7 | +import io.netty.handler.codec.compression.ZlibWrapper; | ||
| 8 | +import io.netty.handler.ssl.SslContext; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Creates a newly configured {@link ChannelPipeline} for a server-side channel. | ||
| 12 | + */ | ||
| 13 | +public class FactorialServerInitializer extends ChannelInitializer<SocketChannel> { | ||
| 14 | + | ||
| 15 | + private final SslContext sslCtx; | ||
| 16 | + | ||
| 17 | + public FactorialServerInitializer(SslContext sslCtx) { | ||
| 18 | + this.sslCtx = sslCtx; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + public void initChannel(SocketChannel ch) { | ||
| 23 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 24 | + | ||
| 25 | + if (sslCtx != null) { | ||
| 26 | + pipeline.addLast(sslCtx.newHandler(ch.alloc())); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + // Enable stream compression (you can remove these two if unnecessary) | ||
| 30 | + pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP)); | ||
| 31 | + pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); | ||
| 32 | + | ||
| 33 | + // Add the number codec first, | ||
| 34 | + pipeline.addLast(new BigIntegerDecoder()); | ||
| 35 | + pipeline.addLast(new NumberEncoder()); | ||
| 36 | + | ||
| 37 | + // and then business logic. | ||
| 38 | + // Please note we create a handler for every new channel | ||
| 39 | + // because it has stateful properties. | ||
| 40 | + pipeline.addLast(new FactorialServerHandler()); | ||
| 41 | + } | ||
| 42 | +} |
| 1 | +package com.waylau.netty.demo.factorial; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.handler.codec.MessageToByteEncoder; | ||
| 6 | + | ||
| 7 | +import java.math.BigInteger; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * Encodes a {@link Number} into the binary representation prepended with | ||
| 11 | + * a magic number ('F' or 0x46) and a 32-bit length prefix. For example, 42 | ||
| 12 | + * will be encoded to { 'F', 0, 0, 0, 1, 42 }. | ||
| 13 | + */ | ||
| 14 | +public class NumberEncoder extends MessageToByteEncoder<Number> { | ||
| 15 | + | ||
| 16 | + @Override | ||
| 17 | + protected void encode(ChannelHandlerContext ctx, Number msg, ByteBuf out) { | ||
| 18 | + // Convert to a BigInteger first for easier implementation. | ||
| 19 | + BigInteger v; | ||
| 20 | + if (msg instanceof BigInteger) { | ||
| 21 | + v = (BigInteger) msg; | ||
| 22 | + } else { | ||
| 23 | + v = new BigInteger(String.valueOf(msg)); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + // Convert the number into a byte array. | ||
| 27 | + byte[] data = v.toByteArray(); | ||
| 28 | + int dataLength = data.length; | ||
| 29 | + | ||
| 30 | + // Write a message. | ||
| 31 | + out.writeByte((byte) 'F'); // magic number | ||
| 32 | + out.writeInt(dataLength); // data length | ||
| 33 | + out.writeBytes(data); // data | ||
| 34 | + } | ||
| 35 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.file; | ||
| 5 | + | ||
| 6 | +import java.io.BufferedReader; | ||
| 7 | +import java.io.IOException; | ||
| 8 | +import java.io.InputStreamReader; | ||
| 9 | + | ||
| 10 | +import io.netty.bootstrap.Bootstrap; | ||
| 11 | +import io.netty.channel.Channel; | ||
| 12 | +import io.netty.channel.ChannelFuture; | ||
| 13 | +import io.netty.channel.ChannelInitializer; | ||
| 14 | +import io.netty.channel.ChannelOption; | ||
| 15 | +import io.netty.channel.EventLoopGroup; | ||
| 16 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 17 | +import io.netty.channel.socket.SocketChannel; | ||
| 18 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 19 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 20 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 21 | +import io.netty.util.CharsetUtil; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 说明:文件客户端 | ||
| 25 | + * | ||
| 26 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 27 | + */ | ||
| 28 | +public class FileClient { | ||
| 29 | + | ||
| 30 | + private String host; | ||
| 31 | + private int port; | ||
| 32 | + private String dest; // 接收到文件存放的路径 | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * | ||
| 36 | + */ | ||
| 37 | + public FileClient(String host, int port, String dest) { | ||
| 38 | + this.host = host; | ||
| 39 | + this.port = port; | ||
| 40 | + this.dest = dest; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public void run() throws InterruptedException, IOException { | ||
| 44 | + | ||
| 45 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 46 | + | ||
| 47 | + try { | ||
| 48 | + Bootstrap b = new Bootstrap(); // (1) | ||
| 49 | + b.group(workerGroup); // (2) | ||
| 50 | + b.channel(NioSocketChannel.class); // (3) | ||
| 51 | + b.option(ChannelOption.SO_KEEPALIVE, true); // (4) | ||
| 52 | + b.handler(new ChannelInitializer<SocketChannel>() { | ||
| 53 | + @Override | ||
| 54 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 55 | + ch.pipeline().addLast("encoder", | ||
| 56 | + new StringEncoder(CharsetUtil.UTF_8)); | ||
| 57 | + ch.pipeline().addLast("decoder", | ||
| 58 | + new StringDecoder(CharsetUtil.UTF_8)); | ||
| 59 | + ch.pipeline().addLast(new FileClientHandler(dest)); | ||
| 60 | + } | ||
| 61 | + }); | ||
| 62 | + | ||
| 63 | + // 启动客户端 | ||
| 64 | + ChannelFuture f = b.connect(host, port).sync(); // (5) | ||
| 65 | + Channel channel = f.channel(); | ||
| 66 | + | ||
| 67 | + // 控制台输入请求的文件路径 | ||
| 68 | + BufferedReader in = new BufferedReader(new InputStreamReader( | ||
| 69 | + System.in)); | ||
| 70 | + while (true) { | ||
| 71 | + channel.writeAndFlush(in.readLine() + "\r\n"); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + // 等待连接关闭 | ||
| 75 | + // f.channel().closeFuture().sync(); | ||
| 76 | + } finally { | ||
| 77 | + workerGroup.shutdownGracefully(); | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * @param args | ||
| 83 | + * @throws InterruptedException | ||
| 84 | + */ | ||
| 85 | + public static void main(String[] args) throws InterruptedException, IOException { | ||
| 86 | + new FileClient("localhost", 8082, "D:/reciveFile.txt").run(); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.file; | ||
| 5 | + | ||
| 6 | +import java.io.File; | ||
| 7 | +import java.io.FileOutputStream; | ||
| 8 | + | ||
| 9 | +import io.netty.channel.ChannelHandlerContext; | ||
| 10 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 说明:文件客户端处理器 | ||
| 14 | + * | ||
| 15 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 16 | + */ | ||
| 17 | +public class FileClientHandler extends SimpleChannelInboundHandler<String> { | ||
| 18 | + | ||
| 19 | + private String dest; | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * | ||
| 23 | + * @param dest 文件生成路径 | ||
| 24 | + */ | ||
| 25 | + public FileClientHandler(String dest) { | ||
| 26 | + this.dest = dest; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + protected void channelRead0(ChannelHandlerContext ctx, String msg) | ||
| 31 | + throws Exception { | ||
| 32 | + | ||
| 33 | + File file = new File(dest); | ||
| 34 | + if (!file.exists()) { | ||
| 35 | + file.createNewFile(); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + FileOutputStream fos = new FileOutputStream(file); | ||
| 39 | + | ||
| 40 | + fos.write(msg.getBytes()); | ||
| 41 | + fos.close(); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2013-2018 Lilinfeng. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package com.waylau.netty.demo.file; | ||
| 17 | + | ||
| 18 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 19 | +import io.netty.channel.ChannelFuture; | ||
| 20 | +import io.netty.channel.ChannelInitializer; | ||
| 21 | +import io.netty.channel.ChannelOption; | ||
| 22 | +import io.netty.channel.EventLoopGroup; | ||
| 23 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 24 | +import io.netty.channel.socket.SocketChannel; | ||
| 25 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 26 | +import io.netty.handler.codec.LineBasedFrameDecoder; | ||
| 27 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 28 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 29 | +import io.netty.util.CharsetUtil; | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * 说明:文件服务器 | ||
| 33 | + * | ||
| 34 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 35 | + */ | ||
| 36 | +public class FileServer { | ||
| 37 | + | ||
| 38 | + public void run(int port) throws Exception { | ||
| 39 | + EventLoopGroup bossGroup = new NioEventLoopGroup(); | ||
| 40 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 41 | + try { | ||
| 42 | + ServerBootstrap b = new ServerBootstrap(); | ||
| 43 | + b.group(bossGroup, workerGroup) | ||
| 44 | + .channel(NioServerSocketChannel.class) | ||
| 45 | + .option(ChannelOption.SO_BACKLOG, 100) | ||
| 46 | + .childHandler(new ChannelInitializer<SocketChannel>() { | ||
| 47 | + /* | ||
| 48 | + * (non-Javadoc) | ||
| 49 | + * | ||
| 50 | + * @see | ||
| 51 | + * io.netty.channel.ChannelInitializer#initChannel(io | ||
| 52 | + * .netty.channel.Channel) | ||
| 53 | + */ | ||
| 54 | + public void initChannel(SocketChannel ch) | ||
| 55 | + throws Exception { | ||
| 56 | + ch.pipeline().addLast( | ||
| 57 | + new StringEncoder(CharsetUtil.UTF_8), | ||
| 58 | + new LineBasedFrameDecoder(1024), | ||
| 59 | + new StringDecoder(CharsetUtil.UTF_8), | ||
| 60 | + new FileServerHandler()); | ||
| 61 | + } | ||
| 62 | + }); | ||
| 63 | + ChannelFuture f = b.bind(port).sync(); | ||
| 64 | + System.out.println("Server start at port : " + port); | ||
| 65 | + f.channel().closeFuture().sync(); | ||
| 66 | + } finally { | ||
| 67 | + // 优雅停机 | ||
| 68 | + bossGroup.shutdownGracefully(); | ||
| 69 | + workerGroup.shutdownGracefully(); | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + public static void main(String[] args) throws Exception { | ||
| 74 | + int port = 8082; | ||
| 75 | + if (args.length > 0) { | ||
| 76 | + try { | ||
| 77 | + port = Integer.parseInt(args[0]); | ||
| 78 | + } catch (NumberFormatException e) { | ||
| 79 | + e.printStackTrace(); | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + new FileServer().run(port); | ||
| 83 | + } | ||
| 84 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2013-2018 Lilinfeng. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package com.waylau.netty.demo.file; | ||
| 17 | + | ||
| 18 | +import io.netty.channel.ChannelHandlerContext; | ||
| 19 | +import io.netty.channel.DefaultFileRegion; | ||
| 20 | +import io.netty.channel.FileRegion; | ||
| 21 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 22 | + | ||
| 23 | +import java.io.File; | ||
| 24 | +import java.io.RandomAccessFile; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * 说明:文件服务器处理器 | ||
| 28 | + * | ||
| 29 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 30 | + */ | ||
| 31 | +public class FileServerHandler extends SimpleChannelInboundHandler<String> { | ||
| 32 | + | ||
| 33 | + private static final String CR = System.getProperty("line.separator"); | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + protected void channelRead0(ChannelHandlerContext ctx, String msg) | ||
| 37 | + throws Exception { | ||
| 38 | + File file = new File(msg); | ||
| 39 | + if (file.exists()) { | ||
| 40 | + if (!file.isFile()) { | ||
| 41 | + ctx.writeAndFlush("Not a file : " + file + CR); | ||
| 42 | + return; | ||
| 43 | + } | ||
| 44 | + ctx.write(file + " " + file.length() + CR); | ||
| 45 | + RandomAccessFile randomAccessFile = new RandomAccessFile(msg, "r"); | ||
| 46 | + FileRegion region = new DefaultFileRegion( | ||
| 47 | + randomAccessFile.getChannel(), 0, randomAccessFile.length()); | ||
| 48 | + ctx.write(region); | ||
| 49 | + ctx.writeAndFlush(CR); | ||
| 50 | + randomAccessFile.close(); | ||
| 51 | + } else { | ||
| 52 | + ctx.writeAndFlush("File not found: " + file + CR); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + } | ||
| 56 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +/** | ||
| 5 | + * 说明:本包主要是要演示了文件服务器的功能. | ||
| 6 | + * 客户端启动时,会指定一个文件要保存的路径,本例为“D:/reciveFile.txt”。 | ||
| 7 | + * 客户端发送文件的请求,需在控制台输入所请求文件的路径(当然为了简单演示,该文件是服务器上的文件), | ||
| 8 | + * 而后,服务器会将该文件传送给客户端端,客户端将文件内容写入“D:/reciveFile.txt” | ||
| 9 | + * | ||
| 10 | + * | ||
| 11 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 12 | + */ | ||
| 13 | +package com.waylau.netty.demo.file; |
| 1 | +package com.waylau.netty.demo.heartbeat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelInitializer; | ||
| 5 | +import io.netty.channel.ChannelPipeline; | ||
| 6 | +import io.netty.handler.timeout.IdleStateHandler; | ||
| 7 | + | ||
| 8 | +import java.util.concurrent.TimeUnit; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 说明:心跳服务器初始化 | ||
| 12 | + * | ||
| 13 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 14 | + */ | ||
| 15 | +public class HeartbeatHandlerInitializer extends ChannelInitializer<Channel> { | ||
| 16 | + | ||
| 17 | + private static final int READ_IDEL_TIME_OUT = 4; // 读超时 | ||
| 18 | + private static final int WRITE_IDEL_TIME_OUT = 5;// 写超时 | ||
| 19 | + private static final int ALL_IDEL_TIME_OUT = 7; // 所有超时 | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + protected void initChannel(Channel ch) throws Exception { | ||
| 23 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 24 | + pipeline.addLast(new IdleStateHandler(READ_IDEL_TIME_OUT, | ||
| 25 | + WRITE_IDEL_TIME_OUT, ALL_IDEL_TIME_OUT, TimeUnit.SECONDS)); | ||
| 26 | + pipeline.addLast(new HeartbeatServerHandler()); | ||
| 27 | + } | ||
| 28 | +} |
| 1 | +package com.waylau.netty.demo.heartbeat; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelOption; | ||
| 6 | +import io.netty.channel.EventLoopGroup; | ||
| 7 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 8 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 9 | +import io.netty.handler.logging.LogLevel; | ||
| 10 | +import io.netty.handler.logging.LoggingHandler; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 说明:心跳服务器 | ||
| 14 | + * | ||
| 15 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 16 | + */ | ||
| 17 | +public final class HeartbeatServer { | ||
| 18 | + | ||
| 19 | + static final int PORT = 8082; | ||
| 20 | + | ||
| 21 | + public static void main(String[] args) throws Exception { | ||
| 22 | + | ||
| 23 | + // Configure the server. | ||
| 24 | + EventLoopGroup bossGroup = new NioEventLoopGroup(1); | ||
| 25 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 26 | + try { | ||
| 27 | + ServerBootstrap b = new ServerBootstrap(); | ||
| 28 | + b.group(bossGroup, workerGroup) | ||
| 29 | + .channel(NioServerSocketChannel.class) | ||
| 30 | + .option(ChannelOption.SO_BACKLOG, 100) | ||
| 31 | + .handler(new LoggingHandler(LogLevel.INFO)) | ||
| 32 | + .childHandler(new HeartbeatHandlerInitializer()); | ||
| 33 | + | ||
| 34 | + // Start the server. | ||
| 35 | + ChannelFuture f = b.bind(PORT).sync(); | ||
| 36 | + | ||
| 37 | + // Wait until the server socket is closed. | ||
| 38 | + f.channel().closeFuture().sync(); | ||
| 39 | + } finally { | ||
| 40 | + // Shut down all event loops to terminate all threads. | ||
| 41 | + bossGroup.shutdownGracefully(); | ||
| 42 | + workerGroup.shutdownGracefully(); | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | +} |
| 1 | +package com.waylau.netty.demo.heartbeat; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.buffer.Unpooled; | ||
| 5 | +import io.netty.channel.ChannelFutureListener; | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.channel.ChannelInboundHandlerAdapter; | ||
| 8 | +import io.netty.handler.timeout.IdleState; | ||
| 9 | +import io.netty.handler.timeout.IdleStateEvent; | ||
| 10 | +import io.netty.util.CharsetUtil; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 说明:心跳服务器处理器 | ||
| 14 | + * | ||
| 15 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月6日 | ||
| 16 | + */ | ||
| 17 | +public class HeartbeatServerHandler extends ChannelInboundHandlerAdapter { | ||
| 18 | + | ||
| 19 | + // Return a unreleasable view on the given ByteBuf | ||
| 20 | + // which will just ignore release and retain calls. | ||
| 21 | + private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled | ||
| 22 | + .unreleasableBuffer(Unpooled.copiedBuffer("Heartbeat", | ||
| 23 | + CharsetUtil.UTF_8)); | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) | ||
| 27 | + throws Exception { | ||
| 28 | + | ||
| 29 | + if (evt instanceof IdleStateEvent) { | ||
| 30 | + IdleStateEvent event = (IdleStateEvent) evt; | ||
| 31 | + String type = ""; | ||
| 32 | + if (event.state() == IdleState.READER_IDLE) { | ||
| 33 | + type = "read idle"; | ||
| 34 | + } else if (event.state() == IdleState.WRITER_IDLE) { | ||
| 35 | + type = "write idle"; | ||
| 36 | + } else if (event.state() == IdleState.ALL_IDLE) { | ||
| 37 | + type = "all idle"; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()).addListener( | ||
| 41 | + ChannelFutureListener.CLOSE_ON_FAILURE); | ||
| 42 | + | ||
| 43 | + System.out.println( ctx.channel().remoteAddress()+"超时类型:" + type); | ||
| 44 | + } else { | ||
| 45 | + super.userEventTriggered(ctx, evt); | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | +} |
| 1 | +package com.waylau.netty.demo.protocol; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 说明: | ||
| 5 | + * | ||
| 6 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 7 | + */ | ||
| 8 | +public class ClientTask implements Runnable { | ||
| 9 | + | ||
| 10 | + /** | ||
| 11 | + * | ||
| 12 | + */ | ||
| 13 | + public ClientTask() { | ||
| 14 | + // TODO Auto-generated constructor stub | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + /* (non-Javadoc) | ||
| 18 | + * @see java.lang.Runnable#run() | ||
| 19 | + */ | ||
| 20 | + @Override | ||
| 21 | + public void run() { | ||
| 22 | + // TODO Auto-generated method stub | ||
| 23 | + try { | ||
| 24 | + ProtocolClient client = new ProtocolClient("localhost", 8082); | ||
| 25 | + | ||
| 26 | + client.run(); | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + } catch (InterruptedException e) { | ||
| 30 | + // TODO Auto-generated catch block | ||
| 31 | + e.printStackTrace(); | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.protocol; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 说明:消息类型 | ||
| 8 | + * | ||
| 9 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 10 | + */ | ||
| 11 | +public enum MsgType { | ||
| 12 | + EMGW_LOGIN_REQ((byte) 0x00), | ||
| 13 | + EMGW_LOGIN_RES((byte) 0x01); | ||
| 14 | + | ||
| 15 | + private byte value; | ||
| 16 | + | ||
| 17 | + public byte getValue() { | ||
| 18 | + return value; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + private MsgType(byte value) { | ||
| 22 | + this.value = value; | ||
| 23 | + } | ||
| 24 | +} |
| 1 | +package com.waylau.netty.demo.protocol; | ||
| 2 | + | ||
| 3 | +import java.nio.charset.Charset; | ||
| 4 | + | ||
| 5 | +import io.netty.bootstrap.Bootstrap; | ||
| 6 | +import io.netty.channel.ChannelFuture; | ||
| 7 | +import io.netty.channel.ChannelInitializer; | ||
| 8 | +import io.netty.channel.ChannelOption; | ||
| 9 | +import io.netty.channel.EventLoopGroup; | ||
| 10 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 11 | +import io.netty.channel.socket.SocketChannel; | ||
| 12 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 说明:自定义协议客户端 | ||
| 16 | + * | ||
| 17 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 18 | + */ | ||
| 19 | +public class ProtocolClient { | ||
| 20 | + | ||
| 21 | + private String host; | ||
| 22 | + private int port; | ||
| 23 | + | ||
| 24 | + private static final int MAX_FRAME_LENGTH = 1024 * 1024; | ||
| 25 | + private static final int LENGTH_FIELD_LENGTH = 4; | ||
| 26 | + private static final int LENGTH_FIELD_OFFSET = 6; | ||
| 27 | + private static final int LENGTH_ADJUSTMENT = 0; | ||
| 28 | + private static final int INITIAL_BYTES_TO_STRIP = 0; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * | ||
| 32 | + */ | ||
| 33 | + public ProtocolClient(String host, int port) { | ||
| 34 | + this.host = host; | ||
| 35 | + this.port = port; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public void run() throws InterruptedException { | ||
| 39 | + | ||
| 40 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 41 | + | ||
| 42 | + try { | ||
| 43 | + Bootstrap b = new Bootstrap(); // (1) | ||
| 44 | + b.group(workerGroup); // (2) | ||
| 45 | + b.channel(NioSocketChannel.class); // (3) | ||
| 46 | + b.option(ChannelOption.SO_KEEPALIVE, true); // (4) | ||
| 47 | + b.handler(new ChannelInitializer<SocketChannel>() { | ||
| 48 | + @Override | ||
| 49 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 50 | + ch.pipeline().addLast( | ||
| 51 | + "decoder", | ||
| 52 | + new ProtocolDecoder(MAX_FRAME_LENGTH, | ||
| 53 | + LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, | ||
| 54 | + LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP)); | ||
| 55 | + ch.pipeline().addLast("encoder", new ProtocolEncoder()); | ||
| 56 | + ch.pipeline().addLast(new ProtocolClientHandler()); | ||
| 57 | + | ||
| 58 | + } | ||
| 59 | + }); | ||
| 60 | + | ||
| 61 | + // 启动客户端 | ||
| 62 | + ChannelFuture f = b.connect(host, port).sync(); // (5) | ||
| 63 | + | ||
| 64 | + while (true) { | ||
| 65 | + | ||
| 66 | + // 发送消息给服务器 | ||
| 67 | + ProtocolMsg msg = new ProtocolMsg(); | ||
| 68 | + ProtocolHeader protocolHeader = new ProtocolHeader(); | ||
| 69 | + protocolHeader.setMagic((byte) 0x01); | ||
| 70 | + protocolHeader.setMsgType((byte) 0x01); | ||
| 71 | + protocolHeader.setReserve((short) 0); | ||
| 72 | + protocolHeader.setSn((short) 0); | ||
| 73 | + String body = "床前明月光疑是地上霜"; | ||
| 74 | + StringBuffer sb = new StringBuffer(); | ||
| 75 | + for (int i = 0; i < 2700; i++) { | ||
| 76 | + sb.append(body); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + byte[] bodyBytes = sb.toString().getBytes( | ||
| 80 | + Charset.forName("utf-8")); | ||
| 81 | + int bodySize = bodyBytes.length; | ||
| 82 | + protocolHeader.setLen(bodySize); | ||
| 83 | + | ||
| 84 | + msg.setProtocolHeader(protocolHeader); | ||
| 85 | + msg.setBody(sb.toString()); | ||
| 86 | + | ||
| 87 | + f.channel().writeAndFlush(msg); | ||
| 88 | + Thread.sleep(2000); | ||
| 89 | + } | ||
| 90 | + // 等待连接关闭 | ||
| 91 | + // f.channel().closeFuture().sync(); | ||
| 92 | + } finally { | ||
| 93 | + workerGroup.shutdownGracefully(); | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * @param args | ||
| 99 | + * @throws InterruptedException | ||
| 100 | + */ | ||
| 101 | + public static void main(String[] args) throws InterruptedException { | ||
| 102 | + new ProtocolClient("localhost", 8082).run(); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | +} |
| 1 | +package com.waylau.netty.demo.protocol; | ||
| 2 | +import io.netty.channel.Channel; | ||
| 3 | +import io.netty.channel.ChannelHandlerContext; | ||
| 4 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 说明:处理器 | ||
| 8 | + * | ||
| 9 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 10 | + */ | ||
| 11 | +public class ProtocolClientHandler extends SimpleChannelInboundHandler<Object> { | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + @Override | ||
| 15 | + protected void channelRead0(ChannelHandlerContext ctx, Object obj) | ||
| 16 | + throws Exception { | ||
| 17 | + Channel incoming = ctx.channel(); | ||
| 18 | + System.out.println("Server->Client:"+incoming.remoteAddress()+obj.toString()); | ||
| 19 | + | ||
| 20 | + if(obj instanceof ProtocolMsg) { | ||
| 21 | + ProtocolMsg msg = (ProtocolMsg)obj; | ||
| 22 | + System.out.println("Server->Client:"+incoming.remoteAddress()+msg.getBody()); | ||
| 23 | + } | ||
| 24 | + } | ||
| 25 | + @Override | ||
| 26 | + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { | ||
| 27 | + //ctx.flush(); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.protocol; | ||
| 5 | + | ||
| 6 | +import java.util.concurrent.Executor; | ||
| 7 | +import java.util.concurrent.Executors; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 说明:自定义协议客户端性能测试 | ||
| 11 | + * | ||
| 12 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 13 | + */ | ||
| 14 | +public class ProtocolClientTest { | ||
| 15 | + | ||
| 16 | + private static final int POOL_SIZE_SEND = 100; | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * @param args | ||
| 20 | + * @throws InterruptedException | ||
| 21 | + */ | ||
| 22 | + public static void main(String[] args) throws InterruptedException { | ||
| 23 | + | ||
| 24 | + Executor executor = Executors.newFixedThreadPool(POOL_SIZE_SEND); | ||
| 25 | + for (int i = 0; i < POOL_SIZE_SEND; i++) { | ||
| 26 | + executor.execute(new ClientTask()); | ||
| 27 | + Thread.sleep(100); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | +} |
| 1 | +package com.waylau.netty.demo.protocol; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 说明: | ||
| 9 | + * | ||
| 10 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月11日 | ||
| 11 | + */ | ||
| 12 | +public class ProtocolDecoder extends LengthFieldBasedFrameDecoder { | ||
| 13 | + private static final int HEADER_SIZE = 10; | ||
| 14 | + | ||
| 15 | + private byte magic; // 魔数 | ||
| 16 | + private byte msgType; // 消息类型 | ||
| 17 | + private short reserve; // 保留字 | ||
| 18 | + private short sn; // 序列号 | ||
| 19 | + private int len; // 长度 | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * @param maxFrameLength | ||
| 23 | + * @param lengthFieldOffset | ||
| 24 | + * @param lengthFieldLength | ||
| 25 | + * @param lengthAdjustment | ||
| 26 | + * @param initialBytesToStrip | ||
| 27 | + */ | ||
| 28 | + public ProtocolDecoder(int maxFrameLength, int lengthFieldOffset, | ||
| 29 | + int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) { | ||
| 30 | + super(maxFrameLength, lengthFieldOffset, lengthFieldLength, | ||
| 31 | + lengthAdjustment, initialBytesToStrip); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * @param maxFrameLength | ||
| 36 | + * @param lengthFieldOffset | ||
| 37 | + * @param lengthFieldLength | ||
| 38 | + * @param lengthAdjustment | ||
| 39 | + * @param initialBytesToStrip | ||
| 40 | + * @param failFast | ||
| 41 | + */ | ||
| 42 | + public ProtocolDecoder(int maxFrameLength, int lengthFieldOffset, | ||
| 43 | + int lengthFieldLength, int lengthAdjustment, | ||
| 44 | + int initialBytesToStrip, boolean failFast) { | ||
| 45 | + super(maxFrameLength, lengthFieldOffset, lengthFieldLength, | ||
| 46 | + lengthAdjustment, initialBytesToStrip, failFast); | ||
| 47 | + // TODO Auto-generated constructor stub | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + | ||
| 51 | + @Override | ||
| 52 | + protected ProtocolMsg decode(ChannelHandlerContext ctx, ByteBuf in2) throws Exception { | ||
| 53 | + ByteBuf in = (ByteBuf) super.decode(ctx, in2); | ||
| 54 | + if (in == null) { | ||
| 55 | + return null; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + if (in.readableBytes() < HEADER_SIZE) { | ||
| 59 | + return null;// response header is 10 bytes | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + magic = in.readByte(); | ||
| 63 | + msgType = in.readByte(); | ||
| 64 | + reserve = in.readShort(); | ||
| 65 | + sn = in.readShort(); | ||
| 66 | + len = in.readInt(); | ||
| 67 | + | ||
| 68 | + if (in.readableBytes() < len) { | ||
| 69 | + return null; // until we have the entire payload return | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + ByteBuf buf = in.readBytes(len); | ||
| 73 | + byte[] req = new byte[buf.readableBytes()]; | ||
| 74 | + buf.readBytes(req); | ||
| 75 | + String body = new String(req, "UTF-8"); | ||
| 76 | + ProtocolMsg msg = new ProtocolMsg(); | ||
| 77 | + ProtocolHeader protocolHeader = new ProtocolHeader(magic, msgType, | ||
| 78 | + reserve, sn, len); | ||
| 79 | + msg.setBody(body); | ||
| 80 | + msg.setProtocolHeader(protocolHeader); | ||
| 81 | + return msg; | ||
| 82 | + } | ||
| 83 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.protocol; | ||
| 5 | + | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +import io.netty.buffer.ByteBuf; | ||
| 9 | +import io.netty.channel.ChannelHandlerContext; | ||
| 10 | +import io.netty.handler.codec.ByteToMessageDecoder; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 说明: | ||
| 14 | + * | ||
| 15 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月10日 | ||
| 16 | + */ | ||
| 17 | +public class ProtocolDecoderDeprecation extends ByteToMessageDecoder { | ||
| 18 | + | ||
| 19 | + private static final int HEADER_SIZE = 10; | ||
| 20 | + | ||
| 21 | + private byte magic; // 魔数 | ||
| 22 | + private byte msgType; // 消息类型 | ||
| 23 | + private short reserve; // 保留字 | ||
| 24 | + private short sn; // 序列号 | ||
| 25 | + private int len; // 长度 | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * | ||
| 29 | + */ | ||
| 30 | + public ProtocolDecoderDeprecation() { | ||
| 31 | + // TODO Auto-generated constructor stub | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /* | ||
| 35 | + * (non-Javadoc) | ||
| 36 | + * | ||
| 37 | + * @see io.netty.handler.codec.ByteToMessageDecoder#decode(io.netty.channel. | ||
| 38 | + * ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List) | ||
| 39 | + */ | ||
| 40 | + @Override | ||
| 41 | + protected void decode(ChannelHandlerContext ctx, ByteBuf in, | ||
| 42 | + List<Object> out) throws Exception { | ||
| 43 | + if (in.readableBytes() < HEADER_SIZE) { | ||
| 44 | + return;// response header is 10 bytes | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + magic = in.readByte(); | ||
| 48 | + msgType = in.readByte(); | ||
| 49 | + reserve = in.readShort(); | ||
| 50 | + sn = in.readShort(); | ||
| 51 | + len = in.readInt(); | ||
| 52 | + | ||
| 53 | + if (in.readableBytes() < len) { | ||
| 54 | + return; // until we have the entire payload return | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + ByteBuf buf = in.readBytes(len); | ||
| 58 | + byte[] req = new byte[buf.readableBytes()]; | ||
| 59 | + buf.readBytes(req); | ||
| 60 | + String body = new String(req, "UTF-8"); | ||
| 61 | + ProtocolMsg msg = new ProtocolMsg(); | ||
| 62 | + | ||
| 63 | +// ProtocolBody body2 = new ProtocolBody(); | ||
| 64 | +// body2.setBody(body); | ||
| 65 | + ProtocolHeader protocolHeader = new ProtocolHeader(magic, msgType, | ||
| 66 | + reserve, sn, len); | ||
| 67 | + //msg.setProtocolBody(body2); | ||
| 68 | + msg.setBody(body); | ||
| 69 | + msg.setProtocolHeader(protocolHeader); | ||
| 70 | + out.add(msg); | ||
| 71 | + | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.protocol; | ||
| 5 | + | ||
| 6 | +import java.nio.charset.Charset; | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +import io.netty.buffer.ByteBuf; | ||
| 10 | +import io.netty.channel.ChannelHandlerContext; | ||
| 11 | +import io.netty.handler.codec.MessageToByteEncoder; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 说明:编码器 | ||
| 15 | + * | ||
| 16 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月10日 | ||
| 17 | + */ | ||
| 18 | +public class ProtocolEncoder extends MessageToByteEncoder<ProtocolMsg> { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * | ||
| 22 | + */ | ||
| 23 | + public ProtocolEncoder() { | ||
| 24 | + // TODO Auto-generated constructor stub | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * @param outboundMessageType | ||
| 29 | + */ | ||
| 30 | + public ProtocolEncoder(Class<? extends ProtocolMsg> outboundMessageType) { | ||
| 31 | + super(outboundMessageType); | ||
| 32 | + // TODO Auto-generated constructor stub | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * @param preferDirect | ||
| 37 | + */ | ||
| 38 | + public ProtocolEncoder(boolean preferDirect) { | ||
| 39 | + super(preferDirect); | ||
| 40 | + // TODO Auto-generated constructor stub | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * @param outboundMessageType | ||
| 45 | + * @param preferDirect | ||
| 46 | + */ | ||
| 47 | + public ProtocolEncoder(Class<? extends ProtocolMsg> outboundMessageType, | ||
| 48 | + boolean preferDirect) { | ||
| 49 | + super(outboundMessageType, preferDirect); | ||
| 50 | + // TODO Auto-generated constructor stub | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + /* (non-Javadoc) | ||
| 54 | + * @see io.netty.handler.codec.MessageToByteEncoder#encode(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.buffer.ByteBuf) | ||
| 55 | + */ | ||
| 56 | + @Override | ||
| 57 | + protected void encode(ChannelHandlerContext ctx, ProtocolMsg msg, | ||
| 58 | + ByteBuf out) throws Exception { | ||
| 59 | + if (msg == null | msg.getProtocolHeader() == null) { | ||
| 60 | + throw new Exception("The encode message is null"); | ||
| 61 | + } | ||
| 62 | + ProtocolHeader header = msg.getProtocolHeader(); | ||
| 63 | + String body = msg.getBody(); | ||
| 64 | + byte[] bodyBytes = body.getBytes(Charset.forName("utf-8")); | ||
| 65 | + int bodySize = bodyBytes.length; | ||
| 66 | + | ||
| 67 | + out.writeByte(header.getMagic()); | ||
| 68 | + out.writeByte(header.getMsgType()); | ||
| 69 | + out.writeShort(header.getReserve()); | ||
| 70 | + out.writeShort(header.getSn()); | ||
| 71 | + out.writeInt(bodySize); | ||
| 72 | + out.writeBytes(bodyBytes); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | +} |
| 1 | +package com.waylau.netty.demo.protocol; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * 说明:协议消息头 | ||
| 6 | + * | ||
| 7 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月4日 | ||
| 8 | + */ | ||
| 9 | +public class ProtocolHeader{ | ||
| 10 | + | ||
| 11 | + private byte magic; // 魔数 | ||
| 12 | + private byte msgType; // 消息类型 | ||
| 13 | + private short reserve; // 保留字 | ||
| 14 | + private short sn; // 序列号 | ||
| 15 | + private int len; // 长度 | ||
| 16 | + | ||
| 17 | + public byte getMagic() { | ||
| 18 | + return magic; | ||
| 19 | + } | ||
| 20 | + public void setMagic(byte magic) { | ||
| 21 | + this.magic = magic; | ||
| 22 | + } | ||
| 23 | + public byte getMsgType() { | ||
| 24 | + return msgType; | ||
| 25 | + } | ||
| 26 | + public void setMsgType(byte msgType) { | ||
| 27 | + this.msgType = msgType; | ||
| 28 | + } | ||
| 29 | + public short getReserve() { | ||
| 30 | + return reserve; | ||
| 31 | + } | ||
| 32 | + public void setReserve(short reserve) { | ||
| 33 | + this.reserve = reserve; | ||
| 34 | + } | ||
| 35 | + public short getSn() { | ||
| 36 | + return sn; | ||
| 37 | + } | ||
| 38 | + public void setSn(short sn) { | ||
| 39 | + this.sn = sn; | ||
| 40 | + } | ||
| 41 | + public int getLen() { | ||
| 42 | + return len; | ||
| 43 | + } | ||
| 44 | + public void setLen(int len) { | ||
| 45 | + this.len = len; | ||
| 46 | + } | ||
| 47 | + public ProtocolHeader() { | ||
| 48 | + } | ||
| 49 | + /** | ||
| 50 | + * | ||
| 51 | + */ | ||
| 52 | + public ProtocolHeader(byte magic, byte msgType,short reserve,short sn,int len) { | ||
| 53 | + this.magic = magic; | ||
| 54 | + this.msgType = msgType; | ||
| 55 | + this.reserve = reserve; | ||
| 56 | + this.sn = sn; | ||
| 57 | + this.len = len; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | +} |
| 1 | +package com.waylau.netty.demo.protocol; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 说明:消息对象 | ||
| 5 | + * | ||
| 6 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 7 | + */ | ||
| 8 | +public class ProtocolMsg { | ||
| 9 | + | ||
| 10 | + private ProtocolHeader protocolHeader = new ProtocolHeader(); | ||
| 11 | + private String body; | ||
| 12 | + | ||
| 13 | + public String getBody() { | ||
| 14 | + return body; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + public void setBody(String body) { | ||
| 18 | + this.body = body; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * | ||
| 24 | + */ | ||
| 25 | + public ProtocolMsg() { | ||
| 26 | + // TODO Auto-generated constructor stub | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public ProtocolHeader getProtocolHeader() { | ||
| 30 | + return protocolHeader; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public void setProtocolHeader(ProtocolHeader protocolHeader) { | ||
| 34 | + this.protocolHeader = protocolHeader; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.demo.protocol; | ||
| 5 | + | ||
| 6 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 7 | +import io.netty.channel.ChannelFuture; | ||
| 8 | +import io.netty.channel.ChannelInitializer; | ||
| 9 | +import io.netty.channel.ChannelOption; | ||
| 10 | +import io.netty.channel.EventLoopGroup; | ||
| 11 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 12 | +import io.netty.channel.socket.SocketChannel; | ||
| 13 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 说明:自定义协议服务端 | ||
| 17 | + * | ||
| 18 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月5日 | ||
| 19 | + */ | ||
| 20 | +public class ProtocolServer { | ||
| 21 | + | ||
| 22 | + private int port; | ||
| 23 | + | ||
| 24 | + private static final int MAX_FRAME_LENGTH = 1024 * 1024; | ||
| 25 | + private static final int LENGTH_FIELD_LENGTH = 4; | ||
| 26 | + private static final int LENGTH_FIELD_OFFSET = 6; | ||
| 27 | + private static final int LENGTH_ADJUSTMENT = 0; | ||
| 28 | + private static final int INITIAL_BYTES_TO_STRIP = 0; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * | ||
| 32 | + */ | ||
| 33 | + public ProtocolServer(int port) { | ||
| 34 | + this.port = port; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public void run() throws Exception { | ||
| 38 | + EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) | ||
| 39 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 40 | + try { | ||
| 41 | + ServerBootstrap b = new ServerBootstrap(); // (2) | ||
| 42 | + b.group(bossGroup, workerGroup) | ||
| 43 | + .channel(NioServerSocketChannel.class) // (3) | ||
| 44 | + .childHandler(new ChannelInitializer<SocketChannel>() { // (4) | ||
| 45 | + @Override | ||
| 46 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 47 | + ch.pipeline().addLast("decoder", | ||
| 48 | + new ProtocolDecoder(MAX_FRAME_LENGTH, | ||
| 49 | + LENGTH_FIELD_OFFSET,LENGTH_FIELD_LENGTH, | ||
| 50 | + LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP)); | ||
| 51 | + ch.pipeline().addLast("encoder", new ProtocolEncoder()); | ||
| 52 | + ch.pipeline().addLast(new ProtocolServerHandler()); | ||
| 53 | + } | ||
| 54 | + }) | ||
| 55 | + .option(ChannelOption.SO_BACKLOG, 128) // (5) | ||
| 56 | + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) | ||
| 57 | + | ||
| 58 | + // 绑定端口,开始接收进来的连接 | ||
| 59 | + ChannelFuture f = b.bind(port).sync(); // (7) | ||
| 60 | + | ||
| 61 | + System.out.println("Server start listen at " + port ); | ||
| 62 | + | ||
| 63 | + // 等待服务器 socket 关闭 。 | ||
| 64 | + // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 | ||
| 65 | + f.channel().closeFuture().sync(); | ||
| 66 | + | ||
| 67 | + | ||
| 68 | + } finally { | ||
| 69 | + workerGroup.shutdownGracefully(); | ||
| 70 | + bossGroup.shutdownGracefully(); | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | + /** | ||
| 74 | + * @param args | ||
| 75 | + * @throws Exception | ||
| 76 | + */ | ||
| 77 | + public static void main(String[] args) throws Exception { | ||
| 78 | + int port; | ||
| 79 | + if (args.length > 0) { | ||
| 80 | + port = Integer.parseInt(args[0]); | ||
| 81 | + } else { | ||
| 82 | + port = 8082; | ||
| 83 | + } | ||
| 84 | + new ProtocolServer(port).run(); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | +} |
| 1 | +package com.waylau.netty.demo.protocol; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 说明:处理器 | ||
| 9 | + * | ||
| 10 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月7日 | ||
| 11 | + */ | ||
| 12 | +public class ProtocolServerHandler extends SimpleChannelInboundHandler<Object> { | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + protected void channelRead0(ChannelHandlerContext ctx, Object obj) | ||
| 17 | + throws Exception { | ||
| 18 | + Channel incoming = ctx.channel(); | ||
| 19 | + | ||
| 20 | + if(obj instanceof ProtocolMsg) { | ||
| 21 | + ProtocolMsg msg = (ProtocolMsg)obj; | ||
| 22 | + System.out.println("Client->Server:"+incoming.remoteAddress()+msg.getBody()); | ||
| 23 | + incoming.write(obj); | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { | ||
| 29 | + ctx.flush(); | ||
| 30 | + } | ||
| 31 | +} |
| 1 | +## 消息格式 | ||
| 2 | + | ||
| 3 | +类型 | 名称 | 字节序列 | 取值范围 | 备注 | ||
| 4 | +--- | ----- | ---------| --------- |---- | ||
| 5 | +消息头 | magic | 0 |0x80、0x81 |帧头 | ||
| 6 | + | msgType | 1 |0x00-0xff |消息类型 | ||
| 7 | + | reserve | 2-3 |0x00 |保留字,以备扩展 | ||
| 8 | + | sn |4-5 |0-32767 |序列号。是一个事务标识,从0开始,每次递增1,响应消息中的序列号是从请求消息中拷贝来的。当序列号达到最大值时(32767),则又从0开始。 | ||
| 9 | + | len |6-9 |0-2147483647 |消息体长度。 | ||
| 10 | +消息体 | body |变长 |0- |消息体。格式和消息类型相关,不同的消息类型有不同的消息体格式。消息大小不应超过2G | ||
| 11 | + | ||
| 12 | + |
| 1 | +package com.waylau.netty.demo.securechat; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.Bootstrap; | ||
| 4 | +import io.netty.channel.Channel; | ||
| 5 | +import io.netty.channel.ChannelFuture; | ||
| 6 | +import io.netty.channel.EventLoopGroup; | ||
| 7 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 8 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 9 | +import io.netty.handler.ssl.SslContext; | ||
| 10 | +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; | ||
| 11 | + | ||
| 12 | +import java.io.BufferedReader; | ||
| 13 | +import java.io.InputStreamReader; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * Simple SSL chat client | ||
| 17 | + */ | ||
| 18 | +public final class SecureChatClient { | ||
| 19 | + | ||
| 20 | + static final String HOST = System.getProperty("host", "127.0.0.1"); | ||
| 21 | + static final int PORT = Integer.parseInt(System.getProperty("port", "8992")); | ||
| 22 | + | ||
| 23 | + public static void main(String[] args) throws Exception { | ||
| 24 | + // Configure SSL. | ||
| 25 | + final SslContext sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); | ||
| 26 | + | ||
| 27 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 28 | + try { | ||
| 29 | + Bootstrap b = new Bootstrap(); | ||
| 30 | + b.group(group) | ||
| 31 | + .channel(NioSocketChannel.class) | ||
| 32 | + .handler(new SecureChatClientInitializer(sslCtx)); | ||
| 33 | + | ||
| 34 | + // Start the connection attempt. | ||
| 35 | + Channel ch = b.connect(HOST, PORT).sync().channel(); | ||
| 36 | + | ||
| 37 | + // Read commands from the stdin. | ||
| 38 | + ChannelFuture lastWriteFuture = null; | ||
| 39 | + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); | ||
| 40 | + for (;;) { | ||
| 41 | + String line = in.readLine(); | ||
| 42 | + if (line == null) { | ||
| 43 | + break; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + // Sends the received line to the server. | ||
| 47 | + lastWriteFuture = ch.writeAndFlush(line + "\r\n"); | ||
| 48 | + | ||
| 49 | + // If user typed the 'bye' command, wait until the server closes | ||
| 50 | + // the connection. | ||
| 51 | + if ("bye".equals(line.toLowerCase())) { | ||
| 52 | + ch.closeFuture().sync(); | ||
| 53 | + break; | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + // Wait until all messages are flushed before closing the channel. | ||
| 58 | + if (lastWriteFuture != null) { | ||
| 59 | + lastWriteFuture.sync(); | ||
| 60 | + } | ||
| 61 | + } finally { | ||
| 62 | + // The connection is closed automatically on shutdown. | ||
| 63 | + group.shutdownGracefully(); | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | +} |
| 1 | +package com.waylau.netty.demo.securechat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelHandlerContext; | ||
| 4 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * Handles a client-side channel. | ||
| 8 | + */ | ||
| 9 | +public class SecureChatClientHandler extends SimpleChannelInboundHandler<String> { | ||
| 10 | + | ||
| 11 | + public void messageReceived(ChannelHandlerContext ctx, String msg) { | ||
| 12 | + System.err.println(msg); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 17 | + cause.printStackTrace(); | ||
| 18 | + ctx.close(); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + protected void channelRead0(ChannelHandlerContext ctx, String msg) | ||
| 23 | + throws Exception { | ||
| 24 | + // TODO Auto-generated method stub | ||
| 25 | + | ||
| 26 | + } | ||
| 27 | +} |
| 1 | +package com.waylau.netty.demo.securechat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 7 | +import io.netty.handler.codec.Delimiters; | ||
| 8 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 9 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 10 | +import io.netty.handler.ssl.SslContext; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Creates a newly configured {@link ChannelPipeline} for a new channel. | ||
| 14 | + */ | ||
| 15 | +public class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> { | ||
| 16 | + | ||
| 17 | + private final SslContext sslCtx; | ||
| 18 | + | ||
| 19 | + public SecureChatClientInitializer(SslContext sslCtx) { | ||
| 20 | + this.sslCtx = sslCtx; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 25 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 26 | + | ||
| 27 | + // Add SSL handler first to encrypt and decrypt everything. | ||
| 28 | + // In this example, we use a bogus certificate in the server side | ||
| 29 | + // and accept any invalid certificates in the client side. | ||
| 30 | + // You will need something more complicated to identify both | ||
| 31 | + // and server in the real world. | ||
| 32 | + pipeline.addLast(sslCtx.newHandler(ch.alloc(), SecureChatClient.HOST, SecureChatClient.PORT)); | ||
| 33 | + | ||
| 34 | + // On top of the SSL handler, add the text line codec. | ||
| 35 | + pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 36 | + pipeline.addLast(new StringDecoder()); | ||
| 37 | + pipeline.addLast(new StringEncoder()); | ||
| 38 | + | ||
| 39 | + // and then business logic. | ||
| 40 | + pipeline.addLast(new SecureChatClientHandler()); | ||
| 41 | + } | ||
| 42 | +} |
| 1 | +package com.waylau.netty.demo.securechat; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.EventLoopGroup; | ||
| 5 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 6 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 7 | +import io.netty.handler.logging.LogLevel; | ||
| 8 | +import io.netty.handler.logging.LoggingHandler; | ||
| 9 | +import io.netty.handler.ssl.SslContext; | ||
| 10 | +import io.netty.handler.ssl.util.SelfSignedCertificate; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Simple SSL chat server | ||
| 14 | + */ | ||
| 15 | +public final class SecureChatServer { | ||
| 16 | + | ||
| 17 | + static final int PORT = Integer.parseInt(System.getProperty("port", "8992")); | ||
| 18 | + | ||
| 19 | + public static void main(String[] args) throws Exception { | ||
| 20 | + SelfSignedCertificate ssc = new SelfSignedCertificate(); | ||
| 21 | + SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); | ||
| 22 | + | ||
| 23 | + EventLoopGroup bossGroup = new NioEventLoopGroup(1); | ||
| 24 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 25 | + try { | ||
| 26 | + ServerBootstrap b = new ServerBootstrap(); | ||
| 27 | + b.group(bossGroup, workerGroup) | ||
| 28 | + .channel(NioServerSocketChannel.class) | ||
| 29 | + .handler(new LoggingHandler(LogLevel.INFO)) | ||
| 30 | + .childHandler(new SecureChatServerInitializer(sslCtx)); | ||
| 31 | + | ||
| 32 | + b.bind(PORT).sync().channel().closeFuture().sync(); | ||
| 33 | + } finally { | ||
| 34 | + bossGroup.shutdownGracefully(); | ||
| 35 | + workerGroup.shutdownGracefully(); | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | +} |
| 1 | +package com.waylau.netty.demo.securechat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 6 | +import io.netty.channel.group.ChannelGroup; | ||
| 7 | +import io.netty.channel.group.DefaultChannelGroup; | ||
| 8 | +import io.netty.handler.ssl.SslHandler; | ||
| 9 | +import io.netty.util.concurrent.Future; | ||
| 10 | +import io.netty.util.concurrent.GenericFutureListener; | ||
| 11 | +import io.netty.util.concurrent.GlobalEventExecutor; | ||
| 12 | + | ||
| 13 | +import java.net.InetAddress; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * Handles a server-side channel. | ||
| 17 | + */ | ||
| 18 | +public class SecureChatServerHandler extends SimpleChannelInboundHandler<String> { | ||
| 19 | + | ||
| 20 | + static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public void channelActive(final ChannelHandlerContext ctx) { | ||
| 24 | + // Once session is secured, send a greeting and register the channel to the global channel | ||
| 25 | + // list so the channel received the messages from others. | ||
| 26 | + ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener( | ||
| 27 | + new GenericFutureListener<Future<Channel>>() { | ||
| 28 | + @Override | ||
| 29 | + public void operationComplete(Future<Channel> future) throws Exception { | ||
| 30 | + ctx.writeAndFlush( | ||
| 31 | + "Welcome to " + InetAddress.getLocalHost().getHostName() + " secure chat service!\n"); | ||
| 32 | + ctx.writeAndFlush( | ||
| 33 | + "Your session is protected by " + | ||
| 34 | + ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() + | ||
| 35 | + " cipher suite.\n"); | ||
| 36 | + | ||
| 37 | + channels.add(ctx.channel()); | ||
| 38 | + } | ||
| 39 | + }); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { | ||
| 43 | + // Send the received message to all channels but the current one. | ||
| 44 | + for (Channel c: channels) { | ||
| 45 | + if (c != ctx.channel()) { | ||
| 46 | + c.writeAndFlush("[" + ctx.channel().remoteAddress() + "] " + msg + '\n'); | ||
| 47 | + } else { | ||
| 48 | + c.writeAndFlush("[you] " + msg + '\n'); | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + // Close the connection if the client has sent 'bye'. | ||
| 53 | + if ("bye".equals(msg.toLowerCase())) { | ||
| 54 | + ctx.close(); | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Override | ||
| 59 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 60 | + cause.printStackTrace(); | ||
| 61 | + ctx.close(); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + @Override | ||
| 65 | + protected void channelRead0(ChannelHandlerContext ctx, String msg) | ||
| 66 | + throws Exception { | ||
| 67 | + // TODO Auto-generated method stub | ||
| 68 | + | ||
| 69 | + } | ||
| 70 | +} |
| 1 | +package com.waylau.netty.demo.securechat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 7 | +import io.netty.handler.codec.Delimiters; | ||
| 8 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 9 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 10 | +import io.netty.handler.ssl.SslContext; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Creates a newly configured {@link ChannelPipeline} for a new channel. | ||
| 14 | + */ | ||
| 15 | +public class SecureChatServerInitializer extends ChannelInitializer<SocketChannel> { | ||
| 16 | + | ||
| 17 | + private final SslContext sslCtx; | ||
| 18 | + | ||
| 19 | + public SecureChatServerInitializer(SslContext sslCtx) { | ||
| 20 | + this.sslCtx = sslCtx; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 25 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 26 | + | ||
| 27 | + // Add SSL handler first to encrypt and decrypt everything. | ||
| 28 | + // In this example, we use a bogus certificate in the server side | ||
| 29 | + // and accept any invalid certificates in the client side. | ||
| 30 | + // You will need something more complicated to identify both | ||
| 31 | + // and server in the real world. | ||
| 32 | + pipeline.addLast(sslCtx.newHandler(ch.alloc())); | ||
| 33 | + | ||
| 34 | + // On top of the SSL handler, add the text line codec. | ||
| 35 | + pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 36 | + pipeline.addLast(new StringDecoder()); | ||
| 37 | + pipeline.addLast(new StringEncoder()); | ||
| 38 | + | ||
| 39 | + // and then business logic. | ||
| 40 | + pipeline.addLast(new SecureChatServerHandler()); | ||
| 41 | + } | ||
| 42 | +} |
| 1 | +package com.waylau.netty.demo.simplechat; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.Bootstrap; | ||
| 4 | +import io.netty.channel.Channel; | ||
| 5 | +import io.netty.channel.EventLoopGroup; | ||
| 6 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 7 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 8 | + | ||
| 9 | +import java.io.BufferedReader; | ||
| 10 | +import java.io.InputStreamReader; | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 简单聊天服务器-客户端 | ||
| 15 | + * | ||
| 16 | + * @author waylau.com | ||
| 17 | + * @date 2015-2-26 | ||
| 18 | + */ | ||
| 19 | +public class SimpleChatClient { | ||
| 20 | + | ||
| 21 | + public static void main(String[] args) throws Exception{ | ||
| 22 | + new SimpleChatClient("localhost", 8080).run(); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + private final String host; | ||
| 26 | + private final int port; | ||
| 27 | + | ||
| 28 | + public SimpleChatClient(String host, int port){ | ||
| 29 | + this.host = host; | ||
| 30 | + this.port = port; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public void run() throws Exception{ | ||
| 34 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 35 | + try { | ||
| 36 | + Bootstrap bootstrap = new Bootstrap() | ||
| 37 | + .group(group) | ||
| 38 | + .channel(NioSocketChannel.class) | ||
| 39 | + .handler(new SimpleChatClientInitializer()); | ||
| 40 | + Channel channel = bootstrap.connect(host, port).sync().channel(); | ||
| 41 | + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); | ||
| 42 | + while(true){ | ||
| 43 | + channel.writeAndFlush(in.readLine() + "\r\n"); | ||
| 44 | + } | ||
| 45 | + } catch (Exception e) { | ||
| 46 | + e.printStackTrace(); | ||
| 47 | + } finally { | ||
| 48 | + group.shutdownGracefully(); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | +} |
| 1 | +package com.waylau.netty.demo.simplechat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelHandlerContext; | ||
| 4 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 客户端 channel | ||
| 8 | + * | ||
| 9 | + * @author waylau.com | ||
| 10 | + * @date 2015-2-26 | ||
| 11 | + */ | ||
| 12 | +public class SimpleChatClientHandler extends SimpleChannelInboundHandler<String> { | ||
| 13 | + @Override | ||
| 14 | + protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { | ||
| 15 | + System.out.println(s); | ||
| 16 | + } | ||
| 17 | +} |
| 1 | +package com.waylau.netty.demo.simplechat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 7 | +import io.netty.handler.codec.Delimiters; | ||
| 8 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 9 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 客户端 ChannelInitializer | ||
| 14 | + * | ||
| 15 | + * @author waylau.com | ||
| 16 | + * @date 2015-2-26 | ||
| 17 | + */ | ||
| 18 | +public class SimpleChatClientInitializer extends ChannelInitializer<SocketChannel> { | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 22 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 23 | + | ||
| 24 | + pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 25 | + pipeline.addLast("decoder", new StringDecoder()); | ||
| 26 | + pipeline.addLast("encoder", new StringEncoder()); | ||
| 27 | + pipeline.addLast("handler", new SimpleChatClientHandler()); | ||
| 28 | + } | ||
| 29 | +} |
| 1 | +package com.waylau.netty.demo.simplechat; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelOption; | ||
| 6 | +import io.netty.channel.EventLoopGroup; | ||
| 7 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 8 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 简单聊天服务器-服务端 | ||
| 12 | + * | ||
| 13 | + * @author waylau.com | ||
| 14 | + * @date 2015-2-16 | ||
| 15 | + */ | ||
| 16 | +public class SimpleChatServer { | ||
| 17 | + | ||
| 18 | + private int port; | ||
| 19 | + | ||
| 20 | + public SimpleChatServer(int port) { | ||
| 21 | + this.port = port; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public void run() throws Exception { | ||
| 25 | + | ||
| 26 | + EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) | ||
| 27 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 28 | + try { | ||
| 29 | + ServerBootstrap b = new ServerBootstrap(); // (2) | ||
| 30 | + b.group(bossGroup, workerGroup) | ||
| 31 | + .channel(NioServerSocketChannel.class) // (3) | ||
| 32 | + .childHandler(new SimpleChatServerInitializer()) //(4) | ||
| 33 | + .option(ChannelOption.SO_BACKLOG, 128) // (5) | ||
| 34 | + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) | ||
| 35 | + | ||
| 36 | + System.out.println("SimpleChatServer 启动了"); | ||
| 37 | + | ||
| 38 | + // 绑定端口,开始接收进来的连接 | ||
| 39 | + ChannelFuture f = b.bind(port).sync(); // (7) | ||
| 40 | + | ||
| 41 | + // 等待服务器 socket 关闭 。 | ||
| 42 | + // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 | ||
| 43 | + f.channel().closeFuture().sync(); | ||
| 44 | + | ||
| 45 | + } finally { | ||
| 46 | + workerGroup.shutdownGracefully(); | ||
| 47 | + bossGroup.shutdownGracefully(); | ||
| 48 | + | ||
| 49 | + System.out.println("SimpleChatServer 关闭了"); | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + public static void main(String[] args) throws Exception { | ||
| 54 | + int port; | ||
| 55 | + if (args.length > 0) { | ||
| 56 | + port = Integer.parseInt(args[0]); | ||
| 57 | + } else { | ||
| 58 | + port = 8080; | ||
| 59 | + } | ||
| 60 | + new SimpleChatServer(port).run(); | ||
| 61 | + | ||
| 62 | + } | ||
| 63 | +} |
| 1 | +package com.waylau.netty.demo.simplechat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 6 | +import io.netty.channel.group.ChannelGroup; | ||
| 7 | +import io.netty.channel.group.DefaultChannelGroup; | ||
| 8 | +import io.netty.util.concurrent.GlobalEventExecutor; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 服务端 channel | ||
| 12 | + * | ||
| 13 | + * @author waylau.com | ||
| 14 | + * @date 2015-2-16 | ||
| 15 | + */ | ||
| 16 | +public class SimpleChatServerHandler extends SimpleChannelInboundHandler<String> { // (1) | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * A thread-safe Set Using ChannelGroup, you can categorize Channels into a meaningful group. | ||
| 20 | + * A closed Channel is automatically removed from the collection, | ||
| 21 | + */ | ||
| 22 | + public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2) | ||
| 26 | + Channel incoming = ctx.channel(); | ||
| 27 | + | ||
| 28 | + // Broadcast a message to multiple Channels | ||
| 29 | + channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n"); | ||
| 30 | + | ||
| 31 | + channels.add(ctx.channel()); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + @Override | ||
| 35 | + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3) | ||
| 36 | + Channel incoming = ctx.channel(); | ||
| 37 | + | ||
| 38 | + // Broadcast a message to multiple Channels | ||
| 39 | + channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n"); | ||
| 40 | + | ||
| 41 | + // A closed Channel is automatically removed from ChannelGroup, | ||
| 42 | + // so there is no need to do "channels.remove(ctx.channel());" | ||
| 43 | + } | ||
| 44 | + @Override | ||
| 45 | + protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { // (4) | ||
| 46 | + Channel incoming = ctx.channel(); | ||
| 47 | + for (Channel channel : channels) { | ||
| 48 | + if (channel != incoming){ | ||
| 49 | + channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + s + "\n"); | ||
| 50 | + } else { | ||
| 51 | + channel.writeAndFlush("[you]" + s + "\n"); | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Override | ||
| 57 | + public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5) | ||
| 58 | + Channel incoming = ctx.channel(); | ||
| 59 | + System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"在线"); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + @Override | ||
| 63 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6) | ||
| 64 | + Channel incoming = ctx.channel(); | ||
| 65 | + System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"掉线"); | ||
| 66 | + } | ||
| 67 | + @Override | ||
| 68 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 69 | + Channel incoming = ctx.channel(); | ||
| 70 | + System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常"); | ||
| 71 | + // 当出现异常就关闭连接 | ||
| 72 | + cause.printStackTrace(); | ||
| 73 | + ctx.close(); | ||
| 74 | + } | ||
| 75 | +} |
| 1 | +package com.waylau.netty.demo.simplechat; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import io.netty.channel.ChannelInitializer; | ||
| 5 | +import io.netty.channel.ChannelPipeline; | ||
| 6 | +import io.netty.channel.socket.SocketChannel; | ||
| 7 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 8 | +import io.netty.handler.codec.Delimiters; | ||
| 9 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 10 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 服务端 ChannelInitializer | ||
| 14 | + * | ||
| 15 | + * @author waylau.com | ||
| 16 | + * @date 2015-2-26 | ||
| 17 | + */ | ||
| 18 | +public class SimpleChatServerInitializer extends | ||
| 19 | + ChannelInitializer<SocketChannel> { | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 23 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 24 | + | ||
| 25 | + pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 26 | + pipeline.addLast("decoder", new StringDecoder()); | ||
| 27 | + pipeline.addLast("encoder", new StringEncoder()); | ||
| 28 | + pipeline.addLast("handler", new SimpleChatServerHandler()); | ||
| 29 | + | ||
| 30 | + System.out.println("SimpleChatClient:"+ch.remoteAddress() +"连接上"); | ||
| 31 | + } | ||
| 32 | +} |
| 1 | +package com.waylau.netty.demo.telnet; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.Bootstrap; | ||
| 4 | +import io.netty.channel.Channel; | ||
| 5 | +import io.netty.channel.ChannelFuture; | ||
| 6 | +import io.netty.channel.EventLoopGroup; | ||
| 7 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 8 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 9 | +import io.netty.handler.ssl.SslContext; | ||
| 10 | +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; | ||
| 11 | + | ||
| 12 | +import java.io.BufferedReader; | ||
| 13 | +import java.io.InputStreamReader; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * Simplistic telnet client. | ||
| 17 | + */ | ||
| 18 | +public final class TelnetClient { | ||
| 19 | + | ||
| 20 | + static final boolean SSL = System.getProperty("ssl") != null; | ||
| 21 | + static final String HOST = System.getProperty("host", "127.0.0.1"); | ||
| 22 | + static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8992" : "8023")); | ||
| 23 | + | ||
| 24 | + public static void main(String[] args) throws Exception { | ||
| 25 | + // Configure SSL. | ||
| 26 | + final SslContext sslCtx; | ||
| 27 | + if (SSL) { | ||
| 28 | + sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); | ||
| 29 | + } else { | ||
| 30 | + sslCtx = null; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 34 | + try { | ||
| 35 | + Bootstrap b = new Bootstrap(); | ||
| 36 | + b.group(group) | ||
| 37 | + .channel(NioSocketChannel.class) | ||
| 38 | + .handler(new TelnetClientInitializer(sslCtx)); | ||
| 39 | + | ||
| 40 | + // Start the connection attempt. | ||
| 41 | + Channel ch = b.connect(HOST, PORT).sync().channel(); | ||
| 42 | + | ||
| 43 | + // Read commands from the stdin. | ||
| 44 | + ChannelFuture lastWriteFuture = null; | ||
| 45 | + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); | ||
| 46 | + for (;;) { | ||
| 47 | + String line = in.readLine(); | ||
| 48 | + if (line == null) { | ||
| 49 | + break; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + // Sends the received line to the server. | ||
| 53 | + lastWriteFuture = ch.writeAndFlush(line + "\r\n"); | ||
| 54 | + | ||
| 55 | + // If user typed the 'bye' command, wait until the server closes | ||
| 56 | + // the connection. | ||
| 57 | + if ("bye".equals(line.toLowerCase())) { | ||
| 58 | + ch.closeFuture().sync(); | ||
| 59 | + break; | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + // Wait until all messages are flushed before closing the channel. | ||
| 64 | + if (lastWriteFuture != null) { | ||
| 65 | + lastWriteFuture.sync(); | ||
| 66 | + } | ||
| 67 | + } finally { | ||
| 68 | + group.shutdownGracefully(); | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | +} |
| 1 | +package com.waylau.netty.demo.telnet; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelHandler.Sharable; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Handles a client-side channel. | ||
| 9 | + */ | ||
| 10 | +@Sharable | ||
| 11 | +public class TelnetClientHandler extends SimpleChannelInboundHandler<String> { | ||
| 12 | + | ||
| 13 | + protected void messageReceived(ChannelHandlerContext ctx, String msg) { | ||
| 14 | + System.err.println(msg); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + @Override | ||
| 18 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 19 | + cause.printStackTrace(); | ||
| 20 | + ctx.close(); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + protected void channelRead0(ChannelHandlerContext ctx, String msg) | ||
| 25 | + throws Exception { | ||
| 26 | + // TODO Auto-generated method stub | ||
| 27 | + | ||
| 28 | + } | ||
| 29 | +} |
| 1 | +package com.waylau.netty.demo.telnet; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 7 | +import io.netty.handler.codec.Delimiters; | ||
| 8 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 9 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 10 | +import io.netty.handler.ssl.SslContext; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Creates a newly configured {@link ChannelPipeline} for a new channel. | ||
| 14 | + */ | ||
| 15 | +public class TelnetClientInitializer extends ChannelInitializer<SocketChannel> { | ||
| 16 | + | ||
| 17 | + private static final StringDecoder DECODER = new StringDecoder(); | ||
| 18 | + private static final StringEncoder ENCODER = new StringEncoder(); | ||
| 19 | + | ||
| 20 | + private static final TelnetClientHandler CLIENT_HANDLER = new TelnetClientHandler(); | ||
| 21 | + | ||
| 22 | + private final SslContext sslCtx; | ||
| 23 | + | ||
| 24 | + public TelnetClientInitializer(SslContext sslCtx) { | ||
| 25 | + this.sslCtx = sslCtx; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public void initChannel(SocketChannel ch) { | ||
| 30 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 31 | + | ||
| 32 | + if (sslCtx != null) { | ||
| 33 | + pipeline.addLast(sslCtx.newHandler(ch.alloc(), TelnetClient.HOST, TelnetClient.PORT)); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + // Add the text line codec combination first, | ||
| 37 | + pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 38 | + pipeline.addLast(DECODER); | ||
| 39 | + pipeline.addLast(ENCODER); | ||
| 40 | + | ||
| 41 | + // and then business logic. | ||
| 42 | + pipeline.addLast(CLIENT_HANDLER); | ||
| 43 | + } | ||
| 44 | +} |
| 1 | +package com.waylau.netty.demo.telnet; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.EventLoopGroup; | ||
| 5 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 6 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 7 | +import io.netty.handler.logging.LogLevel; | ||
| 8 | +import io.netty.handler.logging.LoggingHandler; | ||
| 9 | +import io.netty.handler.ssl.SslContext; | ||
| 10 | +import io.netty.handler.ssl.util.SelfSignedCertificate; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Simplistic telnet server. | ||
| 14 | + */ | ||
| 15 | +public final class TelnetServer { | ||
| 16 | + | ||
| 17 | + static final boolean SSL = System.getProperty("ssl") != null; | ||
| 18 | + static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8992" : "8023")); | ||
| 19 | + | ||
| 20 | + public static void main(String[] args) throws Exception { | ||
| 21 | + // Configure SSL. | ||
| 22 | + final SslContext sslCtx; | ||
| 23 | + if (SSL) { | ||
| 24 | + SelfSignedCertificate ssc = new SelfSignedCertificate(); | ||
| 25 | + sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); | ||
| 26 | + } else { | ||
| 27 | + sslCtx = null; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + EventLoopGroup bossGroup = new NioEventLoopGroup(1); | ||
| 31 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 32 | + try { | ||
| 33 | + ServerBootstrap b = new ServerBootstrap(); | ||
| 34 | + b.group(bossGroup, workerGroup) | ||
| 35 | + .channel(NioServerSocketChannel.class) | ||
| 36 | + .handler(new LoggingHandler(LogLevel.INFO)) | ||
| 37 | + .childHandler(new TelnetServerInitializer(sslCtx)); | ||
| 38 | + | ||
| 39 | + b.bind(PORT).sync().channel().closeFuture().sync(); | ||
| 40 | + } finally { | ||
| 41 | + bossGroup.shutdownGracefully(); | ||
| 42 | + workerGroup.shutdownGracefully(); | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | +} |
| 1 | +package com.waylau.netty.demo.telnet; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelFuture; | ||
| 4 | +import io.netty.channel.ChannelFutureListener; | ||
| 5 | +import io.netty.channel.ChannelHandler.Sharable; | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 8 | + | ||
| 9 | +import java.net.InetAddress; | ||
| 10 | +import java.util.Date; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Handles a server-side channel. | ||
| 14 | + */ | ||
| 15 | +@Sharable | ||
| 16 | +public class TelnetServerHandler extends SimpleChannelInboundHandler<String> { | ||
| 17 | + | ||
| 18 | + @Override | ||
| 19 | + public void channelActive(ChannelHandlerContext ctx) throws Exception { | ||
| 20 | + // Send greeting for a new connection. | ||
| 21 | + ctx.write("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n"); | ||
| 22 | + ctx.write("It is " + new Date() + " now.\r\n"); | ||
| 23 | + ctx.flush(); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + public void messageReceived(ChannelHandlerContext ctx, String request) { | ||
| 27 | + // Generate and write a response. | ||
| 28 | + String response; | ||
| 29 | + boolean close = false; | ||
| 30 | + if (request.isEmpty()) { | ||
| 31 | + response = "Please type something.\r\n"; | ||
| 32 | + } else if ("bye".equals(request.toLowerCase())) { | ||
| 33 | + response = "Have a good day!\r\n"; | ||
| 34 | + close = true; | ||
| 35 | + } else { | ||
| 36 | + response = "Did you say '" + request + "'?\r\n"; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + // We do not need to write a ChannelBuffer here. | ||
| 40 | + // We know the encoder inserted at TelnetPipelineFactory will do the conversion. | ||
| 41 | + ChannelFuture future = ctx.write(response); | ||
| 42 | + | ||
| 43 | + // Close the connection after sending 'Have a good day!' | ||
| 44 | + // if the client has sent 'bye'. | ||
| 45 | + if (close) { | ||
| 46 | + future.addListener(ChannelFutureListener.CLOSE); | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @Override | ||
| 51 | + public void channelReadComplete(ChannelHandlerContext ctx) { | ||
| 52 | + ctx.flush(); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @Override | ||
| 56 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 57 | + cause.printStackTrace(); | ||
| 58 | + ctx.close(); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + @Override | ||
| 62 | + protected void channelRead0(ChannelHandlerContext ctx, String msg) | ||
| 63 | + throws Exception { | ||
| 64 | + // TODO Auto-generated method stub | ||
| 65 | + | ||
| 66 | + } | ||
| 67 | +} |
| 1 | +package com.waylau.netty.demo.telnet; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.DelimiterBasedFrameDecoder; | ||
| 7 | +import io.netty.handler.codec.Delimiters; | ||
| 8 | +import io.netty.handler.codec.string.StringDecoder; | ||
| 9 | +import io.netty.handler.codec.string.StringEncoder; | ||
| 10 | +import io.netty.handler.ssl.SslContext; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * Creates a newly configured {@link ChannelPipeline} for a new channel. | ||
| 14 | + */ | ||
| 15 | +public class TelnetServerInitializer extends ChannelInitializer<SocketChannel> { | ||
| 16 | + | ||
| 17 | + private static final StringDecoder DECODER = new StringDecoder(); | ||
| 18 | + private static final StringEncoder ENCODER = new StringEncoder(); | ||
| 19 | + | ||
| 20 | + private static final TelnetServerHandler SERVER_HANDLER = new TelnetServerHandler(); | ||
| 21 | + | ||
| 22 | + private final SslContext sslCtx; | ||
| 23 | + | ||
| 24 | + public TelnetServerInitializer(SslContext sslCtx) { | ||
| 25 | + this.sslCtx = sslCtx; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 30 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 31 | + | ||
| 32 | + if (sslCtx != null) { | ||
| 33 | + pipeline.addLast(sslCtx.newHandler(ch.alloc())); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + // Add the text line codec combination first, | ||
| 37 | + pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); | ||
| 38 | + // the encoder and decoder are static as these are sharable | ||
| 39 | + pipeline.addLast(DECODER); | ||
| 40 | + pipeline.addLast(ENCODER); | ||
| 41 | + | ||
| 42 | + // and then business logic. | ||
| 43 | + pipeline.addLast(SERVER_HANDLER); | ||
| 44 | + } | ||
| 45 | +} |
| 1 | +package com.waylau.netty.demo.time; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.Bootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelInitializer; | ||
| 6 | +import io.netty.channel.ChannelOption; | ||
| 7 | +import io.netty.channel.EventLoopGroup; | ||
| 8 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 9 | +import io.netty.channel.socket.SocketChannel; | ||
| 10 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 11 | + | ||
| 12 | +public class TimeClient { | ||
| 13 | + | ||
| 14 | + public static void main(String[] args) throws Exception { | ||
| 15 | + | ||
| 16 | + String host = "127.0.0.1";// args[0]; | ||
| 17 | + int port = 8080;//Integer.parseInt(args[1]); | ||
| 18 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 19 | + | ||
| 20 | + try { | ||
| 21 | + Bootstrap b = new Bootstrap(); // (1) | ||
| 22 | + b.group(workerGroup); // (2) | ||
| 23 | + b.channel(NioSocketChannel.class); // (3) | ||
| 24 | + b.option(ChannelOption.SO_KEEPALIVE, true); // (4) | ||
| 25 | + b.handler(new ChannelInitializer<SocketChannel>() { | ||
| 26 | + @Override | ||
| 27 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 28 | + ch.pipeline().addLast(new TimeClientHandler()); | ||
| 29 | + } | ||
| 30 | + }); | ||
| 31 | + | ||
| 32 | + // 启动客户端 | ||
| 33 | + ChannelFuture f = b.connect(host, port).sync(); // (5) | ||
| 34 | + | ||
| 35 | + // 等待连接关闭 | ||
| 36 | + f.channel().closeFuture().sync(); | ||
| 37 | + } finally { | ||
| 38 | + workerGroup.shutdownGracefully(); | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | +} |
| 1 | +package com.waylau.netty.demo.time; | ||
| 2 | + | ||
| 3 | +import java.util.Date; | ||
| 4 | + | ||
| 5 | +import io.netty.buffer.ByteBuf; | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.channel.ChannelInboundHandlerAdapter; | ||
| 8 | + | ||
| 9 | +public class TimeClientHandler extends ChannelInboundHandlerAdapter { | ||
| 10 | + | ||
| 11 | + @Override | ||
| 12 | + public void channelRead(ChannelHandlerContext ctx, Object msg) { | ||
| 13 | + ByteBuf m = (ByteBuf) msg; // (1) | ||
| 14 | + try { | ||
| 15 | + long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L; | ||
| 16 | + System.out.println(new Date(currentTimeMillis)); | ||
| 17 | + ctx.close(); | ||
| 18 | + } finally { | ||
| 19 | + m.release(); | ||
| 20 | + } | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 25 | + cause.printStackTrace(); | ||
| 26 | + ctx.close(); | ||
| 27 | + } | ||
| 28 | +} |
| 1 | +package com.waylau.netty.demo.time; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | + | ||
| 5 | +import io.netty.channel.ChannelFuture; | ||
| 6 | +import io.netty.channel.ChannelInitializer; | ||
| 7 | +import io.netty.channel.ChannelOption; | ||
| 8 | +import io.netty.channel.EventLoopGroup; | ||
| 9 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 10 | +import io.netty.channel.socket.SocketChannel; | ||
| 11 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 时间服务器 | ||
| 15 | + */ | ||
| 16 | +public class TimeServer { | ||
| 17 | + | ||
| 18 | + private int port; | ||
| 19 | + | ||
| 20 | + public TimeServer(int port) { | ||
| 21 | + this.port = port; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public void run() throws Exception { | ||
| 25 | + EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) | ||
| 26 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 27 | + try { | ||
| 28 | + ServerBootstrap b = new ServerBootstrap(); // (2) | ||
| 29 | + b.group(bossGroup, workerGroup) | ||
| 30 | + .channel(NioServerSocketChannel.class) // (3) | ||
| 31 | + .childHandler(new ChannelInitializer<SocketChannel>() { // (4) | ||
| 32 | + @Override | ||
| 33 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 34 | + ch.pipeline().addLast(new TimeServerHandler()); | ||
| 35 | + } | ||
| 36 | + }) | ||
| 37 | + .option(ChannelOption.SO_BACKLOG, 128) // (5) | ||
| 38 | + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) | ||
| 39 | + | ||
| 40 | + // 绑定端口,开始接收进来的连接 | ||
| 41 | + ChannelFuture f = b.bind(port).sync(); // (7) | ||
| 42 | + | ||
| 43 | + // 等待服务器 socket 关闭 。 | ||
| 44 | + // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 | ||
| 45 | + f.channel().closeFuture().sync(); | ||
| 46 | + } finally { | ||
| 47 | + workerGroup.shutdownGracefully(); | ||
| 48 | + bossGroup.shutdownGracefully(); | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + public static void main(String[] args) throws Exception { | ||
| 53 | + int port; | ||
| 54 | + if (args.length > 0) { | ||
| 55 | + port = Integer.parseInt(args[0]); | ||
| 56 | + } else { | ||
| 57 | + port = 8080; | ||
| 58 | + } | ||
| 59 | + new TimeServer(port).run(); | ||
| 60 | + } | ||
| 61 | +} |
| 1 | +package com.waylau.netty.demo.time; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelFutureListener; | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.channel.ChannelInboundHandlerAdapter; | ||
| 8 | + | ||
| 9 | +public class TimeServerHandler extends ChannelInboundHandlerAdapter { | ||
| 10 | + | ||
| 11 | + @Override | ||
| 12 | + public void channelActive(final ChannelHandlerContext ctx) { // (1) | ||
| 13 | + final ByteBuf time = ctx.alloc().buffer(4); // (2) | ||
| 14 | + time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L)); | ||
| 15 | + | ||
| 16 | + final ChannelFuture f = ctx.writeAndFlush(time); // (3) | ||
| 17 | + f.addListener(new ChannelFutureListener() { | ||
| 18 | + @Override | ||
| 19 | + public void operationComplete(ChannelFuture future) { | ||
| 20 | + assert f == future; | ||
| 21 | + ctx.close(); | ||
| 22 | + } | ||
| 23 | + }); // (4) | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 28 | + cause.printStackTrace(); | ||
| 29 | + ctx.close(); | ||
| 30 | + } | ||
| 31 | +} |
| 1 | +package com.waylau.netty.demo.websocketchat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelFutureListener; | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.channel.DefaultFileRegion; | ||
| 8 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 9 | +import io.netty.handler.codec.http.DefaultFullHttpResponse; | ||
| 10 | +import io.netty.handler.codec.http.DefaultHttpResponse; | ||
| 11 | +import io.netty.handler.codec.http.FullHttpRequest; | ||
| 12 | +import io.netty.handler.codec.http.FullHttpResponse; | ||
| 13 | +import io.netty.handler.codec.http.HttpHeaders; | ||
| 14 | +import io.netty.handler.codec.http.HttpResponse; | ||
| 15 | +import io.netty.handler.codec.http.HttpResponseStatus; | ||
| 16 | +import io.netty.handler.codec.http.HttpVersion; | ||
| 17 | +import io.netty.handler.codec.http.LastHttpContent; | ||
| 18 | +import io.netty.handler.ssl.SslHandler; | ||
| 19 | +import io.netty.handler.stream.ChunkedNioFile; | ||
| 20 | + | ||
| 21 | +import java.io.File; | ||
| 22 | +import java.io.RandomAccessFile; | ||
| 23 | +import java.net.URISyntaxException; | ||
| 24 | +import java.net.URL; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * 处理 Http 请求 | ||
| 28 | + * @author waylau.com | ||
| 29 | + * @date 2015-3-26 | ||
| 30 | + */ | ||
| 31 | +public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { //1 | ||
| 32 | + private final String wsUri; | ||
| 33 | + private static final File INDEX; | ||
| 34 | + | ||
| 35 | + static { | ||
| 36 | + URL location = HttpRequestHandler.class.getProtectionDomain().getCodeSource().getLocation(); | ||
| 37 | + try { | ||
| 38 | + String path = location.toURI() + "WebsocketChatClient.html"; | ||
| 39 | + path = !path.contains("file:") ? path : path.substring(5); | ||
| 40 | + INDEX = new File(path); | ||
| 41 | + } catch (URISyntaxException e) { | ||
| 42 | + throw new IllegalStateException("Unable to locate WebsocketChatClient.html", e); | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + public HttpRequestHandler(String wsUri) { | ||
| 47 | + this.wsUri = wsUri; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @Override | ||
| 51 | + public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { | ||
| 52 | + if (wsUri.equalsIgnoreCase(request.getUri())) { | ||
| 53 | + ctx.fireChannelRead(request.retain()); //2 | ||
| 54 | + } else { | ||
| 55 | + if (HttpHeaders.is100ContinueExpected(request)) { | ||
| 56 | + send100Continue(ctx); //3 | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + RandomAccessFile file = new RandomAccessFile(INDEX, "r");//4 | ||
| 60 | + | ||
| 61 | + HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK); | ||
| 62 | + response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8"); | ||
| 63 | + | ||
| 64 | + boolean keepAlive = HttpHeaders.isKeepAlive(request); | ||
| 65 | + | ||
| 66 | + if (keepAlive) { //5 | ||
| 67 | + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length()); | ||
| 68 | + response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); | ||
| 69 | + } | ||
| 70 | + ctx.write(response); //6 | ||
| 71 | + | ||
| 72 | + if (ctx.pipeline().get(SslHandler.class) == null) { //7 | ||
| 73 | + ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length())); | ||
| 74 | + } else { | ||
| 75 | + ctx.write(new ChunkedNioFile(file.getChannel())); | ||
| 76 | + } | ||
| 77 | + ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); //8 | ||
| 78 | + if (!keepAlive) { | ||
| 79 | + future.addListener(ChannelFutureListener.CLOSE); //9 | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + file.close(); | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + private static void send100Continue(ChannelHandlerContext ctx) { | ||
| 87 | + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE); | ||
| 88 | + ctx.writeAndFlush(response); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + @Override | ||
| 92 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) | ||
| 93 | + throws Exception { | ||
| 94 | + Channel incoming = ctx.channel(); | ||
| 95 | + System.out.println("Client:"+incoming.remoteAddress()+"异常"); | ||
| 96 | + // 当出现异常就关闭连接 | ||
| 97 | + cause.printStackTrace(); | ||
| 98 | + ctx.close(); | ||
| 99 | + } | ||
| 100 | +} |
| 1 | +package com.waylau.netty.demo.websocketchat; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import io.netty.channel.Channel; | ||
| 5 | +import io.netty.channel.ChannelHandlerContext; | ||
| 6 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 7 | +import io.netty.channel.group.ChannelGroup; | ||
| 8 | +import io.netty.channel.group.DefaultChannelGroup; | ||
| 9 | +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; | ||
| 10 | +import io.netty.util.concurrent.GlobalEventExecutor; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 处理TextWebSocketFrame | ||
| 14 | + * | ||
| 15 | + * @author waylau.com | ||
| 16 | + * 2015年3月26日 | ||
| 17 | + */ | ||
| 18 | +public class TextWebSocketFrameHandler extends | ||
| 19 | + SimpleChannelInboundHandler<TextWebSocketFrame> { | ||
| 20 | + | ||
| 21 | + public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + protected void channelRead0(ChannelHandlerContext ctx, | ||
| 25 | + TextWebSocketFrame msg) throws Exception { // (1) | ||
| 26 | + Channel incoming = ctx.channel(); | ||
| 27 | + for (Channel channel : channels) { | ||
| 28 | + if (channel != incoming){ | ||
| 29 | + channel.writeAndFlush(new TextWebSocketFrame("[" + incoming.remoteAddress() + "]" + msg.text())); | ||
| 30 | + } else { | ||
| 31 | + channel.writeAndFlush(new TextWebSocketFrame("[you]" + msg.text() )); | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Override | ||
| 37 | + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2) | ||
| 38 | + Channel incoming = ctx.channel(); | ||
| 39 | + | ||
| 40 | + // Broadcast a message to multiple Channels | ||
| 41 | + channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 加入")); | ||
| 42 | + | ||
| 43 | + channels.add(incoming); | ||
| 44 | + System.out.println("Client:"+incoming.remoteAddress() +"加入"); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + @Override | ||
| 48 | + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3) | ||
| 49 | + Channel incoming = ctx.channel(); | ||
| 50 | + | ||
| 51 | + // Broadcast a message to multiple Channels | ||
| 52 | + channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 离开")); | ||
| 53 | + | ||
| 54 | + System.out.println("Client:"+incoming.remoteAddress() +"离开"); | ||
| 55 | + | ||
| 56 | + // A closed Channel is automatically removed from ChannelGroup, | ||
| 57 | + // so there is no need to do "channels.remove(ctx.channel());" | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Override | ||
| 61 | + public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5) | ||
| 62 | + Channel incoming = ctx.channel(); | ||
| 63 | + System.out.println("Client:"+incoming.remoteAddress()+"在线"); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + @Override | ||
| 67 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6) | ||
| 68 | + Channel incoming = ctx.channel(); | ||
| 69 | + System.out.println("Client:"+incoming.remoteAddress()+"掉线"); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + @Override | ||
| 73 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) // (7) | ||
| 74 | + throws Exception { | ||
| 75 | + Channel incoming = ctx.channel(); | ||
| 76 | + System.out.println("Client:"+incoming.remoteAddress()+"异常"); | ||
| 77 | + // 当出现异常就关闭连接 | ||
| 78 | + cause.printStackTrace(); | ||
| 79 | + ctx.close(); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | +} |
| 1 | +package com.waylau.netty.demo.websocketchat; | ||
| 2 | + | ||
| 3 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 4 | +import io.netty.channel.ChannelFuture; | ||
| 5 | +import io.netty.channel.ChannelOption; | ||
| 6 | +import io.netty.channel.EventLoopGroup; | ||
| 7 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 8 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Websocket 聊天服务器-服务端 | ||
| 12 | + * | ||
| 13 | + * @author waylau.com | ||
| 14 | + * @date 2015-3-7 | ||
| 15 | + */ | ||
| 16 | +public class WebsocketChatServer { | ||
| 17 | + | ||
| 18 | + private int port; | ||
| 19 | + | ||
| 20 | + public WebsocketChatServer(int port) { | ||
| 21 | + this.port = port; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public void run() throws Exception { | ||
| 25 | + | ||
| 26 | + EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) | ||
| 27 | + EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| 28 | + try { | ||
| 29 | + ServerBootstrap b = new ServerBootstrap(); // (2) | ||
| 30 | + b.group(bossGroup, workerGroup) | ||
| 31 | + .channel(NioServerSocketChannel.class) // (3) | ||
| 32 | + .childHandler(new WebsocketChatServerInitializer()) //(4) | ||
| 33 | + .option(ChannelOption.SO_BACKLOG, 128) // (5) | ||
| 34 | + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) | ||
| 35 | + | ||
| 36 | + System.out.println("WebsocketChatServer 启动了" + port); | ||
| 37 | + | ||
| 38 | + // 绑定端口,开始接收进来的连接 | ||
| 39 | + ChannelFuture f = b.bind(port).sync(); // (7) | ||
| 40 | + | ||
| 41 | + // 等待服务器 socket 关闭 。 | ||
| 42 | + // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 | ||
| 43 | + f.channel().closeFuture().sync(); | ||
| 44 | + | ||
| 45 | + } finally { | ||
| 46 | + workerGroup.shutdownGracefully(); | ||
| 47 | + bossGroup.shutdownGracefully(); | ||
| 48 | + | ||
| 49 | + System.out.println("WebsocketChatServer 关闭了"); | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + public static void main(String[] args) throws Exception { | ||
| 54 | + int port; | ||
| 55 | + if (args.length > 0) { | ||
| 56 | + port = Integer.parseInt(args[0]); | ||
| 57 | + } else { | ||
| 58 | + port = 8080; | ||
| 59 | + } | ||
| 60 | + new WebsocketChatServer(port).run(); | ||
| 61 | + | ||
| 62 | + } | ||
| 63 | +} |
| 1 | +package com.waylau.netty.demo.websocketchat; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelInitializer; | ||
| 4 | +import io.netty.channel.ChannelPipeline; | ||
| 5 | +import io.netty.channel.socket.SocketChannel; | ||
| 6 | +import io.netty.handler.codec.http.HttpObjectAggregator; | ||
| 7 | +import io.netty.handler.codec.http.HttpServerCodec; | ||
| 8 | +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; | ||
| 9 | +import io.netty.handler.stream.ChunkedWriteHandler; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 服务端 ChannelInitializer | ||
| 13 | + * | ||
| 14 | + * @author waylau.com | ||
| 15 | + * @date 2015-3-13 | ||
| 16 | + */ | ||
| 17 | +public class WebsocketChatServerInitializer extends | ||
| 18 | + ChannelInitializer<SocketChannel> { //1 | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + public void initChannel(SocketChannel ch) throws Exception {//2 | ||
| 22 | + ChannelPipeline pipeline = ch.pipeline(); | ||
| 23 | + | ||
| 24 | + pipeline.addLast(new HttpServerCodec()); | ||
| 25 | + pipeline.addLast(new HttpObjectAggregator(64*1024)); | ||
| 26 | + pipeline.addLast(new ChunkedWriteHandler()); | ||
| 27 | + pipeline.addLast(new HttpRequestHandler("/ws")); | ||
| 28 | + pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); | ||
| 29 | + pipeline.addLast(new TextWebSocketFrameHandler()); | ||
| 30 | + | ||
| 31 | + } | ||
| 32 | +} |
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + */ | ||
| 4 | +package com.waylau.netty.util; | ||
| 5 | + | ||
| 6 | +import java.io.ByteArrayInputStream; | ||
| 7 | +import java.io.ByteArrayOutputStream; | ||
| 8 | +import java.io.IOException; | ||
| 9 | +import java.io.ObjectInputStream; | ||
| 10 | +import java.io.ObjectOutputStream; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 说明: | ||
| 14 | + * | ||
| 15 | + * @author <a href="http://www.waylau.com">waylau.com</a> 2015年11月9日 | ||
| 16 | + */ | ||
| 17 | +public class ByteObjConverter { | ||
| 18 | + | ||
| 19 | + public static Object ByteToObject(byte[] bytes) { | ||
| 20 | + Object obj = null; | ||
| 21 | + ByteArrayInputStream bi = new ByteArrayInputStream(bytes); | ||
| 22 | + ObjectInputStream oi = null; | ||
| 23 | + try { | ||
| 24 | + oi = new ObjectInputStream(bi); | ||
| 25 | + obj = oi.readObject(); | ||
| 26 | + } catch (Exception e) { | ||
| 27 | + e.printStackTrace(); | ||
| 28 | + } finally { | ||
| 29 | + try { | ||
| 30 | + bi.close(); | ||
| 31 | + } catch (IOException e) { | ||
| 32 | + e.printStackTrace(); | ||
| 33 | + } | ||
| 34 | + try { | ||
| 35 | + oi.close(); | ||
| 36 | + } catch (IOException e) { | ||
| 37 | + e.printStackTrace(); | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + return obj; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public static byte[] ObjectToByte(Object obj) { | ||
| 44 | + byte[] bytes = null; | ||
| 45 | + ByteArrayOutputStream bo = new ByteArrayOutputStream(); | ||
| 46 | + ObjectOutputStream oo = null; | ||
| 47 | + try { | ||
| 48 | + oo = new ObjectOutputStream(bo); | ||
| 49 | + oo.writeObject(obj); | ||
| 50 | + bytes = bo.toByteArray(); | ||
| 51 | + } catch (Exception e) { | ||
| 52 | + e.printStackTrace(); | ||
| 53 | + } finally { | ||
| 54 | + try { | ||
| 55 | + bo.close(); | ||
| 56 | + } catch (IOException e) { | ||
| 57 | + e.printStackTrace(); | ||
| 58 | + } | ||
| 59 | + try { | ||
| 60 | + oo.close(); | ||
| 61 | + } catch (IOException e) { | ||
| 62 | + e.printStackTrace(); | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + return (bytes); | ||
| 66 | + } | ||
| 67 | +} |
src/main/resources/WebsocketChatClient.html
0 → 100644
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html> | ||
| 3 | +<head> | ||
| 4 | +<meta charset="UTF-8"> | ||
| 5 | +<title>WebSocket Chat</title> | ||
| 6 | +</head> | ||
| 7 | +<body> | ||
| 8 | + <script type="text/javascript"> | ||
| 9 | + var socket; | ||
| 10 | + if (!window.WebSocket) { | ||
| 11 | + window.WebSocket = window.MozWebSocket; | ||
| 12 | + } | ||
| 13 | + if (window.WebSocket) { | ||
| 14 | + socket = new WebSocket("ws://localhost:8080/ws"); | ||
| 15 | + socket.onmessage = function(event) { | ||
| 16 | + var ta = document.getElementById('responseText'); | ||
| 17 | + ta.value = ta.value + '\n' + event.data | ||
| 18 | + }; | ||
| 19 | + socket.onopen = function(event) { | ||
| 20 | + var ta = document.getElementById('responseText'); | ||
| 21 | + ta.value = "连接开启!"; | ||
| 22 | + }; | ||
| 23 | + socket.onclose = function(event) { | ||
| 24 | + var ta = document.getElementById('responseText'); | ||
| 25 | + ta.value = ta.value + "连接被关闭"; | ||
| 26 | + }; | ||
| 27 | + } else { | ||
| 28 | + alert("你的浏览器不支持 WebSocket!"); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + function send(message) { | ||
| 32 | + if (!window.WebSocket) { | ||
| 33 | + return; | ||
| 34 | + } | ||
| 35 | + if (socket.readyState == WebSocket.OPEN) { | ||
| 36 | + socket.send(message); | ||
| 37 | + } else { | ||
| 38 | + alert("连接没有开启."); | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | + </script> | ||
| 42 | + <form onsubmit="return false;"> | ||
| 43 | + <h3>WebSocket 聊天室:</h3> | ||
| 44 | + <textarea id="responseText" style="width: 500px; height: 300px;"></textarea> | ||
| 45 | + <br> | ||
| 46 | + <input type="text" name="message" style="width: 300px" value="Welcome to www.waylau.com"> | ||
| 47 | + <input type="button" value="发送消息" onclick="send(this.form.message.value)"> | ||
| 48 | + <input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空聊天记录"> | ||
| 49 | + </form> | ||
| 50 | + <br> | ||
| 51 | + <br> | ||
| 52 | + <a href="https://waylau.com/" >更多例子请访问 waylau.com</a> | ||
| 53 | +</body> | ||
| 54 | +</html> |
src/test/java/com/waylau/netty/AppTest.java
0 → 100644
| 1 | +package com.waylau.netty; | ||
| 2 | + | ||
| 3 | +import junit.framework.Test; | ||
| 4 | +import junit.framework.TestCase; | ||
| 5 | +import junit.framework.TestSuite; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Unit test for simple App. | ||
| 9 | + */ | ||
| 10 | +public class AppTest | ||
| 11 | + extends TestCase | ||
| 12 | +{ | ||
| 13 | + /** | ||
| 14 | + * Create the test case | ||
| 15 | + * | ||
| 16 | + * @param testName name of the test case | ||
| 17 | + */ | ||
| 18 | + public AppTest( String testName ) | ||
| 19 | + { | ||
| 20 | + super( testName ); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * @return the suite of tests being tested | ||
| 25 | + */ | ||
| 26 | + public static Test suite() | ||
| 27 | + { | ||
| 28 | + return new TestSuite( AppTest.class ); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * Rigourous Test :-) | ||
| 33 | + */ | ||
| 34 | + public void testApp() | ||
| 35 | + { | ||
| 36 | + assertTrue( true ); | ||
| 37 | + } | ||
| 38 | +} |
-
请 注册 或 登录 后发表评论