CS 1.6 服务器信息读取_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > CS 1.6 服务器信息读取

CS 1.6 服务器信息读取

 2011/10/24 8:01:48  lchshu001  http://lchshu001.iteye.com  我要评论(0)
  • 摘要:最近在做一个CS约战平台的东西,实现类似HLSW读取CS服务器信息的工具。研究了一下CS1.6服务器udp协议的东西。分享一下。对UDP协议不熟悉,可能有错误的地方请大家指点。这个是协议的的全文http://developer.valvesoftware.com/wiki/Server_Queries协议提供了5个请求:*Theserverrespondsto5queries:*A2A_PING*Pingtheserver
  • 标签:服务器 服务
最近在做一个CS约战平台的东西,实现类似 HLSW 读取CS服务器信息的工具。研究了一下CS1.6服务器udp协议的东西。分享一下。
对UDP协议不熟悉,可能有错误的地方请大家指点。
这个是协议的的全文
http://developer.valvesoftware.com/wiki/Server_Queries

协议提供了 5 个请求:
 * The server responds to 5 queries:
 * A2A_PING
 * Ping the server.
// ping 服务器
 * A2S_SERVERQUERY_GETCHALLENGE
 * Returns a challenge number for use in the player and rules query.
//challenge number 应该是用于表示一个用户请求。
 * A2S_INFO
 * Basic information about the server.
//服务器信息
 * A2S_PLAYER
 * Details about each player on the server.
//列出服务器所有用户
 * A2S_RULES
 * The rules the server is using.
//服务器的规则
 * Queries should be sent in UDP packets to the listen port of the server, which is typically port 27015.
//UDP 的数据包,服务器默认端口是 27015


五个请求的内容是:

private static final byte[] A2S_INFO_BYTE = 
 new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
  0x54,//T
  'S', 'o', 'u', 'r', 'c', 'e', ' ', 'E', 'n', 'g', 'i', 'n', 'e', ' ', 'Q', 'u', 'e', 'r', 'y', 
  0x00};

private static final byte[] A2S_PLAYER_BYTE =
 new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
  0x55,//U
  (byte) 0xFF,(byte) 0xFF,(byte) 0xFF,(byte) 0xFF,//四位 0xFF 可能被替换为challenge number
  0x00};

private static final byte[] A2S_RULES_BYTE = 
 new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
  0x56,//V
  (byte) 0xFF,(byte) 0xFF,(byte) 0xFF,(byte) 0xFF,//四位 0xFF 可能被替换为challenge number
  0x00};

private static final byte[] A2S_SERVERQUERY_GETCHALLENGE_BYTE =
 new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
  0x57,//W
  0x00};
	
private static final byte[] A2A_PING_BYTE = 
 new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
  0x69,//i
  0x00};


服务器返回的数据包格式:
数据包除去IP/UDP 头部,最大长度为 1400 bytes。
如果单个数据包传输数据则开始的四个字节是 -1 (0xFF,0xFF,0xFF,0xFF)后面是数据。

如果多个数据包传输数据则开始的四个字节是 -2 (0xFE,0xFF,0xFF,0xFF),后面是四个字节的Request ID(对一次请求返回的多个数据包是相同的且唯一),之后是一个字节的数据包大小和编号,低位的4bit是表示请求返回的包的总数,高位的4bit是表示当前包的编号(从零开始)(协议中说 Source Engine 引擎,会用两个字节来标识,但未发现这种服务器,而且后面有两个字节的 Split Length 也没有发现)。第0个数据包开头有四个字节 (0xFF,0xFF,0xFF,0xFF),其他数据包没有。

socket.receive(datapack);
Frame f = new Frame(HelpUtil.getSubBytes(datapack.getData(), 0, datapack.getLength()));
if(f.isHasNext()){
	log.debug("has next!");
	//if(type == SOURCE_OR_SHIP_SERVERS) throw new UnsupportedOperationException(" do not have implient for source agent! ");
	byte[][] bts = new byte[f.getDataLength()][];
	bts[f.getIndex()] = f.getData();
	for(int i=1; i < bts.length; i++){
		socket.receive(datapack);
		Frame next = new Frame(HelpUtil.
				getSubBytes(datapack.getData(), 0, datapack.getLength()));
		if(!next.isHasNext() || next.getId() != f.getId()){
			i--;
			log.warn("one package frame is discard!");
			//不应该丢弃包,应该根据第一个字节判断返回的类型。
			continue;
		}else{
			bts[next.getIndex()] = next.getData();
			if(i >= bts.length ) break;
		}
	}
	ByteArrayOutputStream array = new ByteArrayOutputStream();
	for(int i=0; i<bts.length; i++){
		array.write(bts[i]);
	}




服务器返回的数据起始的一个字节都是标识位(除去开头的 0xFF,0xFF,0xFF,0xFF),这个是后来才发现的,所以现在的设计上有很大问题。如:ping 第一个字节是 'j',A2S_PLAYER 是 'D' ......

如果服务器返回的数据中是字符串 则是以 UTF-8 编码, 并且以 0x00 做为字符串结尾标识。
  • A2A_PING 请求:
  • 用户发现服务器是不是可以连接(或检查服务器类型)。
    第一个字节 是  'j'(0x6A)
    Goldsource servers 引擎的服务器返回

    0x6A,0x00


    Source servers 引擎的服务器返回(这种服务器暂时未发现)

    0x6A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

  • A2S_SERVERQUERY_GETCHALLENGE 请求:

  • 第一个字节 是 'A' (0x41)
    接下来是 4 个字节的 challenge number 用于 A2S_PLAYER 和 A2S_RULES 这个请求的时候替换请求的后四个字节。
    很多服务器不支持该请求。
  • A2S_INFO 请求:

  • 区分两种服务器
    第一个字节分别是  'I','m'(貌似区分是正版服务器,还是盗版服务器,这个不确定)

    m 类型:
    	int i = 0, t;
    	info.setType(bts[i]);	//m / I
    	t = i + 1;
    if(info.getType() == 'm'){
    	i = HelpUtil.find(bts, t, (byte) 0x00);//ip:port
    	t = i + 1;
    	i = HelpUtil.find(bts, t, (byte) 0x00);//server name
    	if(i >= 0){
    		info.setName(new String(HelpUtil.getSubBytes(bts, t, i-t), DEFAULT_CHARSET));
    	}
    	t = i + 1;
    	i = HelpUtil.find(bts, t, (byte) 0x00);//map
    	if(i >= 0){
    		info.setMap(new String(HelpUtil.getSubBytes(bts, t, i-t), DEFAULT_CHARSET));
    	}
    	t = i + 1;
    	i = HelpUtil.find(bts, t, (byte) 0x00);//Game Directory
    	t = i + 1;
    	i = HelpUtil.find(bts, t, (byte) 0x00);//Game Description
    	if(i >= 0){
    		info.setDescription(new String(HelpUtil.getSubBytes(bts, t, i-t), DEFAULT_CHARSET));
    	}
    	t = i + 1;
    	info.setCurrentPlayers(bts[t]);
    	t += 1;
    	info.setMaxPlayers(bts[t]);
    	t += 1; //version
    	t += 1; //Dedicated
    	t += 1; //OS
    	info.setNeedPassword(bts[t++] != 0);//Password
    	t += 1; //IsMod
    	i = t;
    	if(bts[t] == 0x01){
    		t = i + 1;
    		i = HelpUtil.find(bts, t, (byte) 0x00); //URLInfo
    		t = i + 1;
    		i = HelpUtil.find(bts, t, (byte) 0x00); //URLDL
    		t = i + 1; //Nul
    		t += 4; //ModVersion
    		t += 4; //ModSize
    		t += 1; //SvOnly
    		t += 1; //ClDLL
    	}
    	t += 1; //Secure
    	t += 1;//Number of bots
    	byte end = bts[t];//不应该溢出
    }
    


    对于'I'类型

    if(info.type == 'I'){
    	int version = bts[t++];//version
    	i = HelpUtil.find(bts, t, (byte) 0x00);//server name
    	if(i >= 0){
    		info.setName(new String(HelpUtil.getSubBytes(bts, t, i-t), DEFAULT_CHARSET));
    	}
    	t = i + 1;
    	i = HelpUtil.find(bts, t, (byte) 0x00);//Map
    	if(i >= 0){
    		info.setMap(new String(HelpUtil.getSubBytes(bts, t, i-t), DEFAULT_CHARSET));
    	}
    	t = i + 1;
    	i = HelpUtil.find(bts, t, (byte) 0x00);//Map
    	if(i >= 0){//Game Directory
    		//info.setMap(new String(HelpUtil.getSubBytes(bts, t, i-t), DEFAULT_CHARSET));
    	}
    	t = i + 1;
    	i = HelpUtil.find(bts, t, (byte) 0x00);//Map
    	if(i >= 0){//Game Description
    		info.setDescription(new String(HelpUtil.getSubBytes(bts, t, i-t), DEFAULT_CHARSET));
    	}
    	t = i + 1;
    	HelpUtil.toShort(bts[t++],bts[t++]); //AppID
    	info.setCurrentPlayers(bts[t++]);
    	info.setMaxPlayers(bts[t++]);
    	t++; //Number of bots
    	t++; //Dedicated	byte	 'l' for listen, 'd' for dedicated, 'p' for SourceTV
    	t++; //Host operating system. 'l' for Linux, 'w' for Windows
    	t++; //Password
    	t++; //Secure
    	
    	//后面的数据不清楚做什么用的  所以忽略掉了。
    }else{
    	throw new RuntimeException("donot support type [" + info.type + "]");
    }
    

  • A2S_PLAYER 请求:
  • 玩家的列表,名字,得分,在线时间

    int t = 0;
    byte type = bts[t++];//type  Should be equal to 'D' (0x44)
    int len = bts[t++];
    for(int i=0; i<len; i++){
    	Player p = new Player();
    	p.setIndex(bts[t++]);//序号
    	int u = HelpUtil.find(bts, t, (byte) 0x00);
    	p.setName(new String(HelpUtil.getSubBytes(bts, t, u - t), DEFAULT_CHARSET));//name
    	t = u + 1;
    	p.setKill(Integer.reverseBytes(HelpUtil.toInt(bts[t++],bts[t++],bts[t++],bts[t++]))); 
    		//Number of kills this player has
    	p.setConnectedTime(
    		Float.intBitsToFloat(//浮点类型  秒数,不清楚为什么用浮点数
    			Integer.reverseBytes(//比较奇怪为什么是反向的字节
    				HelpUtil.toInt(bts[t++],bts[t++],bts[t++],bts[t++])))); //(x)
    	//The time in seconds this player has been connected
    	list.add(p);
    }
    


  • A2S_RULES 请求:
  • 这个结构比较简单 字符串的键值对

    int t = 0;
    int type = bts[t++];//Should be equal to 'E' (0x45)
    short len = HelpUtil.toShort(bts[t++], bts[t++]);//The number of rules reported in this response
    // top is error!
    for(short i=0; ; i++){
    	int u = HelpUtil.find(bts, t, (byte) 0x00);
    	if(u == -1)  break;
    	String key = new String(HelpUtil.getSubBytes(bts, t, u-t));
    	t = u + 1;
    	u = HelpUtil.find(bts, t, (byte) 0x00);
    	String value = new String(HelpUtil.getSubBytes(bts, t, u-t));
    	t = u + 1;
    	map.put(key, value);
    }
    


    源码SVN地址:https://lineblog.googlecode.com/svn/trunk/
    目录:httpAnalysis/cs/ 下

    [完]

    转载请保留原文地址: http://lchshu001.iteye.com/blog/1207956 , 谢谢
    • cs.zip (8.7 KB)
    • 下载次数: 0
    发表评论
    用户名: 匿名