开发网游中的同步问题(socket相关)

李雅勇的技术专栏 2010-01-09 06:02:27
我之前开贴问过一个问题:socket开发对战游戏,两客户端动作不同,延时所致,如何解决?
原贴地址:
http://topic.csdn.net/u/20090316/04/b19c6901-2c86-4d4b-ad24-7e00265ff647.html

其中的preperme大侠和coldanimal大侠都说需要进行信号同步,是正解。、
但由于我在这方面经验尚浅,想请大家说说相关的经验。比如coldanimal大侠在上帖中说:
[Quote=引用 33 楼回复:]
键盘是有一个队列 系统每隔10ms去读一下这个队列(windows是10ms)
最终当windows confirm了你这个request的时候 他才去干事
网络游戏也是一样的 当你有request的时候 他是马上发送给HOST的
而同时又有一个线程每隔比如50ms去读一下HOST的response
每个游戏的处理方法根据游戏的性质会不一样 但是视频游戏肯定是这样的
最多可能的改变就是 上面说的接受response的方式是由客户端去像HOST拉的
也可以变成HOST往client推的方式 就是有了一个response 就马上发送给client
而不是client每隔50ms 去update一下
[/Quote]
就给了我很大的启发。
当然preperme大侠所说:
[Quote=引用 24 楼 preferme 的回复:]
我没编过网络游戏。
倒是可以谈谈自己的看法。
首先,网络延迟,是任何时候都存在的,因为信号传输虽然是光速,但是,设备的解析转发却好花费巨量的时间。
比如,阳光从美国到中国,可能1毫秒都用不了,但是,如果美国某个电脑向中国发送一个数据包,最快也得几十毫秒吧。一般都100毫秒的样子。

局域网通信,由于各节点之间,数据转发的机会非常少,所以,延迟相对较少,一般也会控制在10毫秒以下。
这时,网络游戏,一般会采用心跳同步的技术,来完成各主机之间的协同同步问题。
举个例子,各主机每隔100毫秒,向服务端发一个时钟同步包,包的内容包括游戏开始时间,已进入游戏的时间,上一个包的时间,与上一个包之间所产生的事件数量、所应答的事件数量。通过这些信息,服务端可以校验每个主机在每一个时钟周期,里面的信息都是同步的。如果发生不同步的现象,则服务端发送广播,各主机开始等待超时,直到不同步的主机完成同步为止。这样,每秒可完成10次的同步校验,使得游戏可以顺畅进行。(因为,假设每次心跳同步后把当前游戏的图像信息,刷新到显示器,那么,可以达到每秒10帧的效果。当然,实际上有可能不是这么做滴,呵呵。)

互联网通信,就比较麻烦了,因为,主机和服务端传递信息一般都要30~50毫秒。所以,数据延迟所带来的影响,就会非常之大。
我觉得,可以把图像同步,和物品同步,分开来处理。图像同步就是指,里面的人物动物的位置移动、招式显示;物品同步就是指抢宝什么的。
一个人物的位置方向以及是否正在移动的信息,估计6~8个浮点数就可以搞定了。各主机如果发送队列为空的情况下,每隔100毫秒发送一下自己人物的位置动作信息,服务端接收并转发,这样,其他主机可以根具这个信息,对人物动作进行预判,当然也会因为网络延迟,在视觉上产生误差,但是,下一个位置动作信息来了以后,主机可以进行更正。这些由于移动而产生的事件,服务端,一般不做处理只做转发的。
下面谈的是物品同步的问题,一般是依据谁的争夺物品事件先被服务端处理成功,谁就拿到物品。而主机因动作预判而产生的幻象,不作为依据。服务端,只要保证,同一件物品,只能被一个人抢到就OK了。当某个主机抢到物品后,服务端负责广播通知其他主机,进行物品信息的同步。
[/Quote]
让我也体会到如果需要实现,其中的算法的实现难度。
因为网络编程本来就需要考虑很多方面~~!~!~~

所以,此帖的目的就是让更多的人说说自己的经验,大家一起学习娱乐下~~~!
...全文
561 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
感谢preferme,preferme解决了“substring的问题”
原帖位置[http://topic.csdn.net/u/20090722/09/c2626348-5583-4134-848f-497000c6fd48.html]
已经把答案发到那个帖,且做结帖加分,但本帖的问题是网络延时问题,还未解决。
故请大家帮忙
timeriver_wang 2010-01-30
  • 打赏
  • 举报
回复
学习,佩服。
breezedancer 2010-01-30
  • 打赏
  • 举报
回复
jf
鸿杰 2010-01-29
  • 打赏
  • 举报
回复
我最近也在研究一个对同步要求很高的网游。键盘控制人物行走。所有信息都交由服务器端处理,但是用户体验不是很棒。当用户松开按键时,会发现人物还要继续行走一些时间才会停下。
我准备换种思路去重写人物行走这一块代码。。。
冰思雨 2010-01-29
  • 打赏
  • 举报
回复
package houlei.java.util;

import java.io.UnsupportedEncodingException;

/**
* 这个类的功能类似于String对象的包装器类,由于String类不能被继承,
* 所以,也只是根据现有状况进行了一部分关于编码后的字节功能方面的扩展。<p>
* 在使用该类的对象时,尽量一个String对象,只创建一次对应的ByteString对象,因为构造器要进行转码操作。<br/>
* 该对象的其他方法可以多次调用也没关系,因为转码确实比较耗费CPU时间。
* @author HouLei
*/
public class ByteString {
public static final String DefaultCharsertName = "GBK";
private String value;//该参数用于本类功能的扩展,目前而言,可以除去。
private byte [] buff;

public ByteString(String value,String charsetName) throws UnsupportedEncodingException {
this.value = value;
buff = value.getBytes(charsetName);
}

public ByteString(String value) throws UnsupportedEncodingException {
this(value,DefaultCharsertName);
}

/**
* 获得字符串被转码以后所占的字节数。
* @return 所占字节数。
*/
public int size(){
return buff.length;
}

/**
* 将转码以后的字节数组进行切割,并返回切割后的字符数组。<br/>
* 该方法并不保证切割后的字节数组,仍然是完整的字符串。
* @param beginIndex 字节数组的开始位置。
* @param endIndex 字节数组的结束位置。
* @return 切割后的结果。
*/
public byte [] subBytes(int beginIndex,int endIndex){
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > buff.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
byte [] newBuff = new byte [endIndex - beginIndex];
System.arraycopy(buff, beginIndex, newBuff, 0, newBuff.length);
return newBuff;
}

/**
* 这个方法是示例代码,应该删除。
*/
public static void main(String args []) throws UnsupportedEncodingException{
String str="a您好b";
ByteString bs = new ByteString(str);//只要保持bs对象,就可以多次调用它的一些方法,这样效率就高了。
byte [] b1 = bs.subBytes(0, 1);
byte [] b2 = bs.subBytes(1, 3);
byte [] b3 = bs.subBytes(0, 3);
System.out.println(new String(b1,"GBK"));//可以得到从字节0开始的1个字节“a”
System.out.println(new String(b2,"GBK"));//可以得到从字节1开始的2个字节“您”而不是str.substring(0,3)的“您好”
System.out.println(new String(b3,"GBK"));//可以得到从字节1开始的2个字节“a您”而不是str.substring(0,3)的“a您好”
}
}
冰思雨 2010-01-29
  • 打赏
  • 举报
回复
实在不好意思,近半年时间都没怎么上CSDN了。
1楼的问题,貌似参与过。
首先,编码转换函数getByte,它的效率已经是很高的了,楼主说它效率低,应该是多次使用的结果。
这种情况,就如同我们向文件里写入内容一样,本身一次写入其效率已经就在那里了,它效率不高,是因为这个过程本身就很复杂。我们说,我们的InputStream.write()方法效率太低了,并不是他本身很差,而是因为我们多次调用这个方法的原因(比如,有一万字的小说,我们非要一个字节一个字节向文件里面写)。实际上,大家都知道,缓冲区可以很好的解决这个问题。
其次,我们在使用String对象的时候,应该想到的是他的字符,而不是具体编码的字节,这是面向对象的编程理念所要求的,字符编码方面,应该考虑的是类似Encoder或Decoder这样的类。
第三,由于网络通信协议的限制,需要底层分包的话,这个分包操作,每个包只进行一次的。
所以,我们不妨先将String对象转换成GBK编码的字节数组(每个字符串只转换一次),并在内存中保留这个数组作为缓冲区,然后通过对缓冲区的操作,来提高效率。
或者干脆编写一个包装器类来完成这些经常调用的操作。
阿_布 2010-01-09
  • 打赏
  • 举报
回复
帮顶!
hongjn 2010-01-09
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 bolink5 的回复:]
没做过这块
  纯粹帮LZ 顶下
[/Quote]
顶下
bolink5 2010-01-09
  • 打赏
  • 举报
回复
没做过这块
纯粹帮LZ 顶下
passself 2010-01-09
  • 打赏
  • 举报
回复
这个可以解决吗?网络延迟几乎是解决不了的,不过你可以取两方的时间对比,每次按大的来运行就可以了
pywepe 2010-01-09
  • 打赏
  • 举报
回复
  • 打赏
  • 举报
回复
preperme有机会请再看下这个帖
http://topic.csdn.net/u/20090722/09/c2626348-5583-4134-848f-497000c6fd48.html?seed=235380842&r=62599710#r_62599710

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧