一、问题背景:Photon在WebGL上的“连接拦路虎”

很多做实时联机游戏、互动直播的开发者,应该都用过Photon这个工具——它就像一个“联机中转站”,能帮我们快速实现多个玩家之间的实时数据同步,比如游戏里的玩家移动、实时聊天、组队状态更新这些功能。但不少人把做好的项目打包成WebGL版本(就是能直接在浏览器里跑的版本)时,会遇到一个特别闹心的问题:明明在PC端、移动端原生平台上连得好好的,到了WebGL就突然连不上Photon服务器,查日志会发现是WebSocket相关的错误。

这不是Photon本身的bug,而是浏览器对WebSocket的限制导致的。简单说,浏览器为了安全,不允许未加密的WebSocket连接(也就是ws://开头的)直接连到非本地的服务器,而且对WebSocket的跨域、端口、证书都有严格要求;再加上Photon默认的一些配置,就很容易触发连接失败。接下来我们就一步步拆解怎么解决这个问题。

二、替代方案:不用原生WebSocket的Photon连接方式

如果不想跟浏览器的WebSocket限制死磕,我们可以换一种连接方式——Photon支持用HTTP长轮询(Long Polling)来模拟实时通信,本质是浏览器不断发HTTP请求,服务器有新数据就立刻返回,没有就等一会儿再返回,就像浏览器“蹲点”拿数据,能完美绕过WebSocket的所有限制。

2.1 技术栈统一说明

我们所有示例都用Unity作为开发引擎,因为Photon最常用的场景就是Unity游戏开发,WebGL打包也是Unity的核心功能之一。

2.2 具体实现步骤

首先,我们要把Unity项目里的Photon连接配置改成用HTTP长轮询。原来的Photon连接代码一般是默认用WebSocket的,我们只需要改一行配置就行。

// 统一技术栈:Unity 2022.3 + Photon Realtime 4.1.10
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;

public class PhotonWebGLConnector : MonoBehaviourPunCallbacks
{
    // 你的Photon服务器地址(和原生平台的地址一致)
    public string photonServerAddress = "wss://your-photon-server:5055";
    // 你的Photon应用ID(从Photon官网申请的唯一标识)
    public string photonAppId = "YOUR_APP_ID";

    void Start()
    {
        // 关键修改:设置Photon的连接协议为HTTP长轮询
        // 只有WebGL平台才用这个协议,原生平台还是用默认的WebSocket
        #if UNITY_WEBGL
            PhotonNetwork.NetworkingClient.LoadBalancingPeer.Protocol = ConnectionProtocol.Http;
        #endif

        // 连接Photon服务器的标准代码
        if (!PhotonNetwork.IsConnected)
        {
            PhotonNetwork.ConnectUsingSettings();
            PhotonNetwork.GameVersion = "1.0";
        }
    }

    // 连接成功的回调函数
    public override void OnConnectedToMaster()
    {
        Debug.Log("Photon连接成功!");
        // 连接成功后可以加入房间、创建房间等操作
        PhotonNetwork.JoinRandomRoom();
    }

    // 连接失败的回调函数
    public override void OnDisconnected(DisconnectCause cause)
    {
        Debug.Log($"Photon连接失败,原因:{cause}");
    }
}

这里要注意几个细节:一是#if UNITY_WEBGL这个条件编译,意思是只有打包成WebGL的时候才会执行改协议的代码,PC、移动端原生平台还是用原来的WebSocket,不会影响其他平台的性能;二是HTTP长轮询的连接地址,不用改,还是和原来的WebSocket地址一样,Photon会自动适配。

2.3 替代方案的优缺点

这个方案的优点非常明显:一是完全绕过了浏览器对WebSocket的所有限制,不用管证书、跨域、端口的问题,只要你的Photon服务器能正常接收HTTP请求就行;二是实现起来特别简单,只需要加一行配置,原来的Photon代码几乎不用改,学习成本极低;三是兼容性好,所有支持HTML5的浏览器都能跑,包括一些旧版本的浏览器。

缺点也有:HTTP长轮询的实时性比WebSocket差一点,毕竟是靠不断发请求来拿数据,会有几十毫秒到几百毫秒的延迟,对实时性要求特别高的场景(比如第一人称射击游戏的精准同步)可能会有影响;另外,HTTP长轮询的流量消耗比WebSocket高,因为每次请求都会带HTTP头,长时间联机的话,流量会比WebSocket多一些。

三、协议栈调优:如果坚持用WebSocket的优化方法

如果你的项目对实时性要求特别高,必须用WebSocket,那我们也可以通过调优协议栈来解决连接问题,核心就是满足浏览器对WebSocket的所有安全要求。

3.1 浏览器WebSocket的核心限制

要调优,首先得知道浏览器到底限制了什么:

  1. 必须用加密连接:浏览器只允许ws://(未加密)连接本地服务器(比如localhost),如果要连接公网服务器,必须用wss://(加密);
  2. 必须有合法的SSL证书:wss://连接需要服务器有受浏览器信任的SSL证书,不能是自签名的证书;
  3. 必须满足跨域要求:如果你的WebGL项目是从A域名加载的,Photon服务器的域名必须允许A域名的跨域请求;
  4. 端口限制:浏览器不允许用一些特殊端口(比如22、25)来建立WebSocket连接。

3.2 具体调优步骤

我们一步步来解决这些限制:

3.2.1 给Photon服务器配置SSL证书

首先,我们要把Photon服务器的WebSocket连接改成wss://,并且配上合法的SSL证书。这里我们用免费的Let's Encrypt证书来演示,适合个人开发者和小团队。

第一步,先在Photon服务器上安装certbot工具(用来申请Let's Encrypt证书):

# 以Ubuntu服务器为例,安装certbot
apt update && apt install certbot

第二步,申请证书(假设你的Photon服务器域名是photon.example.com):

# 申请证书,会自动验证域名所有权
certbot certonly --standalone -d photon.example.com

第三步,把证书配置到Photon服务器。打开Photon服务器的配置文件(一般在Photon根目录的deploy/LoadBalancing/bin/PhotonServer.config),找到WebSocket相关的配置,改成这样:

<WebSocketSettings>
  <Enabled>True</Enabled>
  <BindAddress>0.0.0.0</BindAddress>
  <Port>5055</Port>
  <!-- 配置SSL证书路径 -->
  <SslCertificatePath>/etc/letsencrypt/live/photon.example.com/fullchain.pem</SslCertificatePath>
  <SslCertificateKeyPath>/etc/letsencrypt/live/photon.example.com/privkey.pem</SslCertificateKeyPath>
  <SslCertificatePassword></SslCertificatePassword>
</WebSocketSettings>

配置完后重启Photon服务器,现在你的Photon服务器就支持wss://连接了。

3.2.2 配置Photon服务器的跨域规则

接下来,我们要让Photon服务器允许你的WebGL项目域名的跨域请求。还是打开Photon的配置文件,找到CORS相关的配置,改成这样:

<HttpSettings>
  <Enabled>True</Enabled>
  <Port>9090</Port>
  <!-- 配置允许跨域的域名,*代表允许所有域名(测试用,生产环境建议指定具体域名) -->
  <CorsAllowedOrigins>*</CorsAllowedOrigins>
  <CorsAllowedMethods>GET,POST,PUT,DELETE,OPTIONS</CorsAllowedMethods>
  <CorsAllowedHeaders>Content-Type,Authorization,X-Requested-With</CorsAllowedHeaders>
</HttpSettings>

这里要注意,生产环境下不要用*,要把你的WebGL项目的域名填进去,比如https://your-game.com,这样更安全。

3.2.3 修改Unity端的连接代码

最后,我们要把Unity端的Photon连接地址改成wss://开头的,代码修改如下:

// 统一技术栈:Unity 2022.3 + Photon Realtime 4.1.10
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;

public class PhotonWebSocketConnector : MonoBehaviourPunCallbacks
{
    // 改成wss://开头的地址
    public string photonServerAddress = "wss://photon.example.com:5055";
    public string photonAppId = "YOUR_APP_ID";

    void Start()
    {
        #if UNITY_WEBGL
            // WebGL平台用wss://连接,协议用默认的WebSocket
            PhotonNetwork.NetworkingClient.LoadBalancingPeer.Protocol = ConnectionProtocol.WebSocket;
        #endif

        if (!PhotonNetwork.IsConnected)
        {
            PhotonNetwork.ConnectUsingSettings();
            PhotonNetwork.GameVersion = "1.0";
        }
    }

    public override void OnConnectedToMaster()
    {
        Debug.Log("Photon WebSocket连接成功!");
        PhotonNetwork.JoinRandomRoom();
    }

    public override void OnDisconnected(DisconnectCause cause)
    {
        Debug.Log($"Photon WebSocket连接失败,原因:{cause}");
    }
}

3.3 调优方案的优缺点

这个方案的优点是实时性好,能达到和原生平台差不多的延迟,适合对实时性要求高的项目;流量消耗也比HTTP长轮询低,长时间联机更省流量。

缺点是配置比较复杂,需要给Photon服务器申请和配置SSL证书,还要处理跨域、端口等问题,对服务器配置不熟悉的开发者来说门槛比较高;另外,证书需要定期更新(Let's Encrypt证书有效期是90天),如果忘了更新,连接会再次失败。

四、两种方案的适用场景对比

我们来总结一下两种方案分别适合什么情况:

  1. HTTP长轮询方案:适合对实时性要求不高的项目,比如回合制游戏、实时聊天、多人互动课件等;适合个人开发者、小团队,不想花太多时间配置服务器的场景;适合需要兼容旧浏览器的场景。
  2. WebSocket调优方案:适合对实时性要求高的项目,比如MOBA游戏、射击游戏、实时协作绘图等;适合有一定服务器配置能力的团队;适合流量敏感的项目。

五、注意事项

不管用哪种方案,都有几个通用的注意事项:

  1. 测试环境尽量和生产环境一致:不要只在本地测试就上线,要把项目打包成WebGL后,放到公网服务器上测试,因为本地环境的限制和公网不一样;
  2. 版本一致性:Photon的客户端版本和服务器版本要一致,不然会出现连接成功但数据同步失败的问题;
  3. 错误日志要详细:连接失败时,要打印出具体的错误原因,比如是证书错误、跨域错误还是超时,方便排查问题;
  4. 避免自签名证书:如果用WebSocket方案,不要用自签名的证书,浏览器会直接拒绝连接;
  5. 跨域配置不要用*:生产环境下,一定要指定允许跨域的具体域名,不要用*,不然会有安全风险。

六、文章总结

Photon在WebGL平台的连接问题,本质是浏览器对WebSocket的安全限制导致的,我们可以根据项目的实际需求选择两种解决思路:如果追求简单、兼容,就用HTTP长轮询替代;如果追求实时性,就对WebSocket协议栈进行调优,满足浏览器的安全要求。

两种方案各有优劣,没有绝对的好坏,适合自己项目的就是最好的。希望这篇文章能帮大家解决Photon在WebGL上的连接问题,让大家的联机项目能顺利跑在浏览器上。