当前位置:  编程技术>移动开发
本页文章导读:
    ▪网络游戏服务器架构研究(-)代理服务器的简略实践        网络游戏服务器架构研究(-)代理服务器的简单实践 网络游戏的服务器框架中,通常需要代理服务器ProxyServer来解决高并发的请求。目前实现了一个很简单代理服务器,没有考虑高并发.........
    ▪ Toast和Looper。Handler消息循环机制        Toast和Looper。Handler消息循环机制。 (1) Looper类别用来为一个线程开启一个消息循环。默认情况下Android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper.........
    ▪ java 线程方面的几个诠注       java 线程方面的几个注解 转自: http://hi.baidu.com/youziguo/item/0d1c7004b1e9f1d4dde5b0edgccrJava同步注解(Annotation)解释。    JDK 5.0 以后增加了注解。就是@后面加一个名称。有其自身的含意。跟C#里的Attr.........

[1]网络游戏服务器架构研究(-)代理服务器的简略实践
    来源: 互联网  发布时间: 2014-02-18
网络游戏服务器架构研究(-)代理服务器的简单实践
网络游戏的服务器框架中,通常需要代理服务器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();

	}
}

    
[2] Toast和Looper。Handler消息循环机制
    来源: 互联网  发布时间: 2014-02-18
Toast和Looper。Handler消息循环机制。

(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();

问题代码:

Java代码  
  • private Handler myHandler = new Handler() {  
  •         public void handleMessage(Message msg) {  
  •                                 Timer timer = new Timer();  
  •                 timer.schedule(new TimerTask() {  
  •                     @Override  
  •                     public void run() {  
  •                         InputMethodManager m = (InputMethodManager) editText  
  •                                 .getContext().getSystemService(  
  •                                         Context.INPUT_METHOD_SERVICE);  
  •                         m.showSoftInput(editText, 0);  
  •                         //  
  •                         Looper.prepare();  
  •                         Toast.makeText(Main.this, "show", Toast.LENGTH_LONG).show();  
  •                         Looper.loop();  
  •                     }  
  •                 }, 1000);  
  •                 }  
  • }  
  •  

     

    Toast 和 Looper,一个属于 android.widget,一个属于 android.os,两个貌似联系不怎么紧密的类,却通过下面这个异常联系到了一起:

    Java代码  
  • E/AndroidRuntime( 1819): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()  
  • E/AndroidRuntime( 1819):        at android.os.Handler.<init>(Handler.java:121)  
  • E/AndroidRuntime( 1819):        at android.widget.Toast.<init>(Toast.java:68)  
  • E/AndroidRuntime( 1819):        at android.widget.Toast.makeText(Toast.java:231)  
  •  

    Handler.java:121

    Java代码  
  • 119        mLooper = Looper.myLooper();  
  • 120        if (mLooper == null) {  
  • 121            throw new RuntimeException(  
  • 122            "Can't create handler inside thread that has not called Looper.prepare()");}  
  •  

    Toast.java:68 ——>成员变量,在初始化时会跟着初始化

    Java代码  
  • 68    final Handler mHandler = new Handler();  
  • 由以上的错误信息可以看出:程序要创建 handler,但是发现Looper.prepare还没有被调用。通过 Android SDK 中的Reference可以看到,Looper、Handler 的调用是非常有讲究的,如下面示例代码:

     

    Java代码  
  • class LooperThread extends Thread {  
  •     public Handler mHandler;  
  •    
  •     public void run() {  
  •         Looper.prepare();  
  •         mHandler = new Handler() {  
  •             public void handleMessage(Message msg) {  
  •                 // process incoming messages here  
  •             }  
  •         };  
  •         Looper.loop();  
  •     }  
  • }  
  •  言归正题,继续寻找 Toast、Looper 和 Handler 三者之间的联系,也许谜底就能解开了。欲揭谜底,从源码入手是一条捷径。

    Toast.java 的第231行的代码是创建一个新的Toast实例,而实例化的过程中,就需要执行第68行,也就是声明并创建Handler(成员变量)的实例。那么来看Handler.java的第121行到底做了什么,如下所示:

    Java代码  
  • 119        mLooper = Looper.myLooper();  
  • 120        if (mLooper == null) {  
  • 121            throw new RuntimeException(  
  • 122            "Can't create handler inside thread that has not called Looper.prepare()");}  
  •  到此,距离真相的解开近了一大步,既然抛出了 RuntimeException,那么 mLooper 肯定是 null,但是为什么 Looper.myLooper() 会返回 null?继续进入到 Looper.java 中寻根究底。

     

    Java代码  
  • /** 
  •  * Return the Looper object associated with the current thread.  Returns 
  •  * null if the calling thread is not associated with a Looper. 
  •  */  
  • public static final Looper myLooper() {  
  •     return (Looper)sThreadLocal.get();  
  • }  
  •  以上就是 myLooper() 方法的真实面貌,通过注释可以看出问题的真正原因在于当前线程并没有绑定 Looper,返回为 null 是正确但非正常的结果。

     

    http://jeff-pluto-1874.iteye.com/blog/869710


        
    [3] java 线程方面的几个诠注
        来源: 互联网  发布时间: 2014-02-18
    java 线程方面的几个注解


    转自: 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类的直接锁对象保护。而不是这个类的某个实例的锁对象。


        
    最新技术文章:
    ▪Android开发之登录验证实例教程
    ▪Android开发之注册登录方法示例
    ▪Android获取手机SIM卡运营商信息的方法
    ▪Android实现将已发送的短信写入短信数据库的...
    ▪Android发送短信功能代码
    ▪Android根据电话号码获得联系人头像实例代码
    ▪Android中GPS定位的用法实例
    ▪Android实现退出时关闭所有Activity的方法
    ▪Android实现文件的分割和组装
    ▪Android录音应用实例教程
    ▪Android双击返回键退出程序的实现方法
    ▪Android实现侦听电池状态显示、电量及充电动...
    ▪Android获取当前已连接的wifi信号强度的方法
    ▪Android实现动态显示或隐藏密码输入框的内容
    ▪根据USER-AGENT判断手机类型并跳转到相应的app...
    ▪Android Touch事件分发过程详解
    ▪Android中实现为TextView添加多个可点击的文本
    ▪Android程序设计之AIDL实例详解
    ▪Android显式启动与隐式启动Activity的区别介绍
    ▪Android按钮单击事件的四种常用写法总结
    ▪Android消息处理机制Looper和Handler详解
    ▪Android实现Back功能代码片段总结
    ▪Android实用的代码片段 常用代码总结
    ▪Android实现弹出键盘的方法
    ▪Android中通过view方式获取当前Activity的屏幕截...
    ▪Android提高之自定义Menu(TabMenu)实现方法
    ▪Android提高之多方向抽屉实现方法
    ▪Android提高之MediaPlayer播放网络音频的实现方法...
    ▪Android提高之MediaPlayer播放网络视频的实现方法...
    ▪Android提高之手游转电视游戏的模拟操控
     


    站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3