《网络游戏核心技术与实战》笔记-1

《网络游戏核心技术与实战》笔记-1

网络和游戏编程的技术基础

网络分层结构

游戏开发的传输层大多使用TCP协议,不需要直接操纵网络层一下的分层。

应用层需要在游戏中予以实现。

高性能,高功能服务器的特性 —- 网络游戏所需具备的要素

  1. 小带宽
  2. 极高的连接数
  3. 低延迟
  4. 稳定

套接字

TPC通信链路的状态迁移和套接字API

  • socket()
  • connect()
  • bind()
  • listen()
  • accept()
  • read()/write()

处理并发连接的方法

  • 每次连接时启动一个进程
  • 实行异步的多重输入输出(多重I/O)
  • 使用线程并行进行同步处理

    网络游戏输入输出的特定 —- 单线程、非阻塞、事件驱动

libevent的特点

  • 使用套接字出与某个自定状态时(可以writ、也可以read、可以accept),调用实现指定的函数。
  • libevent库会自动选择各个OS中最高效的方法(比如,在Linux中套接字数量很多时,选择epoll函数等)来轮询当前状态。
  • 应用程序用事先设置的函数调用(成为回调函数)来获取这一结果,在回调函数中十级执行read,accept等本来应该在等待的函数。

RPC

自动生成RPC存根代码的RPC工具

网络游戏和二级制数据交换格式/库

网络游戏及其基本结构

游戏的基础 —— 认知、判断、操作的重复

网络游戏与测试

  • 封闭测试(CBT)
  • 公开测试(OBT)

不断更新 —— 网络游戏的运营和更新

  • 定期的补丁
  • 大型补丁(拓展包、追加包)
  • 紧急维护

游戏空间划分

  • 空间分割(地图分隔)
  • 平行世界(多服务器/区)
  • 实例化(副本)

服务器功能性划分

名称 缩略语 用途
客户端 gmsv 运行在用户客户端上
登陆服务器 loginsv 用于广利游戏数据的逻辑
数据库服务器 loginsv 登陆,负责服务整体使用情况的管理、解压缩以及解密等处理,减轻游戏服务器的负荷
反向代理服务器 proxy 接受连接,负责进行TCP会话本身的管理、解压缩以及解密等处理,减轻游戏服务器的负荷
消息服务器 msgsv 用于使网上聊天、即时消息、公会等社交活动能够跨越平行世界和空间分割来进行消息交换。
世界服务器 worksv 使用空间分割的情况下,属于该世界的所有gmsv连接至这个服务器进程,负责
通用数据库服务器(整体通用的数据库服务器) commondbsv 用于实现所有世界都需要的内存处理,实时统计处理和等级排名等处理

服务器硬件规格

  • 以CPU为中心的服务器:CPU较快,内核较多,内存一般,容错性低。一次性的。
  • 以储存为中心的服务器:CPU一般,内核一般,内存高,储存量大,容错性高。使用期长。

一般游戏需要持久化的信息

  • 用户(id、密码、计费方式等)
  • 角色(体力、经验、金钱、当前位置,名字等)
  • 技能
  • 任务日志 (正在进行、已完成)
  • 拍卖
  • 好友列表
  • 邮件
  • 公会
  • 日志

路径搜索和实际的移动处理

  • 阶段1:玩家自己进行移动路径的计算,实际的移动也在客户端进行,服务器无法限制地接受

  • 阶段2:与阶段1相同,但是在服务端加入一项限制:限制移动请求的接受频率

  • 阶段3:与阶段1相同,但是在服务端加入一项限制:限制移动请求的接受频率

  • 阶段3: 与阶段2相同,但是在move函数中加入客户端的发送时间,一起发送给服务器

  • 阶段4:服务端进行路径搜索和移动处理

  • 阶段5:在客户端中只进行路径搜索中的搜索处理

移动路径的发送方式 —- 优先发送最终结果

《Thinking in Java 》读书笔记-17

《Thinking in Java 》读书笔记-17

17.1 完整的容器分类法

这里写图片描述

17.2 填充容器

Collections.nCopies()和Collection.fill()都可以用单个对象的引用来填充Collection,并且所有引用都被设置为指向相同的的对象。
fill()方法的用处更为有限,它只能替换已经在List中存在的元素,而不能添加新的元素。

17.2.1 一种Generator解决方案

所有的Collection子类型都有一个接受另一个Collection对象的构造器。

17.2.2 Map生成器

未获支持的操作

17.2.3 使用Abstract类

17.3 Collection的功能方法

17.4 可选操作

执行各种不同的添加和移除的方法在Collection接口中都是可选操作。
使用Arrays.asList()生成的List,基于一个固定大小的数组,仅支持那些不会改变数组大小的操作。

17.5 List的功能方法

17.6 Set和存储顺序

接口 描述
Set 存入Set的每一个元素都必须是唯一的,因为Set不保存重复元素.加入Set的元素必须定义equals()方法以确保对象的唯一性. Set和Collection有完全一样的接口.Set接口不保证维护元素的次序.
HashSet * 为快速查找而设计的Set.存入HashSet的元素必须定义HashCode()方法.
TreeSet 保持次序的Set,底层为树结构.使用它可以从Set中提取有序的序列.元素必须实现Comparable接口
LinkedHashSet 具有HashSet的查询速度,且内部使用链表维护元素的顺序.于是在使用迭代器遍历Set时,结果会按元素插入的次序显示.元素也必须定义hashCode()方法.

17.7 队列

17.7.1 优先级队列

17.8 理解Map

17.9 散列与散列码

散列的价值在于速度。我们使用数组来保存键的信息,这个信息并不是键本身,而是通过键对象生成一个数字(散列码),作为数组下标。由于数组的容量是固定的,而散列容器的大小是可变的,所以不同的键可以产生相同的数组下标(散列码)。也就是说,可能会有冲突(当然也有特例,比如EnumMap和EnumSet)。所以,数组的值存放着一个保存所有相同散列码的值的list(引用)。然后对list中的值使用equals进行线性查询。如果散列函数设计的比较好的话,数组的每个位置只有较少的值,并且浪费空间也小。于是,查询过程就是首先计算键的散列码得到数组下标,然后内存寻址(时间复杂度为O(1),赋值)找到数组的值,再遍历list(时间复杂度为O(n),线性查询)即可。即hashCode和equals共同确定了对象的唯一性。

所有类都继承于Object。Object的hashCode方法生成的散列码,实际上是默认使用对象的地址计算散列码;而Object的equals方法实际上就是地址比较(==)。显然,当我们在使用散列容器(如HashMap的Key,HashSet等)时,我们自定义的类中还继承Object的hashCode和equals是不行的。必须重写hashCode和equals方法。好的hashCode()应该产生分布均匀的散列码。

17.10 选择接口的不同实现

17.11 实用方法

17.12 持有引用

  • SoftRefernce
  • WeakRefernce
  • PhantomRefernce

17.13 Java 1.0/1.1的容器

  1. 不要在新代码里使用Vector、Enumeration。
  2. 用LinkedLsit或基于LinlkedList的子类代替旧版的Stack。
  3. 如果拥有一个可以命名的固定的标志集合,那么EnumSet与BitSet相比是更好的选择。
  4. 只有在运行时才知道需要多少个标志,对标志命名不合理,需要BitSet中的某种特性操作。

总结

《Thinking in Java 》读书笔记-11

第11章 持有对象


11.1 泛型和类型安全的容器

1.2 基本概念

  • Collection
  • Map
  • List

1.3添加一组元素

构建一个不包含元素的Collection,然后调用Collection.addAll()这种方式,运行速度快,也比较方便,是首先方式。

使用Array.asList()可以使用显式类型参数说明产生的List的实际的目标类型。

1.4 容器的打印

使用Arrays.toString()来产生数组的可打印表示。

11.5 List

  • 基本的ArrayList,它长于随机访问元素,但是在List的中间插入移动元素时较慢。

  • LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但它的特性较ArrayList更大。

11.6 迭代器

Java的iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。

  1. 使用方法iterator()要求容器返回一个Iterator.Iterator将准备好返回序列的第一个元素。
  2. 使用next()获得序列中的下一个元素。
  3. 使用hashNext()检查序列中是否还有元素。
  4. 使用remove()将迭代器新近返回的元素删除。

Iterator可以移除由next()产生的最后一个元素,这意味着在调用remove()之前必须先调用next()。

LinkedList

LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。

peek()方法提供栈顶元素,但是并不将其从栈顶移除,而pop()将移除并返回栈顶元素。

11.9 Set

Set不保存重复的元素。

TreeSet将元素储存在红-黑树数据结构中,有序。

HashSet使用的是散列函数,无序。

LinkedHashList因为查询速度的原因使用散列,但是也使用了链表来维护元素的插入顺序。

11.10 Map

Map可以用keySet()返回它的键的Set,用valueSet()它的值的Collection。

  • HashMap

  • TreeMap

  • LinkedHashMap

11.111 Queue

队列是一个典型的先进先出的容器。

11.11.1 PriorityQueue

优先队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。

Collection和Iterator

Collection是描述所有序列容器的共性的跟接口,它可能被认为是一个“附属接口”,即因为要表示其他若干个接口的共性而出现的接口。

实现Collection就意味着需要提供iterator()方法。

Collection可以生成Iterator,而List可以生成ListIterator。

11.13 Foreach与迭代器

foreach语法主要用于数组和实现了Iterable接口的对象。

11.13.1 适配器方法惯用法

由Arrays.asList()产生的List对象会使用底层数组作为其物理实现。只要你执行的操作会修改这个List,并且你不想原来的数组被修改,那么你应该在另一个容器中创建一个副本。

11.14 总结

  1. 数组将数字与对象联系起来。它保存类型明确的对象,查询对象时,不需要对结果做类型转换。他可以是多维的,可以保存基本类型的数组。但是,数组一旦生成,其容量就不能改变。

  2. Collection保存单一的元素,而Map保存相关的键值对。有了Java的泛型,你就可可以指定容器中存放的对象类型,因此你就不会将错误类型的对象放置到容器中,并且自从容器中获取元素时,不必进行类型转换。各种Collection和各种Map都可以在你向其中添加更多的元素时,紫红调整其尺寸。容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到容器中所持有的包装其类型之间的双向转换。

  3. 像数组一样,List也建立数字索引与对象的关联,因此,数组和List都是排好序的容器。List能够自动扩充容量。

  4. 如果要进行大量的随机访问,就使用ArrayList;如果要经常从表中插入或删除元素,则应该使用LinkedList。

  5. 各种Queue以及栈的行为,由LinkedList提供支持。

  6. Map是一种将对象(而非数字)与对象相关联的设计。HashMap设计用来快速访问;而TreeMap保持“键”始终处于排序状态,所以没有HashMap快。LinkedHashMap保持元素插入的顺序,但是也通过散列提供快速访问能力。

  7. Set不接受重复元素。HashSet提供组块的查询速度,而TreeSet保持元素处于排序状态,LinkedHashSet以插入顺序保存元素。

  8. 新程序中不应该使用过时的Vector、Hashtable和Stack。