签到领奖

[问答活动] 【获奖名单公布】U3D问答再开 新书等你来拿!

  [复制链接]
查看: 16923|回复: 65
排名
1
昨日变化
发表于 2016-12-14 15:04:02 | 显示全部楼层 |阅读模式
这一期u3d问答我们请来《Unity3d网络游戏实战》的作者罗培羽为大家出题&评奖

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!



罗培羽:
一名正在创作好玩游戏的程序员。对服务端技术、游戏客户端、人工智能和虚拟现实等领域有一定研究,
著有《手把手教你用C#制作RPG游戏》(2014年)一书,曾发布《教你用VB制作RPG游戏》(2009年)、
《教你制作3DRPG游戏》(2010年)等多部视频教程。
曾组织“民间团队”开发仙剑同人游戏《仙剑5前传之心愿》,该作引起一定的反响。有媒体评价“所谓初心,大抵如此”。
愿与诸位一同努力,造就经典游戏产品。欢迎读者关注作者的知乎专栏“游戏研究院”。



本期问答题目如下:

1)如何解决Tcp协议的沾包和分包问题?
2)怎样减缓网络游戏的同步延迟问题?
3)请比较protobuf、json、字节流三种协议格式的优劣。
4)请用unity写一个简单的聊天室(代码)。
5)在广域网的多人FPS游戏中,能否使用Unity作为服务端的战斗服?如果可以,给出你的方案和性能分析。

活动时间:
12月19日(周一)~12月25日(周日)晚24:00

活动奖励:
最佳回答(共8人):《Unity3D网络游戏实战》一本,获奖名单公布后,请联系管理员(QQ:865259317)告知相应收货信息
优秀奖(若干名):泰斗币1~200个
所有回帖将在活动结束后开放阅读!!!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

《Unity3D网络游戏实战》内容简介:
本书在一开始便提出一个明确的学习目标,便是要制作一款完整的多人对战游戏,然后一步一步去实现它。全书分为3个部分:
第一部分“单机游戏”:第1章至第5章,主要在于开发一款功能完整坦克单机游戏。除了让坦克行走、开炮,还将介绍基于代码和资源分离的界面系统、敌人AI。了解开发单机游戏的知识,也是为接下来的网络开发学习奠定基础。
第二部分“网络原理”:第6章至第8章,主要介绍网络通信的原理,开发客户端的网络模块和服务端程序框架。这套框架具有较高的通用性,可以运用在多种游戏上。
第三部分“网络游戏”:第9章至第12章,主要讲解房间系统和同步系统的逻辑实现,将单机坦克游戏改造成多人对战网络游戏。
此外,本书提供所有示例的源码和素材,读者可以在笔者提供的网盘中(http://pan.baidu.com/s/1c18esDE,密码:9inz)下载

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

U3D问答再开《Unity3D网络游戏实战》新书等你来拿!

获奖名单公布:

【获奖名单公布】U3D问答再开 新书等你来拿!

【获奖名单公布】U3D问答再开 新书等你来拿!

老师(tyxxxx)标准答案已置顶~

+1
16966°C
42
  • tyxxxx
  • YOOYOO
  • wyc222
  • u33d
  • d_ts
过: 他们
排名
784
昨日变化
5
发表于 2016-12-27 20:05:23 | 显示全部楼层
本帖最后由 tyxxxx 于 2016-12-27 20:31 编辑

1)如何解决Tcp协议的沾包和分包问题?

使用固定长度的协议:根据包长度截取数据包
采用分隔符的方式:如果出现结尾标识,即将粘包分开,如果一个包中没有出现结尾符,则等待下个包,组合成一个完整的数据包
添加包含长度信息的包头:服务器接收到数据后,先解析包头,然后根据包长度截取数据包


2)怎样减缓网络游戏的同步延迟问题?

预测:以位置同步为例,客户端根据服务端之前发送的位置信息,预测下一次服务端数据到达时的位置,从而预先将物体以一定的速度移向目的地。
帧同步:该方案并不能减缓网络延迟,甚至人为的加大了延迟。以此使各个客户端表现一直,可以公平竞技。

关于预测的描述,可参考wyc222的答案
3)请比较protobuf、json、字节流三种协议格式的优劣。
数据包大小:json > 字节流 > protobuf
数据处理速度:字节流 > protobuf ≈ json
可读性:json >  protobuf ≈ 字节流

可参考丶邪恶馒馒酱的答案

4)请用unity写一个简单的聊天室(代码)。
上面大家已经回答的很好了,也有多种方案,请参考上面的回答




5)在广域网的多人FPS游戏中,能否使用Unity作为服务端的战斗服?如果可以,给出你的方案和性能分析。

unet:目前unet不适用。其一功能和效率还不够完善。其二目前主要以制作局域网游戏为主,广域网游戏不适用。其三,鉴于国内网络环境,使用Cloud风险非常大。但有望将来继续完善。

战斗服方案:目前可能使用unity作为战斗服,另写调控程序作为匹配服、登录服。匹配服匹配玩家后,让客户端连接某个Unity战斗服,战斗结束后断开。按照目前理论计算,一台普通服务器大概可以撑数百名玩家。比起现有的服务端方案,效能是其1/10。但它可以较好的实现客户端和服务端使用同一套逻辑,并使用服务端计算,有效杜绝作弊现象,降低开发成本。

发表于 2016-12-19 10:11:57 | 显示全部楼层
哥顶的不是帖子,是寂寞!
回复 支持 1 反对 0

使用道具 举报

4后起之秀
720/1000
排名
36
昨日变化
1
发表于 2016-12-19 10:23:42 | 显示全部楼层
本帖最后由 wyc222 于 2016-12-20 19:29 编辑

1)如何解决Tcp协议的沾包和分包问题?
:如果是文本传输的数据,可以采用分隔符的方式,即我们在封装要传输的数据包的时候,采用固定的符号作为结尾符(数据中不能含结尾符),这样我们接收到数据后,如果出现结尾标识,即人为的将粘包分开,如果一个包中没有出现结尾符,认为出现了分包,则等待下个包中出现后组合成一个完整的数据包,如采用/r/n之类的分隔符;

另一种是采用在数据包中添加长度的方式,即在每个包的包头加上固定字节的长度,服务器接收到数据后解析包长度,再根据长度截取数据包。这种方式会有一个问题就是如果客户端第一个数据包数据长度封装的有错误,那么很可能就会导致后面接收到的所有数据包都解析出错(由于TCP建立连接后流式传输机制),只有客户端关闭连接后重新打开才可以消除此问题,处理这个问题的时候需要服务端对数据长度做校验,然后对接收到的有问题的包进行人为的丢弃处理(因此客户端必须有自动重发机制,才能保证在应用层不会导致数据的不完整性)。

2)怎样减缓网络游戏的同步延迟问题?
答:没有做过需要实时同步的游戏,根据查找资料和平时的观察来看,以下几种方式应该是目前的网游经常用来减缓同步延迟的,有的时候,不一定真的是减缓了同步延迟,但是只要看起来延迟不高,那么就应当可以认为减缓了同步延迟,这也算是欺骗的艺术吧。
客户端预测和插值
服务器可以允许某些情况下客户端本地即时执行移动操作,这种方法可以称为客户端预测。
比如游戏中键盘控制角色行走,这个时候可以在很小的时间段(时间很短,比如1-3秒)内预测用户行动轨迹(方向+加速度,角色行走结果),这部分的命令客户端会全部发送到服务器端校验正确与否(避免瞬间转移等外挂)。但客户端预测有时也不是百分百准确,需要服务器进行纠正(所谓服务器就是上帝,The sever is the man!)。纠正结果可能就是游戏角色行走轨迹和客户端预测轨迹有所偏差,客户端可以使用插值方式(粗略来讲,就是角色在两点之间移动渲染的方式)渲染游戏角色在游戏世界中的位置转移平滑一些,避免游戏角色从一个位置瞬间拉回到另一个位置,让人有些莫名其妙。
插值,有人也称之为路径补偿,都是一回事。插值的方法会涉及到很多数学公式,线性插值、三次线性插值等。
小结:客户端预测,服务器端纠正,客户端采用插值方式微调。
针对交互的一群玩家,网络好坏层次不齐,游戏的一些操作效果可能需要”延迟补偿“策略进行
延迟补偿
延迟补偿是游戏服务器端执行的一种策略,处理用户命令回退到客户端发送命令的准确时间(延迟导致),根据客户端的具体情况进行修正,以牺牲游戏在伤害判定方面的真实感来弥补攻击行为等方面真实感,本质上是一种折中选择。
主要注意,延迟补偿不是发生在客户端。
关于延迟补偿的一个例子:
  • 在FPS游戏中,玩家A在10.5秒时向目标对象玩家B射击并且击中,射击信息被打包发送(网络延迟100毫秒),服务器于10.6秒收到,此时玩家B可能已跑到另外一个位置。
  • 若服务器仅仅基于接收时刻(10.6秒)进行判断,那么玩家B没有收到伤害,或许可能会击中玩家B后面紧跟的玩家C(100ms后玩家C完全由可能已处于玩家A的射击目标位置)
  • 为了弥补由于延迟造成的问题,服务器端需要引入“延迟补偿”策略用于修正因延迟造成错乱假象
  • 服务器计算执行设计命令时间,然后找出当前世界10.5秒时刻玩家信息,根据射击算法模拟得出是否命中判断,以达到尽可能精确
若游戏延迟补偿被禁用,那么就会有许多玩家抱怨自己明明打中了对方却没有造成任何伤害。。
有所得,有所失:但这对低延时玩家貌似有些不公平,移动速度快,可能已经跑到角落里并且已蹲在一个箱子后面隐藏起来时被对手击中的错觉(子弹无视掩体,玩家隔着墙被射击),确实有些不乐意。
延迟补偿,网络高延迟的玩家有利,低延迟的玩家优势可能会被降低(低延迟玩家利益受损),但对维护游戏世界的平衡还是有利的。
对时&阀值
客户端和服务器需要对时,互相知道彼此延迟情况,比如云风定义的某个步骤:

客户端发送一个本地时间量给服务器,服务收到包后,夹带一个服务器时间返回给客户端。当客户端收到这个包后,可以估算出包在路程上经过的时间。同时把本地新时间夹带进去,再次发送给服务器。服务器也可以进一步的了解响应时间。

C/S两端通过类似步骤进行计算彼此延时/时差,同时会对实时同步设置一个阀值,比如对延迟低于10ms(0.01秒)的交互认为是即时同步发生,不会认为是延迟。

3)请比较protobuf、json、字节流三种协议格式的优劣。
答:这三种方式中protobuf算比较新的方式,由谷歌推出,json和字节流都是传统的格式,json的优点应该就是非常方便,阅读性也还算可以,修改也比较方便,各种语言都有很多类库来支持,缺点应该就是占用的空间比较大,由于采用的是键值对的方式,存储了很多无意义的内容,在传输数据不大的情况下这个缺点倒不是那么重要,目前很多网站提供的API接口都是json数据,这点也说明了json的应用广泛和易用性。
字节流占用的空间相对json来说小一些,但是完全没有阅读性可言。
protobuf
优点
    二进制消息,性能好/效率高(空间和时间效率都很不错)
    proto文件生成目标代码,简单易用
    序列化反序列化直接对应程序中的数据类,不需要解析后在进行映射(XML,JSON都是这种方式)
    支持向前兼容(新加字段采用默认值)和向后兼容(忽略新加字段),简化升级
    支持多种语言(可以把proto文件看做IDL文件)
缺点
    官方只支持C++,JAVA和Python语言绑定(当然其他很多语言也有民间大牛实现了)
    二进制可读性差(貌似提供了Text_Fromat功能)
    二进制不具有自描述特性
    默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持)
    只涉及序列化和反序列化技术,不涉及RPC功能(类似XML或者JSON的解析器)

4)请用unity写一个简单的聊天室(代码)。
答:使用NetWork实现的简单的聊天室
服务端 Server.cs:
[AppleScript] 纯文本查看 复制代码
int  connectPort  = 2278;  
  
void OnGUI ()  
  
{  
  
  if (Network.peerType == NetworkPeerType.Disconnected)  
  
  {  
  
  GUILayout.Label("Connection status: Disconnected");  
  
  Network.InitializeServer(32, connectPort, false);  
  
 }  
  
}  

客户端 Client.cs:
[AppleScript] 纯文本查看 复制代码
string connectToIP = "127.0.0.1";  
  
int connectPort = 2288;  
  
string MyName="";  
  
string Inputword="";  
  
string Outputword="";  
  
void OnGUI ()  
  
{  
  
  if(Network.peerType == NetworkPeerType.Disconnected){  
  
    GUILayout.Label("Connection status: Disconnected");  
  
    if(GUILayout.Button("Connect as client")){  
  
      Network.Connect(connectToIP, connectPort);  
  
    }  
  
  }  
  
 else{  
  
  if(Network.isClient){  
  
    MyName=GUI.TextField(new Rect(20,20,100,30),MyName,20);  
  
    Outputword=GUI.TextArea(new Rect(20,50,400,300),Outputword,1000);  
  
    Inputword=GUI.TextField(new Rect(20,360,300,60),Inputword,200);  
  
    if(GUI.Button(new Rect(350,360,70,60),"send")){  
  
       networkView.RPC("SomeoneSay",RPCMode.All,Inputword,MyName);  
  
       Inputword="";  
  
      }  
  
    }  
  
  }   
  
}  
  
@RPC  
void SomeoneSay(string sayWord,string name){  
  
 Outputword =Outputword+"\n"+ name + ":\n" + "  " + sayWord;  
  
}  

5)在广域网的多人FPS游戏中,能否使用Unity作为服务端的战斗服?如果可以,给出你的方案和性能分析。
:最新版本的Unet应该是能够实现这样的需求的,看过别人用来实现手游FPS的双端同工程。在这一点上,Unet的通讯同步确实做得非常方便,不需要关心底层数据交互。 但是性能方面比较让人担心的可能是IO量略大。以下是简化版的使用流程:

1、NetworkManager 安装

• 向场景添加一个新的游戏对象并将它重命名为"NetworkManager"。
• 为新的游戏对象添加NetworkManager 组件。
• 将 NetworkManagerHUD 组件添加到游戏物体。这将为管理网络游戏状态提供默认 UI。



2、Player Prefab 安装

• 在游戏中Find player的预制体prefab,或者从player对象创建一个预置。
• NetworkIdentity 组件添加到player prefab上。
• 检查 NetworkIdentity 的 LocalPlayerAuthority box 。
• 在player prefab的NetworkManager 组件上为 “Spawn Info” 设置 playerPrefab
• 从现场删除player 对象实例,如果它存在于现场


3、Player Movement

• 将 NetworkTransform 组件添加到 player prefab
• 更新输入和控制脚本来遵守 isLocalPlayer
• 修复camera 使用衍生出的player 和 isLocalPlayer

例如,此脚本只处理local player输入:

[AppleScript] 纯文本查看 复制代码
using UnityEngine;  
using UnityEngine.Networking;  
  
public class Controls : NetworkBehaviour  
{  
    void Update()  
    {  
        if (!isLocalPlayer)  
        {  
            // exit from update if this is not the local player  
            return;  
        }  
  
        // handle player input for movement  
    }  
}  

4、Basic Player Game State

• 使脚本包含重要数据到NetworkBehaviours , 而不是 MonoBehaviours
• 使重要成员变量到 SyncVars

5、Networked Actions

• 使脚本执行NetworkBehaviours重要操作成而不是 MonoBehaviours
• Update函数执行player操作的重要命令

6、Non-Player Objects


修改非玩家prefabs,像敌人等:
• 添加 NetworkIdentify 组件
• 添加 NetworkTransform 组件
• NetworkManager注册 spawnable 预置体
• 更新脚本与游戏状态和操作


7、Spawners


• 可能更改的spawner 脚本要继承自 NetworkBehaviours
• 修改spawners 只运行在服务器上,使用 isServer 属性或 OnStartServer() 函数
• 为创建对象调用 NetworkServer.Spawn()


8、Player的产生的位置


• 添加一个新的游戏对象,并将它放在玩家的应该开始的位置
• 新的游戏对象添加 NetworkStartPosition 组件


9、Lobby


• Create Lobby Scene创建大厅现场
• Import multiplayer-lobby package导入多人游戏大厅包
• 添加 GuiLobbyManager 预置到场景
• Configure the manager配置 管理器

       •Scenes
      • Prefabs
      • spawners


点评

回答很详细,特别是第二题给出了多个方案  发表于 2016-12-27 19:07

评分

参与人数 1泰斗币 +30 收起 理由
zliex + 30 赞一个!给力!

查看全部评分

[发帖际遇]: wyc222 拉灯想跟你学用unity做航母,这是 1 泰斗币当学费了. 幸运榜 / 衰神榜
排名
784
昨日变化
5
发表于 2016-12-19 11:02:16 | 显示全部楼层
    1)如何解决Tcp协议的沾包和分包问题?
         答: 两种方案:1、数据包采用固定的符号作为结尾符,2、数据包将长度信息封装到包内的固定位置
    2)怎样减缓网络游戏的同步延迟问题?
          答:帧同步和CS同步
    3)请比较protobuf、json、字节流三种协议格式的优劣。
          答:本身的优点、缺点就不说了,只说对比后的优缺点。 protobuf:优点:1.高效(一条消息数据,用protobuf序列化后的大小是json的10分之一)。缺点:1.可读性差。    json:优点:1.可读性高。缺点:相同数据封装后,体积比protobuf大很多。字节流:优点:1.真正的“跨平台”,2.不用关心数据的大小,按照长度取byte就行。缺点:不知道怎么说--
    4)请用unity写一个简单的聊天室(代码)。
             这个问题有点。。。。呵呵。最近刚好写了个,但是太麻烦,不上传了。用的是socket,挺简单的。
    5)在广域网的多人FPS游戏中,能否使用Unity作为服务端的战斗服?如果可以,给出你的方案和性能分析。
             没有不能,只有需求合适不合适。纯属个人观点:用Unity做出来完全可以,性能嘛、呵呵呵
            

点评

虽然比较简略,但也基本答到点上了  发表于 2016-12-27 19:08

评分

参与人数 1泰斗币 +20 收起 理由
洞悉 + 20 第一个回答!

查看全部评分

3江湖小虾
457/500
排名
1016
昨日变化
5
发表于 2016-12-19 13:15:12 | 显示全部楼层
题目太简单了。。。。。。。。。。。。。。。。。。。。

评分

参与人数 1泰斗币 +10 收起 理由
洞悉 + 10 快来答 <(-︿-)>

查看全部评分

[发帖际遇]: d_ts 拉登想跟你学用unity做航母,这是 7 泰斗币当学费了. 幸运榜 / 衰神榜
2武林新丁
162/200
排名
47
昨日变化
2
发表于 2016-12-19 14:16:00 | 显示全部楼层
不错不错,支持一下
排名
1
昨日变化
发表于 2016-12-19 19:17:42 | 显示全部楼层
本帖最后由 我是一头小毛驴 于 2016-12-25 18:27 编辑

1)如何解决Tcp协议的沾包和分包问题?
一个是采用分隔符的方式,即我们在封装要传输的数据包的时候,采用固定的符号作为结尾符(数据中不能含结尾符),这样我们接收到数据后,如果出现结尾标识,即人为的将粘包分开,如果一个包中没有出现结尾符,认为出现了分包,则等待下个包中出现后 组合成一个完整的数据包,这种方式适合于文本传输的数据,如采用/r/n之类的分隔符;

另一种是采用在数据包中添加长度的方式,即在数据包中的固定位置封装数据包的长度信息(或可计算数据包总长度的信息),服务器接收到数据后,先是解析包长度,然后根据包长度截取数据包(此种方式常出现于自定义协议中),但是有个小问题就是如果客户端第一个数据包数据长度封装的有错误,那么很可能就会导致后面接收到的所有数据包都解析出错(由于TCP建立连接后流式传输机制),只有客户端关闭连接后重新打开才可以消除此问题,我在处理这个问题的时候对数据长度做了校验,会适时的对接收到的有问题的包进行人为的丢弃处理(客户端有自动重发机制,故而在应用层不会导致数据的不完整性);
2)怎样减缓网络游戏的同步延迟问题?
1. 帧同步
  2.TCP和UDP不停的换端口

3)请比较protobuf、json、字节流三种协议格式的优劣。
    protobuf跨语言且空间小 效率高 性能好     二进制可读性差
    json   简单    标识符和实际属性值可使用同一种命名   
    字节流  效率低

4)请用unity写一个简单的聊天室(代码)。
[AppleScript] 纯文本查看 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;

namespace TestServer
{
    class Program
    {
        // 设置连接端口
        const int portNo = 500;

        static void Main(string[] args)
        {
            // 初始化服务器IP
            System.Net.IPAddress localAdd = System.Net.IPAddress.Parse("127.0.0.1");

            // 创建TCP侦听器
            TcpListener listener = new TcpListener(localAdd, portNo);

            listener.Start();

            // 显示服务器启动信息
            Console.WriteLine("Server is starting...\n");

            // 循环接受客户端的连接请求
            while (true)
            {
                ChatClient user = new ChatClient(listener.AcceptTcpClient());

                // 显示连接客户端的IP与端口
                Console.WriteLine(user._clientIP + " is joined...\n");
            }
        }
    }
}

[AppleScript] 纯文本查看 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Net.Sockets;

namespace TestServer
{
    class ChatClient
    {
        public static Hashtable ALLClients = new Hashtable(); // 客户列表

        private TcpClient _client;  // 客户端实体
        public  string _clientIP;   // 客户端IP
        private string _clientNick; // 客户端昵称

        private byte[] data;        // 消息数据

        private bool ReceiveNick = true;

        public ChatClient(TcpClient client)
        {
            this._client = client;

            this._clientIP = client.Client.RemoteEndPoint.ToString();

            // 把当前客户端实例添加到客户列表当中
            ALLClients.Add(this._clientIP, this);

            data = new byte[this._client.ReceiveBufferSize];

            // 从服务端获取消息
            client.GetStream().BeginRead(data, 0, System.Convert.ToInt32(this._client.ReceiveBufferSize), ReceiveMessage, null);
        }

        // 从客戶端获取消息
        public void ReceiveMessage(IAsyncResult ar)
        {
            int bytesRead;

            try
            {
                lock (this._client.GetStream())
                {
                    bytesRead = this._client.GetStream().EndRead(ar);
                }

                if (bytesRead < 1)
                {
                    ALLClients.Remove(this._clientIP);

                    Broadcast(this._clientNick + " has left the chat");

                    return;
                }
                else
                {
                    string messageReceived = System.Text.Encoding.ASCII.GetString(data, 0, bytesRead);

                    if (ReceiveNick)
                    {
                        this._clientNick = messageReceived;

                        Broadcast(this._clientNick + " has joined the chat.");

                        //this.sendMessage("hello");

                        ReceiveNick = false;
                    }
                    else
                    {
                        Broadcast(this._clientNick + ">" + messageReceived);
 
                    }
                }

                lock (this._client.GetStream())
                {
                    this._client.GetStream().BeginRead(data, 0, System.Convert.ToInt32(this._client.ReceiveBufferSize), ReceiveMessage, null);
                }
            }
            catch (Exception ex)
            {
                ALLClients.Remove(this._clientIP);

                Broadcast(this._clientNick + " has left the chat.");
            }
        }

        // 向客戶端发送消息
        public void sendMessage(string message)
        {
            try
            {
                System.Net.Sockets.NetworkStream ns;

                lock (this._client.GetStream())
                {
                    ns = this._client.GetStream();
                }

                // 对信息进行编码
                byte[] bytesToSend = System.Text.Encoding.ASCII.GetBytes(message);

                ns.Write(bytesToSend, 0, bytesToSend.Length);
                ns.Flush();
            }
            catch (Exception ex)
            { 
            
            }
        }

        // 向客户端广播消息
        public void Broadcast(string message)
        {
            Console.WriteLine(message);

            foreach (DictionaryEntry c in ALLClients)
            {
                ((ChatClient)(c.Value)).sendMessage(message + Environment.NewLine);
            }
        }

    }
}
[AppleScript] 纯文本查看 复制代码
using UnityEngine;[/font][/color][/color][/align]using System.Collections;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Net.Sockets;

public class ClientHandler : MonoBehaviour 
{
        const int portNo = 500;
    private TcpClient _client;
    byte[] data;
        
        // Use this for initialization
        void Start () 
        {
        
        }
        
        // Update is called once per frame
        void Update () 
        {
        
        }
        
        public string nickName = "";
        public string message = "";
        public string sendMsg = "";
        
        void OnGUI()
        {
                nickName = GUI.TextField(new Rect(10, 10, 100, 20), nickName);
                message = GUI.TextArea(new Rect(10, 40, 300, 200), message);
                sendMsg = GUI.TextField(new Rect(10, 250, 210, 20), sendMsg);
                
                if(GUI.Button(new Rect(120, 10, 80, 20), "Connect"))
                {
                        //Debug.Log("hello");
                        
                        this._client = new TcpClient();
            this._client.Connect("127.0.0.1", portNo);

            data = new byte[this._client.ReceiveBufferSize];

            //SendMessage(txtNick.Text);
                        SendMessage(nickName);

            this._client.GetStream().BeginRead(data, 0, System.Convert.ToInt32(this._client.ReceiveBufferSize), ReceiveMessage, null);
                };
                
                if(GUI.Button(new Rect(230, 250, 80, 20), "Send"))
                {
                        SendMessage(sendMsg);
            sendMsg = "";
                };
        }
        
        public void SendMessage(string message)
    {
            try
        {
                NetworkStream ns = this._client.GetStream();

            byte[] data = System.Text.Encoding.ASCII.GetBytes(message);

            ns.Write(data, 0, data.Length);
            ns.Flush();
         }
         catch (Exception ex)
         {
            //MessageBox.Show(ex.ToString());
         }   
           }
        
        public void ReceiveMessage(IAsyncResult ar)
    {
            try
            {
                int bytesRead;

                bytesRead = this._client.GetStream().EndRead(ar);

                if (bytesRead < 1)
                {
                    return;
                }
                else
                {
                                
                                        Debug.Log(System.Text.Encoding.ASCII.GetString(data, 0, bytesRead));
                                
                                        message += System.Text.Encoding.ASCII.GetString(data, 0, bytesRead);
                        }

                this._client.GetStream().BeginRead(data, 0, System.Convert.ToInt32(this._client.ReceiveBufferSize), ReceiveMessage, null);
                        
                        
            }
            catch (Exception ex)
            { 
            
            }
    }
}

5)在广域网的多人FPS游戏中,能否使用Unity作为服务端的战斗服?如果可以,给出你的方案和性能分析。
    可以使用Unet来做

点评

大部分回答是正确的。TCP和UDP不停的换端口的话,不如直接弄两个连接更好?  发表于 2016-12-27 19:11
2武林新丁
164/200
排名
252
昨日变化
2
发表于 2016-12-20 08:46:40 | 显示全部楼层
看了LZ的帖子,我只想说一句很好很强大!
2武林新丁
122/200
排名
238
昨日变化
1
发表于 2016-12-20 09:40:58 | 显示全部楼层
GOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOD
[发帖际遇]: ericlin328 拉灯想跟你学用unity做航母,这是 6 泰斗币当学费了. 幸运榜 / 衰神榜
2武林新丁
137/200
排名
151
昨日变化
10
发表于 2016-12-20 10:03:04 | 显示全部楼层
3)请比较protobuf、json、字节流三种协议格式的优劣
    优点比较:
    protobuf 不管是处理时间上,还是空间占用上都优于现有的其他序列化方式。内存占用是java 序列化的1/9,时间也是差了一个数量级,一次操作在1us左右。
    json格式在空间占用还是有一些优势,是java序列化的1/2.6。序列化和反序列化处理时间上差不多,也就在5us。当然这次使用的jackson,如果使用普通的jsonlib可能没有这样好的性能,jsonlib估计跟java序列化差不多。
   
    字节流:以前一种的java序列化,表现得有些失望; 性能上也不如jackjson,输得比较彻底。
    即使使用压缩,虽然在字节上有20%以上的空间提升,但性能上差了4,5倍,典型的以时间换空间。总的来说还是protobuf比较给力。

缺点比较:
     protobuf就是对象结构体有限制,只适合于内部系统使用。

点评

由于protobuf和json的序列化需要一定运算,字节流可以较好的节省这部分时间。  发表于 2016-12-27 19:15

评分

参与人数 1泰斗币 +20 收起 理由
洞悉 + 20 感谢回答~ 其他问题如果有会的也答上哦!.

查看全部评分

[发帖际遇]: 一个袋子砸在了 cc386725 头上,cc386725 赚了 2 泰斗币. 幸运榜 / 衰神榜
排名
1016
昨日变化
5
发表于 2016-12-20 10:13:13 | 显示全部楼层
粘包与分包的处理方法:  我根据现有的一些开源资料做了如下总结(常用的解决方案): 一个是采用分隔符的方式,即我们在封装要传输的数据包的时候,采用固定的符号作为结尾符(数据中不能含结尾符),这样我们接收到数据后,如果出现结尾标识,即人为的将粘包分开,如果一个包中没有出现结尾符,认为出现了分包,则等待下个包中出现后 组合成一个完整的数据包,这种方式适合于文本传输的数据,如采用/r/n之类的分隔符;  另一种是采用在数据包中添加长度的方式,即在数据包中的固定位置封装数据包的长度信息(或可计算数据包总长度的信息),服务器接收到数据后,先是解析包长度,然后根据包长度截取数据包(此种方式常出现于自定义协议中),但是有个小问题就是如果客户端第一个数据包数据长度封装的有错误,那么很可能就会导致后面接收到的所有数据包都解析出错(由于TCP建立连接后流式传输机制),只有客户端关闭连接后重新打开才可以消除此问题,我在处理这个问题的时候对数据长度做了校验,会适时的对接收到的有问题的包进行人为的丢弃处理(客户端有自动重发机制,故而在应用层不会导致数据的不完整性);  另一种不建议的方式是TCP采用短连接处理粘包(这个得根据需要来,所以不建议)

点评

感谢回复~ TCP短连接确实不推荐,除非发一条协议重新连接一次  发表于 2016-12-27 19:16

评分

参与人数 1泰斗币 +20 收起 理由
zliex + 20 感谢回复~其他问题如果有会的也答上哦!.

查看全部评分

排名
361
昨日变化
5
发表于 2016-12-20 10:22:04 | 显示全部楼层
新手路过,本来是看大家的评论的,结果一个都看不到
发表于 2016-12-20 10:37:17 | 显示全部楼层
只想学点儿东西,成长自己。
2武林新丁
141/200
排名
142
昨日变化
3
发表于 2016-12-20 10:40:54 | 显示全部楼层
1.如何解决Tcp协议的沾包和分包问题?
一个是采用分隔符的方式,即我们在封装要传输的数据包的时候,采用固定的符号作为结尾符(数据中不能含结尾符),这样我们接收到数据后,如果出现结尾标识,即人为的将粘包分开,如果一个包中没有出现结尾符,认为出现了分包,则等待下个包中出现后 组合成一个完整的数据包,这种方式适合于文本传输的数据,如采用/r/n之类的分隔符。
另一种是采用在数据包中添加长度的方式,即在数据包中的固定位置封装数据包的长度信息(或可计算数据包总长度的信息),服务器接收到数据后,先是解析包长度,然后根据包长度截取数据包(此种方式常出现于自定义协议中),但是有个小问题就是如果客户端第一个数据包数据长度封装的有错误,那么很可能就会导致后面接收到的所有数据包都解析出错(由于TCP建立连接后流式传输机制),只有客户端关闭连接后重新打开才可以消除此问题,我在处理这个问题的时候对数据长度做了校验,会适时的对接收到的有问题的包进行人为的丢弃处理(客户端有自动重发机制,故而在应用层不会导致数据的不完整性)。



2.怎样减缓网络游戏的同步延迟问题?
帧同步和CS同步。


3.请比较protobuf、json、字节流三种协议格式的优劣。
protobuf常用于前后端协议,利于扩展,性能高。
json常用于http等网络协议,可读性强,轻量,多面向web。
字节流常用于文件传输,通过序列化和反序列化传输,便于加密。
xml常用于静态数据存储,可转成excel的编辑性强的文件类型。


4.请用unity写一个简单的聊天室(代码)。
可参考C# socket通信
namespace SocketServer
{
    class Program
    {
        private static byte[] result = new byte[1024];
        private static int myProt = 8885;   //端口
        static Socket serverSocket;
        static void Main(string[] args)
        {
            //服务器IP地址
            IPAddress ip = IPAddress.Parse("127.0.0.1");
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverSocket.Bind(new IPEndPoint(ip, myProt));  //绑定IP地址:端口
            serverSocket.Listen(10);    //设定最多10个排队连接请求
            Console.WriteLine("启动监听{0}成功", serverSocket.LocalEndPoint.ToString());
            //通过Clientsoket发送数据
            Thread myThread = new Thread(ListenClientConnect);
            myThread.Start();
            Console.ReadLine();
        }


        /// <summary>
        /// 监听客户端连接
        /// </summary>
        private static void ListenClientConnect()
        {
            while (true)
            {
                Socket clientSocket = serverSocket.Accept();
                clientSocket.Send(Encoding.ASCII.GetBytes("Server Say Hello"));
                Thread receiveThread = new Thread(ReceiveMessage);
                receiveThread.Start(clientSocket);
            }
        }


        /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="clientSocket"></param>
        private static void ReceiveMessage(object clientSocket)
        {
            Socket myClientSocket = (Socket)clientSocket;
            while (true)
            {
                try
                {
                    //通过clientSocket接收数据
                    int receiveNumber = myClientSocket.Receive(result);
                    Console.WriteLine("接收客户端{0}消息{1}", myClientSocket.RemoteEndPoint.ToString(), Encoding.ASCII.GetString(result, 0, receiveNumber));
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    myClientSocket.Shutdown(SocketShutdown.Both);
                    myClientSocket.Close();
                    break;
                }
            }
        }
    }
}

namespace SocketClient
{
    class Program
    {
        private static byte[] result = new byte[1024];
        static void Main(string[] args)
        {
            //设定服务器IP地址
            IPAddress ip = IPAddress.Parse("127.0.0.1");
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                clientSocket.Connect(new IPEndPoint(ip, 8885)); //配置服务器IP与端口
                Console.WriteLine("连接服务器成功");
            }
            catch
            {
                Console.WriteLine("连接服务器失败,请按回车键退出!");
                return;
            }
            //通过clientSocket接收数据
            int receiveLength = clientSocket.Receive(result);
            Console.WriteLine("接收服务器消息:{0}",Encoding.ASCII.GetString(result,0,receiveLength));
            //通过 clientSocket 发送数据
            for (int i = 0; i < 10; i++)
            {
                try
                {
                    Thread.Sleep(1000);    //等待1秒钟
                    string sendMessage = "client send Message Hellp" + DateTime.Now;
                    clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage));
                    Console.WriteLine("向服务器发送消息:{0}" + sendMessage);
                }
                catch
                {
                    clientSocket.Shutdown(SocketShutdown.Both);
                    clientSocket.Close();
                    break;
                }
            }
            Console.WriteLine("发送完毕,按回车键退出");
            Console.ReadLine();
        }
    }
}




5.在广域网的多人FPS游戏中,能否使用Unity作为服务端的战斗服?如果可以,给出你的方案和性能分析。
可以。使用unet可实现Unity作为服务端的FPS,unet原生解决方案。

点评

感谢回答~ 大部分能答到点上  发表于 2016-12-27 19:19

评分

参与人数 1泰斗币 +30 收起 理由
zliex + 30 赞一个!给力!

查看全部评分

排名
1016
昨日变化
5
发表于 2016-12-20 11:12:04 来自手机 | 显示全部楼层
本帖最后由 13996004765 于 2016-12-20 12:55 编辑

新人学了不太久,答的不对还请指正。Protobuf的优点
1.性能好,效率高。
2.代码生成机制,数据解析类自动生成
3.序列化后数据较小。
三,Protobuf的缺点
1.应用不够广
2.可读性差
json的优点
1. 数据格式比较简单, 易于读写, 格式都是压缩的, 占用带宽小,浏览器解析快
2. 便于服务器端的解析
缺点:
1. 通用性不够。
2. JSON目前不够普及,还属于初级阶段。相关资源不太足。
字节流,这个还没有涉及到,不过最近正在学。

点评

感谢! json的普及度已经相当高了,资源比较充足  发表于 2016-12-27 19:20

评分

参与人数 1泰斗币 +20 收起 理由
zliex + 20 感谢回复~其他问题如果有会的也答上哦!.

查看全部评分

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /4 下一条

在线客服(工作时间:9:00-22:00)
010-82609395
泰斗社区公众号

Copyright   ©2015-2016  【泰斗社区】-虚拟现实,微信开发,unity3d,unity3d教程下载首选u3d,unity3d官网  Powered by©Discuz!  技术支持:迪恩网络     ( 沪ICP备14023207号-9 )