`

基于netty4.0+spring的游戏完整架构

阅读更多
之前写过一篇和本文类似的博客,不过原博客是基于netty3.x实现的,今天整理了一份基于4.0的完整系统分享给大家,希望能对大家有所帮助。

架构细节原博客都有,请参照 http://cpjsjxy.iteye.com/blog/1587601

propholder.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation=" 
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd 
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>config/properties/settings.properties</value>
			</list>
		</property>
	</bean>
	<bean id="serverInitializer" class="com.cp.netty.ServerInitializer"
		init-method="init">
		<property name="timeout" value="${app.channel.readtimeout}" />
		<property name="handlerDispatcher" ref="handlerDispatcher" />
		<property name="requestType" value="${app.requestType}" />

	</bean>

	<bean id="handlerDispatcher" class="com.cp.dispatcher.HandlerDispatcher">
		<property name="messageExecutor">
			<bean class="com.cp.domain.FiexThreadPoolExecutor"
				destroy-method="shutdown">
				<constructor-arg value="${app.handler.pool.corePoolSize}" />
				<constructor-arg value="${app.handler.pool.maximumPoolSize}" />
				<constructor-arg value="${app.handler.pool.keepAliveSecond}" />
				<constructor-arg value="${app.handler.pool.name}" />
			</bean>
		</property>
		<property name="sleepTime" value="${app.handler.sleepTime}" />
		<property name="handlerMap" ref="gameHandlerMap" />
	</bean>

	<bean id="gameHandlerMap" class="java.util.HashMap">
		<constructor-arg>
			<map>
				<entry key="999">
					<bean class="com.cp.handler.InitHandler">
					</bean>
				</entry>
			</map>
		</constructor-arg>
	</bean>

</beans>



settings.properties
app.handler.pool.corePoolSize=16
app.handler.pool.maximumPoolSize=32
app.handler.pool.keepAliveSecond=300
app.handler.pool.name=gamework
app.handler.sleepTime=10
app.channel.readtimeout = 3600
#http  socket  websocket_text  websocket_binary
app.requestType=socket



测试类
TestClient

package com.cp.test;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpVersion;

import java.net.URI;

import com.cp.domain.ERequestType;

public class TestClient {
	public void connect(String host, int port, final ERequestType requestType)
			throws Exception {
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		String msg = "Are you ok?";
		if (ERequestType.SOCKET.equals(requestType)) {
			try {
				Bootstrap b = new Bootstrap();
				b.group(workerGroup);

				b.channel(NioSocketChannel.class).option(
						ChannelOption.TCP_NODELAY, true);
				b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
				b.handler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch)
							throws Exception {
						ch.pipeline().addLast(
								"encode",
								new LengthFieldBasedFrameDecoder(
										Integer.MAX_VALUE, 0, 4, 0, 4));
						ch.pipeline().addLast("decode",
								new LengthFieldPrepender(4));
						ch.pipeline().addLast("handler",
								new ClientInboundHandler());
					}
				});
				ChannelFuture f = b.connect(host, port).sync();
				ByteBuf messageData = Unpooled.buffer();
				messageData.writeInt(999);
				messageData.writeInt(msg.length());
				messageData.writeBytes(msg.getBytes());
				f.channel().writeAndFlush(messageData).sync();
				f.channel().closeFuture().sync();

			} catch (Exception e) {
				e.printStackTrace();
			}

		} else if (ERequestType.HTTP.equals(requestType)) {

			Bootstrap b = new Bootstrap();
			b.group(workerGroup);
			b.channel(NioSocketChannel.class);
			b.option(ChannelOption.SO_KEEPALIVE, true);
			b.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				public void initChannel(SocketChannel ch) throws Exception {

					// 客户端接收到的是httpResponse响应,所以要使用HttpResponseDecoder进行解码
					ch.pipeline().addLast(new HttpResponseDecoder());
					// 客户端发送的是httprequest,所以要使用HttpRequestEncoder进行编码
					ch.pipeline().addLast(new HttpRequestEncoder());
					ch.pipeline().addLast(new ClientInboundHandler());

				}
			});
			ChannelFuture f = b.connect(host, port).sync();
			b.channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY,true);
			b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);

			URI uri = new URI("http://" + host + ":" + port);

			DefaultFullHttpRequest request = new DefaultFullHttpRequest(
					HttpVersion.HTTP_1_1, HttpMethod.POST, uri.toASCIIString(),
					Unpooled.wrappedBuffer(msg.getBytes("UTF-8")));

			// 构建http请求
			request.headers().set(HttpHeaders.Names.HOST, host);
			request.headers().set(HttpHeaders.Names.CONNECTION,
					HttpHeaders.Values.KEEP_ALIVE);
			request.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
					request.content().readableBytes());
			// 发送http请求
			f.channel().write(request);
			f.channel().flush();
			f.channel().closeFuture().sync();
		}

		try {
		} finally {
			workerGroup.shutdownGracefully();
		}

	}

	public static void main(String[] args) throws Exception {
		TestClient client = new TestClient();
		client.connect("127.0.0.1", 8080, ERequestType.SOCKET);
	}
}



ClientInboundHandler
package com.cp.test;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;

public class ClientInboundHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		if (msg instanceof HttpResponse) {
			HttpResponse response = (HttpResponse) msg;
			System.out.println("CONTENT_TYPE:"
					+ response.headers().get(HttpHeaders.Names.CONTENT_TYPE));
		}
		if (msg instanceof HttpContent) {
			HttpContent content = (HttpContent) msg;
			ByteBuf buf = content.content();
			System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));
			buf.release();
		}
		if (msg instanceof ByteBuf) {
			ByteBuf messageData = (ByteBuf) msg;
			int commandId = messageData.readInt();
			int length = messageData.readInt();
			byte[] c = new byte[length];
			messageData.readBytes(c);
			System.out.println("commandId:"+commandId+"\tmessage:"+new String(c));
		}
	}
}


本测试代码已经过http、socket、websocket测试。

鉴于很多朋友想深入交流,特提供源码demo项目下载地址:
https://github.com/pofuchenzhou/netty-spring-game.git
分享到:
评论
13 楼 cpjsjxy 2016-05-17  
185248148 写道
没原码啊,,,

https://github.com/pofuchenzhou/netty-spring-game
12 楼 185248148 2016-05-05  
没原码啊,,,
11 楼 cpjsjxy 2016-01-21  
roway 写道
weiluo12@126.com求源码,谢谢!!!

https://github.com/pofuchenzhou/netty-spring-game
10 楼 roway 2016-01-21  
weiluo12@126.com求源码,谢谢!!!
9 楼 zcqshine 2015-09-06  
OK 了, 基于 netty5.0的 socket 请求方式调通过了. 谢谢博主
8 楼 zcqshine 2015-09-02  
请教下博主. 反编译了你的 jar 包后, 在有 switch 的地方反编译出来的语句很奇怪, 能提供下 command 类的源码吗, 还有 ERequest 这个枚举类里的请求类型的顺序是否为SOCKET("socket"), HTTP("http"), WEBSOCKET_TEXT("websocket_text"), WEBSOCKET_BINARY("websocket_binary")
如果是按照这个顺序来的话, 那么根据反编译出来的 command 类里的 switch 语句里的逻辑好像有问题.
7 楼 zcqshine 2015-09-02  
TestClient 类84行的代码要修改, 要删掉".channel(NioSocketChannel.class)"这部分, 因为在69行处已经设置过channelFactory了, 否则运行的时候会报错:java.lang.IllegalStateException: channelFactory set already.

对了, 我用的是 netty5.0
6 楼 kenjianyin 2015-04-27  
楼主,求源码~谢谢
5 楼 ketzi 2015-01-28  
socket,websocket可以使用同一个端口不?
4 楼 cpjsjxy 2014-11-26  
web_dig 写道
2593399875@qq.com 谢谢


反编译自己看吧
3 楼 web_dig 2014-11-21  
2593399875@qq.com 谢谢
2 楼 web_dig 2014-11-21  
楼主,求源码。
1 楼 yexiaoxiao1991 2014-11-13  
                 

相关推荐

Global site tag (gtag.js) - Google Analytics