网络游戏的服务器框架中,通常需要代理服务器ProxyServer来解决高并发的请求。
目前实现了一个很简单代理服务器,没有考虑高并发的问题,只是实现了从客户端、代理服务器和游戏服务器的简单通信。
从客户端发送数据,经过代理服务器转发,到达游戏服务器
游戏服务器反射数据到代理服务器,后者在传递给客户端;
1. 负责接收来自客户端的消息
2. 负责转发来自服务器的消息
3. 管理客户端连接
增加、删除客户端连接
每个连接唯一标记
4. 管理服务器端连接
根据serverid管理服务器端连接;
目前存在很多需要后续完善的地方
1. 客户端连接的管理,
代理服务器接收到客户端连接后,产生随机的clientid, 构造出ClientConnectIon, 并且保存在map中待用
目前的clientid是固定的
所以目前还不支持多个客户端连接
当然还涉及到客户端的断开的管理,从map中移除相关的连接;
2. 代理服务器的并发能力差
后续考虑采用netty 或者mina来解决
关键点是当客户端连接上来后,需要产生随机clientid, 保存到map中
从游戏服务器下来的数据,能够找到正确clientid的客户端连接
将数据传到正确的客户端中
netty4.x的版本貌似比较复杂,和3.5的版本比较,多了很多新的类和接口,需要些时间来学习;
3. 数据包的封装
一般都有数据包的封装,包头+包体;
包头比较包括TAG,bodylen,cmdtype等
代理服务器先这样,后面的关于游戏服务器的框架更加重要;
package net.tuolian.main; import net.tuolian.client.Client; import net.tuolian.proxy.SocketProxy; import net.tuolian.server.GameServer; public class TestAll { public static void main(String[] args) { // boot game server new GameServer(8080); try { Thread.sleep(1000 * 3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // boot socket proxy SocketProxy proxy = new SocketProxy(8081); proxy.connectToLogicServer("localhost", 8080); try { Thread.sleep(1000 * 3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // start client new Client("localhost", 8081).doTest(); } }
package net.tuolian.server; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; public class GameServer implements Runnable{ ServerSocket serverSocket = null; Logger logger = LogManager.getLogger(GameServer.class); public GameServer(int port) { try { serverSocket = new ServerSocket(port); logger.info("GameServer initialized"); new Thread(this).start(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { while(true) { try { Socket socket = serverSocket.accept(); logger.info("client accepted: " + socket.getRemoteSocketAddress()); new MyClient(socket); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } public void doTest() { new Thread(this).start(); } class MyClient extends Thread { DataInputStream dis = null; DataOutputStream dos = null; public MyClient(Socket socket) { try { dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); start(); logger.info("MyClient init"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { while(true) { byte[] tmp =new byte[1024]; try { if(dis.available()>0) { dis.read(tmp); logger.info("recv data from proxy len: " + new String(tmp)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // int cmd = 1; try { dos.write("this is echo from gameserver: ".getBytes()); dos.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
package net.tuolian.proxy; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Random; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; public class SocketProxy implements Runnable{ Logger logger = LogManager.getLogger(SocketProxy.class); ServerSocket sSocket = null; public SocketProxy(int port) { logger.info("SocketProxy init"); try { sSocket = new ServerSocket(port); new Thread(this).start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { while(true) { Socket client = null; try { client = sSocket.accept(); if(client!=null) { logger.info("accept client "); // new clientconn int clientId = new Random().nextInt(); ClientConnection clientConn = new ClientConnection(this, clientId,client); addClientConn(clientConn, 1); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void connectToLogicServer(String ip, int port) { logger.info("connectToLogicServer"); Socket socket; try { socket = new Socket(ip, port); ServerConnection serverConn =new ServerConnection(this, 1, socket); addServerConn(serverConn, 1); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private HashMap<Integer,ClientConnection> clientConns = new HashMap<Integer, ClientConnection>(); private HashMap<Integer,ServerConnection> serverConns = new HashMap<Integer, ServerConnection>(); public void addClientConn(ClientConnection clientConn, int clientId) { // TODO Auto-generated method stub clientConns.put(clientId, clientConn); } public ClientConnection getClientConn(int clientId) { return clientConns.get(clientId); } public void removeClientConn(int clientId) { clientConns.remove(clientId); } public void addServerConn(ServerConnection serverConn, int serverid) { // TODO Auto-generated method stub serverConns.put(serverid, serverConn); } public ServerConnection getServerConn(int serverid) { return serverConns.get(serverid); } public void removeServerConn(int serverId) { serverConns.remove(serverId); } }
package net.tuolian.proxy; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; public class ClientConnection implements Runnable { Logger logger = LogManager.getLogger(ClientConnection.class); SocketProxy proxy = null; int clientId = 0; Socket client = null; DataInputStream dis = null; DataOutputStream dos = null; public ClientConnection(SocketProxy proxy, int clientId, Socket socket) { logger.info("ClientConnection init"); this.proxy = proxy; this.clientId = clientId; this.client = socket; try { dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); new Thread(this).start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { while(true) { logger.info("run" ); try { byte[] rawData = new byte[1024]; while(dis.available()>0) { logger.info("recv client data: " ); dis.read(rawData); String msg = new String(rawData); logger.info(msg); int serverid = 1; ServerConnection severConn = proxy.getServerConn(serverid); if(severConn!=null) { severConn.sendData(rawData); } } Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void sendDataBack(byte[] data) { logger.info(new String(data)); try { dos.write(data); dos.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package net.tuolian.proxy; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; public class ServerConnection implements Runnable { Logger logger = LogManager.getLogger(ServerConnection.class); SocketProxy proxy = null; int serverId = 0; Socket client = null; DataInputStream dis = null; DataOutputStream dos = null; public ServerConnection(SocketProxy proxy, int serverId, Socket socket) { // TODO Auto-generated constructor stub logger.info("ServerConnection init"); this.proxy = proxy; this.serverId = serverId; this.client = socket; try { dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); new Thread(this).start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { while(true) { try { byte[] rawData = new byte[1024]; while(dis.available()>0) { dis.read(rawData); logger.info(new String(rawData)); // send to serverId; int clientId = 1; ClientConnection clientConn = proxy.getClientConn(clientId); if(clientConn!=null) { clientConn.sendDataBack(rawData); } } Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void sendDataBack(byte[] data) { try { dos.write(data); dos.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void sendData(byte[] data) { // TODO Auto-generated method stub try { dos.write(data); dos.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package net.tuolian.client; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; public class Client implements Runnable{ Logger logger = LogManager.getLogger(Client.class); public Client() { } Socket socket = null; DataInputStream dis = null; DataOutputStream dos = null; public Client(String ip, int port) { try { socket = new Socket(ip, port); dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); logger.info("connect to proxy server success"); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { while(true) { byte[] tmp =new byte[1024]; try { if(dis.available()>0) { dis.read(tmp); logger.info(new String(tmp)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { dos.write("hello from client ".getBytes()); dos.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void doTest() { new Thread(this).start(); } }
(1) Looper类别用来为一个线程开启一个消息循环。默认情况下Android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环)
Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。
(2) 通常是通过Handler对象来与Looper交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。
默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,在主线程中定义,其是与主线程的Looper绑定。
mainHandler = new Handler() 等价于new Handler(Looper.myLooper()).
Looper.myLooper():Return the Looper object associated with the current thread 获取当前进程的looper对象。
还有一个类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。
(3) 在非主线程中直接new Handler() 会报如下的错误:
E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。
(4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
(5) 基于以上知识,可实现主线程给子线程(非主线程)发送消息。
Toast或者Dialog中都有一个Handler的成员变量,在初始化时都会跟着初始化,而Toast或者Dialog中的Handler都需要一个Looper,所以需要在包含该Toast或者Dialog的线程中(如下面的Timer线程)初始化Looper。Looper.prepare();
问题代码:
Toast 和 Looper,一个属于 android.widget,一个属于 android.os,两个貌似联系不怎么紧密的类,却通过下面这个异常联系到了一起:
Handler.java:121
Toast.java:68 ——>成员变量,在初始化时会跟着初始化
由以上的错误信息可以看出:程序要创建 handler,但是发现Looper.prepare还没有被调用。通过 Android SDK 中的Reference可以看到,Looper、Handler 的调用是非常有讲究的,如下面示例代码:
言归正题,继续寻找 Toast、Looper 和 Handler 三者之间的联系,也许谜底就能解开了。欲揭谜底,从源码入手是一条捷径。
Toast.java 的第231行的代码是创建一个新的Toast实例,而实例化的过程中,就需要执行第68行,也就是声明并创建Handler(成员变量)的实例。那么来看Handler.java的第121行到底做了什么,如下所示:
到此,距离真相的解开近了一大步,既然抛出了 RuntimeException,那么 mLooper 肯定是 null,但是为什么 Looper.myLooper() 会返回 null?继续进入到 Looper.java 中寻根究底。
以上就是 myLooper() 方法的真实面貌,通过注释可以看出问题的真正原因在于当前线程并没有绑定 Looper,返回为 null 是正确但非正常的结果。
http://jeff-pluto-1874.iteye.com/blog/869710
转自: http://hi.baidu.com/youziguo/item/0d1c7004b1e9f1d4dde5b0ed
gccr
Java同步注解(Annotation)解释。
JDK 5.0 以后增加了注解。就是@后面加一个名称。有其自身的含意。跟C#里的Attributes似的。
Java并发编程中,用到了一些专门为并发编程准备的 Annotation。
主要包括三类:
1、类 Annotation(注解)
就像名字一样,这些注解是针对类的。主有要以下三个:
@Immutable
@ThreadSafe
@NotThreadSafe
@ThreadSafe 是表示这个类是线程安全的。具体是否真安全,那要看实现者怎么实现的了,反正打上这个标签只是表示一下。不线程安全的类打上这个注解也没事儿。
@Immutable 表示,类是不可变的,包含了 @ThreadSafe 的意思。
@NotThreadSafe 表示这个类不是线程安全的。如果是线程安全的非要打上这个注解,那也不会报错。
这三个注解,对用户和维护者是有益的,用户可以立即看出来这个类是否是线程安全的,维护者则是可以根据这个注解,重点检查线程安全方面。另外,代码分析工具可能会利用这个注解。
2、域 Annotation(注解)
域注解是对类里面成员变量加的注解。
3、方法 Annotation(注解)
方法注解是对类里面方法加的注解。
域注解和方法注解都是用@GuardedBy( lock )来标识。里面的Lock是告诉维护者:这个状态变量,这个方法被哪个锁保护着。这样可以强烈的提示类的维护者注意这里。
@GuardedBy( lock )有以下几种使用形式:
1、@GuardedBy( "this" ) 受对象内部锁保护
2、@GuardedBy( "fieldName" ) 受 与fieldName引用相关联的锁 保护。
3、@GuardedBy( "ClassName.fieldName" ) 受 一个类的静态field的锁 保存。
4、@GuardedBy( "methodName()" ) 锁对象是 methodName() 方法的返值,受这个锁保护。
5、@GuardedBy( "ClassName.class" ) 受 ClassName类的直接锁对象保护。而不是这个类的某个实例的锁对象。