CS 1.6 服务器信息读取
最近在做一个CS约战平台的东西,实现类似 HLSW 读取CS服务器信息的工具。研究了一下CS1.6服务器udp协议的东西。分享一下。
对UDP协议不熟悉,可能有错误的地方请大家指点。
这个是协议的的全文
[url]http://developer.valvesoftware.com/wiki/Server_Queries[/url]
协议提供了 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/ 下
[完]
转载请保留原文地址: [url]http://lchshu001.iteye.com/blog/1207956[/url] , 谢谢
CS 1.6 服务器信息读取相关推荐
- python反恐精英代码_python下如何查询CS反恐精英的服务器信息
前言 服务器的相关知识曾经让我非常困惑.我相信还有很多的Python开发者和我有着类似的遭遇.本文主要介绍了python下如何查询CS反恐精英的服务器信息,有需要的可以参考学习. CS反恐精英1.5版 ...
- python反恐精英cs_python下如何查询CS反恐精英的服务器信息
前言 服务器的相关知识曾经让我非常困惑.我相信还有很多的Python开发者和我有着类似的遭遇.本文主要介绍了python下如何查询CS反恐精英的服务器信息,有需要的可以参考学习. CS反恐精英1.5版 ...
- 信息服务器怎么填写,如何设定服务器信息
如何设定服务器信息 内容精选 换一换 创建并登录弹性云服务器,请参见<弹性云服务器快速入门>中"购买弹性云服务器"和"登录弹性云服务器".该弹性云服 ...
- 网狐app端获取服务器信息,获取服务器信息 网狐客户端
获取服务器信息 网狐客户端 内容精选 换一换 华为云帮助中心,为用户提供产品简介.价格说明.购买指南.用户指南.API参考.最佳实践.常见问题.视频帮助等技术文档,帮助您快速上手使用华为云服务. 帐户 ...
- 怎么读服务器文件内容,服务器上读取文件内容
服务器上读取文件内容 内容精选 换一换 通过云服务器或者外部镜像文件创建私有镜像时,如果云服务器或镜像文件所在虚拟机的网络配置是静态IP地址时,您需要修改网卡属性为DHCP,以使私有镜像发放的新云服务 ...
- 客户读取文件服务器上文件的过程,java从服务器上读取文件
java从服务器上读取文件 [2021-02-03 06:36:28] 简介: php去除nbsp的方法:首先创建一个PHP代码示例文件:然后通过"preg_replace("/ ...
- 服务器上读取文件,服务器上读取文件
服务器上读取文件 内容精选 换一换 当创建文件系统后,您需要使用云服务器来挂载该文件系统,以实现多个云服务器共享使用文件系统的目的.CIFS类型的文件系统不支持使用Linux操作系统的云服务器进行挂载 ...
- 游戏获取服务器信息,易语言获取游戏服务器信息
易语言获取游戏服务器信息 内容精选 换一换 使用Touch对终端进行配置前需要先将Touch接入终端,接入方式包括TOUCH口连接.交换机网口连接和Wi-Fi连接.TOUCH口连接将Touch接入终端 ...
- 华为服务器磁盘没显示不出来,服务器磁盘读取不了
服务器磁盘读取不了 内容精选 换一换 查询弹性云服务器挂载的单个磁盘信息.该接口支持企业项目细粒度权限的校验,具体细粒度请参见 ecs:cloudServers:getGET /v1/{project ...
- python+shell 批量获取服务器信息并且生成excel表
我们工作中,经常需要获取服务器的一些信息,包括软件的和硬件的.例如刚刚接收的服务器,需要批量获取信息确认硬件配置是否符合我们需求,或者维护很长时间的服务器,中间变动了很多,但是服务器信息没有更改,这个 ...
最新文章
- java项目新东方在线源码_基于JSP的在线考试系统-JavaWeb项目-有源码
- MySQL配置主从同步过程记录
- 1008 Elevator (20 分)【难度: 简单 / 知识点: 模拟】
- 基于Bootstrap Ace模板+bootstrap.addtabs.js的菜单
- 持续集成之 Nuget 进阶
- 数独游戏技巧从入门到精通_如何引导孩子入门九宫格数独?掌握4个技巧口诀,孩子思维提升快...
- Golang——Println与键盘录入
- python使用ray框架改进原有代码,实现多进程与分布式
- android studio 预览问题 :java.lang.NoClassDefFoundError: com/android/util/PropertiesMap
- linux 安装sysstat使用iostat、mpstat、sar、sa(转载)
- 40.django中重要概念
- cadence Virtuoso ADE原理图AnalogLib库中的switch使用
- [转载]ubuntu samba Windows共享 你可能没有权限访问网络资源
- 【超超超easy】5分钟:自制酷炫猫咪词云图,会点鼠标即可。
- 【计算机网络】实验3:虚拟机配置测试实验
- MySQL练习(一)
- Python语句求一个正整数的全部约数
- edius pro9 安装教程
- 搜狐白社会1个月来的使用体验(转)
- Unity 结合数据库实现商城购买装备以及数据更新