最近在做一个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 服务器信息读取相关推荐

  1. python反恐精英代码_python下如何查询CS反恐精英的服务器信息

    前言 服务器的相关知识曾经让我非常困惑.我相信还有很多的Python开发者和我有着类似的遭遇.本文主要介绍了python下如何查询CS反恐精英的服务器信息,有需要的可以参考学习. CS反恐精英1.5版 ...

  2. python反恐精英cs_python下如何查询CS反恐精英的服务器信息

    前言 服务器的相关知识曾经让我非常困惑.我相信还有很多的Python开发者和我有着类似的遭遇.本文主要介绍了python下如何查询CS反恐精英的服务器信息,有需要的可以参考学习. CS反恐精英1.5版 ...

  3. 信息服务器怎么填写,如何设定服务器信息

    如何设定服务器信息 内容精选 换一换 创建并登录弹性云服务器,请参见<弹性云服务器快速入门>中"购买弹性云服务器"和"登录弹性云服务器".该弹性云服 ...

  4. 网狐app端获取服务器信息,获取服务器信息 网狐客户端

    获取服务器信息 网狐客户端 内容精选 换一换 华为云帮助中心,为用户提供产品简介.价格说明.购买指南.用户指南.API参考.最佳实践.常见问题.视频帮助等技术文档,帮助您快速上手使用华为云服务. 帐户 ...

  5. 怎么读服务器文件内容,服务器上读取文件内容

    服务器上读取文件内容 内容精选 换一换 通过云服务器或者外部镜像文件创建私有镜像时,如果云服务器或镜像文件所在虚拟机的网络配置是静态IP地址时,您需要修改网卡属性为DHCP,以使私有镜像发放的新云服务 ...

  6. 客户读取文件服务器上文件的过程,java从服务器上读取文件

    java从服务器上读取文件 [2021-02-03 06:36:28]  简介: php去除nbsp的方法:首先创建一个PHP代码示例文件:然后通过"preg_replace("/ ...

  7. 服务器上读取文件,服务器上读取文件

    服务器上读取文件 内容精选 换一换 当创建文件系统后,您需要使用云服务器来挂载该文件系统,以实现多个云服务器共享使用文件系统的目的.CIFS类型的文件系统不支持使用Linux操作系统的云服务器进行挂载 ...

  8. 游戏获取服务器信息,易语言获取游戏服务器信息

    易语言获取游戏服务器信息 内容精选 换一换 使用Touch对终端进行配置前需要先将Touch接入终端,接入方式包括TOUCH口连接.交换机网口连接和Wi-Fi连接.TOUCH口连接将Touch接入终端 ...

  9. 华为服务器磁盘没显示不出来,服务器磁盘读取不了

    服务器磁盘读取不了 内容精选 换一换 查询弹性云服务器挂载的单个磁盘信息.该接口支持企业项目细粒度权限的校验,具体细粒度请参见 ecs:cloudServers:getGET /v1/{project ...

  10. python+shell 批量获取服务器信息并且生成excel表

    我们工作中,经常需要获取服务器的一些信息,包括软件的和硬件的.例如刚刚接收的服务器,需要批量获取信息确认硬件配置是否符合我们需求,或者维护很长时间的服务器,中间变动了很多,但是服务器信息没有更改,这个 ...

最新文章

  1. java项目新东方在线源码_基于JSP的在线考试系统-JavaWeb项目-有源码
  2. MySQL配置主从同步过程记录
  3. 1008 Elevator (20 分)【难度: 简单 / 知识点: 模拟】
  4. 基于Bootstrap Ace模板+bootstrap.addtabs.js的菜单
  5. 持续集成之 Nuget 进阶
  6. 数独游戏技巧从入门到精通_如何引导孩子入门九宫格数独?掌握4个技巧口诀,孩子思维提升快...
  7. Golang——Println与键盘录入
  8. python使用ray框架改进原有代码,实现多进程与分布式
  9. android studio 预览问题 :java.lang.NoClassDefFoundError: com/android/util/PropertiesMap
  10. linux 安装sysstat使用iostat、mpstat、sar、sa(转载)
  11. 40.django中重要概念
  12. cadence Virtuoso ADE原理图AnalogLib库中的switch使用
  13. [转载]ubuntu samba Windows共享 你可能没有权限访问网络资源
  14. 【超超超easy】5分钟:自制酷炫猫咪词云图,会点鼠标即可。
  15. 【计算机网络】实验3:虚拟机配置测试实验
  16. MySQL练习(一)
  17. Python语句求一个正整数的全部约数
  18. edius pro9 安装教程
  19. 搜狐白社会1个月来的使用体验(转)
  20. Unity 结合数据库实现商城购买装备以及数据更新

热门文章

  1. 网络操作系统项目教程----Windows server 2003篇----打印机安装与配置
  2. 新建数据库监听端口被占用
  3. editormd文件上传
  4. JAVA NIO介绍及使用
  5. linux 常用查看日志命令--more 命令
  6. 硬件设计--阻抗匹配
  7. python 自动下载脚本_Python实现115网盘自动下载的方法
  8. 计算机操作系统有几种基本管理,操作系统有哪些管理功能
  9. net idautomationhc39m条形码字体生成条形码
  10. 系统仿真实践中的精益思维(随感)