欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于netty4.0+spring的游戏完整架构 博客分类: java 游戏  

程序员文章站 2024-03-06 19:19:32
...
之前写过一篇和本文类似的博客,不过原博客是基于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