Archive for the ‘JAVA’ Category

Java正则表达式取目标字符串简单范例

星期二, 六月 3rd, 2014 1 views

本篇只讲述JAVA正则表达式类java.util.regex的取字符串的使用方法,关于正则表达式的语法及使用不在本篇的描述之中,若有需要可参看《正则表达式30分钟入门教程》

首先做完范例需要一个待解析字符串,这里顺带把最近一个项目中的jdbcUrl拿来解析好了,字符串如下

1
jdbc:mysql://127.0.0.1:3306/test

这里说下目标需求,即把这段jdbcUrl分解成IP(127.0.0.1)、端口号(3306)以及数据库名(test),现将Java中的操作分为以下几步:

①书写正则表达式

正则表达式分别如下:

IP:(?<=//).*(?=:)取”//”和” :”之间的内容
PORT:(?<=\d:).*(?=/)取”数字+:”和” /”之间的内容
DBNAME:(?<=\d/).*”)取”数字+/”后面的内容

这里用正则表达式测试工具可以实时看到效果。
(更多…)

Netty4服务端心跳机制

星期六, 五月 17th, 2014 26 views

Netty4与Netty3.x的心跳机制略有不同,在Netty4中已经去掉了IdleStateAwareChannelHandler这个类,但IdleStateHandler依旧保留,只是心跳超时的触发事件的写法略有不同,Netty底层实现了一套类似信号和槽的事件通信机制。
这里且看实现。

首先是在 SocketChannel.pipeline 中注册 IdleStateHandler 进行心跳时间的定制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<socketchannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline()
                                    .addLast(
                                            new LoggingHandler(LogLevel.INFO),
                                            new IdleStateHandler(30,0,0),// 心跳控制
                                            new ServerHandler());
                        }
                    });

            // Start the server.
            ChannelFuture f = b.bind(port).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
</socketchannel>

这里要注意IdleStateHandler()要放在ServerHandler()的前面,否则心跳管理无法触发ServerHandler中的userEventTriggered()方法。

此时已经成功在服务器中创建了心跳控制器:
IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds)

Property Meaning
readerIdleTime an IdleStateEvent whose state is IdleState.READER_IDLE will be triggered when no read was performed for the specified period of time. Specify 0to disable.
writerIdleTime an IdleStateEvent whose state is IdleState.WRITER_IDLE will be triggered when no write was performed for the specified period of time. Specify 0 to disable.
allIdleTime an IdleStateEvent whose state is IdleState.ALL_IDLE will be triggered when neither read nor write was performed for the specified period of time. Specify 0 to disable.

(更多…)

Netty4客户端断线重连机制

星期五, 五月 16th, 2014 34 views

首先要了解一下Netty4客户端的运行机制,NettyClient.java代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class NettyClient {
    private final String host;
    private final int port;

    public NettyClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void run() throws Exception {
        // Configure the client.
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<socketchannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(
                             //new LoggingHandler(LogLevel.INFO),
                             new NettyHandler());
                 }
             });

            // Start the client.
            ChannelFuture f = b.connect(host, port).sync();

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();//运行后会在这里阻塞
        } finally {
            // Shut down the event loop to terminate all threads.
            group.shutdownGracefully();
        }
    }
}
</socketchannel>

当我们在主线程中执行 NettyClient(NettyClient).run()时,主线程会在 ChannelFuture f = b.connect(host, port).sync(); 这里阻塞,这是Netty4的一个新特性,我们利用这个特性即可创建一个定时器来实现断线重连的功能。

(更多…)

Protobuf在Java中的简单实例

星期日, 五月 11th, 2014 16 views

关于protobuf的介绍请大家自行查看https://code.google.com/p/protobuf/,本篇直奔主题——如何实现一个最简protobuf的Demo

①定义协议格式
首先定义.proto文件,可参考https://developers.google.com/protocol-buffers/docs/proto

大体格式如下:

1
2
3
4
5
package com.beitown.net.msg;//包名
message TestMsg {
    required int64 Id=1;
    required string Name=2;
}

每个字段后标记的“=1”、“=2”,是在二进制编码时使用的每个字段的唯一标识。在编码时,数字1~15要比大于它们的数字少一个字节,因此,作为一个优化选项,可以把1~15的数字用于常用的或重复性的元素。
将其保存为test.proto文件

②编译ProtocolBuffer
运行ProtocolBuffer编译器protoc来生成与.proto文件相关的类
首先我们已经有了一个protoc.exe文件(生成方式不再赘述,需要的可自行查找)
官方给出的编译语句为 protoc -I=$SRC_DIR –java_out=$DST_DIR $SRC_DIR/test.proto
打开cmd命令行窗口,先cd到protoc.exe所在文件夹为根文件夹,此时输出的java源码在根文件夹下的src子文件夹中,test.proto文件在proto子文件夹中,因此命令如下

1
protoc --java_out=src proto/test.proto

若出现提示 Expected top-level statement (e.g. “message”) 为编码格式问题,(win7下)解决方法是使用记事本打开后另存为ANSI格式。
编译通过后会在目标文件夹即可 src/com/beitown/net/msg 下生成一个Test.java文件,我们可以对这个类的操作来实现protobuf内容的读取和写入。

③创建Message实例-序列化
接下来将说明如何对上文中的Test类进行操作,上文中定义了TestMsg这个结构体,因此创建代码如下

1
2
3
TestMsg msg = TestMsg.newBuilder().setId(001).setName("beitown").build();
msg.toByteArray();
//发送msg.toByteArray()字节流

这里的setID,setName即为protoc自动生成Test.java中自带的方法。以上即可看出对于自定数据结构我们已经无需再去关心其从二进制转为消息类的过程,即将开发从通信层剥离出来,程序只需要关心消息类的处理逻辑。

③读取Message实例-反序列化

1
2
3
4
5
//接收到字节流
byte[] data = 二进制流对象转换的字节数组;
TestMsg msg = TestMsg.parseFrom(data);
int id = msg.getId();
String name =  msg.getName();

以上即为一个protobuf在java中的简单运用实例,通过对字节数组的发送和接收来实现protobuf消息的通信。
本篇针对初次接触protobuf的朋友做为学习参考之用,更多高级应用请自行举一反三。
本篇到此,谢谢关注。

BeiTown
2014-05-11

Android触摸事件及触摸屏滑动在游戏视角中的应用

星期二, 四月 23rd, 2013 53 views

Android触摸事件是建立于Ontuch事件相应机制上的,首先我们需要为Activity窗体添加一个OnTouchListener,实现OnTouchListener接口即可

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends Activity implements OnTouchListener {

Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
                button = (Button) findViewById(R.id.button1);
                button.setOnTouchListener(this);
    }
}

(更多…)

Android 4.0 Socket调试工具制作过程及源码发布

星期一, 四月 15th, 2013 450 views

首先是还是界面设计

分别是IP输入、Port输入、连接按钮、接受信息文本框、输入框、发送按钮。
界面设计好之后,为对应控件添加相应功能。
(更多…)

Android 主线程、子线程间的通信问题

星期日, 四月 14th, 2013 277 views

在Android中,子线程是不可以操作UI界面的,必须在主线程中操作,同时在主线程中也不能使用网络通信,必须在子线程中使用(4.0版本后的新特性)。所以Android中关于线程间的通信便显得相当重要。

这里需要使用Looper和Handler,利用Message进行基于消息队列的线程通信,简单介绍一下几个对象

Looper:管理当前线程的消息队列。
Handler: 向目标线程发送消息Handler.sendMessage(Message msg),处理消息队列中的消息Handler.handleMessage(Message msg);
Message:线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。
(更多…)

Android Socket TCP客户端简单实例

星期三, 四月 10th, 2013 740 views

声明:本篇代码仅可用于android 4.0以下版本,4.0以后的版本中网络连接的代码不可在主线程运行。解决方法见后续几篇博客。

由于Android中可以使用java.io.*包和java.net.*包,所以此Demo的逻辑代码和JAVA版本的client没有区别,主要区别还是在于UI和一些接口函数上。
我们抛开UI从最基础的说起。

①建立连接:

先来看建立连接的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainActivity extends Activity{
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        SocketAddress socketAddress = new InetSocketAddress("192.168.0.1", 2001);
        Socket socket = new Socket();  
       
        try {
            socket.connect(socketAddress,5000);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }      
    }
......
       
}

(更多…)

Netty游戏服务器开发——利用Channel绑定机制 共享聊天服务器与逻辑服务器信息

星期二, 十二月 18th, 2012 901 views

因为工作原因,之前做的网游项目一直没有时间对其技术点做一个统一的整理,今天正好朋友问到了这个问题,所以顺便把之前项目中的方案贴出来供大家参考。
首先我们需要明白一些概念,我们所玩的网络游戏的服务器是一台大规模的收发兼处理工厂,与客户端交互的数据流如同一个个快递。
Netty的NIO机制保证了服务器与每一个客户端之间都有一条特快专线,即Channel,游戏中的各种交互性数据即是通过这个Channel进行收发的。

现在问题来了,我们假设玩家的战斗行走等信息都是一个个包裹,聊天喊话信息都是一封封信件,客户端与服务器交互时,这些数据都是在一条该客户端独有的Channel里的,无论服务器还是客户端对数据的读取都是顺序执行的,如果此时客户端在接受一个角色战斗信息(包裹)时,聊天窗口有大量的聊天信息(信件)发来,那么客户端需要先读取完排在角色战斗信息(包裹)之前的聊天信息(信件)然后再读取战斗信息,接着才进行相应动画处理,这样就会产生一个delay,影响战斗的流畅感。反之服务器的接收也是如此。

所以为了保证在聊天信息大量收发的同时,战斗及其他游戏信息能够快速被相应,我们需要为每一个客户端再开辟一个收发聊天信息的Channel,把聊天信息通道分离出来。因此需要另外开始一个Netty服务器,即聊天服务器。
架设完成之后你将会看到,每个客户端与服务器之间都产生了2个Channel,分别为包裹专线,和信件专线。并且这两个服务器的Port是不一样的,需要客户端分别进行2次的连接。

这个时候又产生了一个问题,如果此时玩家要发送消息给周围的玩家或是自己的好友,而这些人物的列表并不在聊天服务器上,而是在逻辑服务器上。那么怎样将信息传达到对应人的Channel上呢。
传统的方法可能是在聊天服务器上再复制一份好友列表,甚至周围玩家列表,并且两个服务器之间时刻进行通信,以保证数据的同步性;或者每次发送聊天信息时,调用缓存区的数据,通过目标ID遍历寻找到目标的Channel。

我们来简单分析下这两种方案的运算开销:

方案一中,同步包的心跳最长不得超过1s,否则可能会出现玩家体验上的不同步。那么每1s同步一次,OK,我们设1000个玩家,每1s需要同步1000个玩家的各种信息,当然这显然不现实。所以继续优化方案一,改为触发型同步,即当逻辑服务器上的数据更新后,立即发送该更新数据的同步包给聊天服务器。此时1000个玩家如果聚集在主城附近来回跑动,最频繁的更新当属周围玩家列表的更新,或者采用的是九宫格灯塔AOI(此区域管理算法另外开篇说明)机制不需要周围玩家列表,但仍旧需要不停的更新自己的区域坐标——仍旧是大量的同步包。
(更多…)

HashMap中以动态对象作为Key值时的Remove陷阱

星期一, 十一月 5th, 2012 86 views

在游戏服务器上控制角色的坐标点可以通过Point这个类来实现,地图模块则以HashMap来承载。此问题从简单来说,既HashMap以Point作为Key值时,用户往往会忽略其中一个细节照成后期的非编译Bug。

以下是示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HashMap  <point ,String] hashMap = new HashMap<Point, String]();//把]换成>
Point A = new Point(2,2);
hashMap.put(A,"A");//将2,2点作为Key值存入HashMap
A.x = 1;//修改A点的x值
A.y = 1;//修改A点的y值
System.out.println(hashMap.get(A));//此时结果为Null 已经找不到了

//我们试着迭代一下
System.out.println(hashMap.size());//此处为1
for(Iterator<string ] iterator = hashMap.values().iterator();iterator.hasNext();)
{
    iterator.next();
    iterator.remove();
}
System.out.println(hashMap.size());//此处依旧为1 即无法销毁

(更多…)