游戏将9月28日有新分支测试,请看下面第一段内容

游戏有所更新

太吾9月28日更新了新的测试分支,“Frame - 框架调整测试”。此测试分支中使用命名管道与后端交互而非Socket,会解决因端口问题造成的本问题。需要注意的是此分支不知合适才会合并到主分支。

NVIDIA_Share_o7SAYOrMP5.png

如果更新此分支或此分支合并后还遇到报本错误的,请根据上面的更新查找后台的日志。目前见到反馈有因为“Failed to initialize steam api”导致此报错。这个描述的意思是初始化Steam API失败,请通过Steam启动游戏,不要双击游戏本体启动。另有反馈通过管理员模式运行Steam可以解决此问题。

0x0 背景

心心念念等到太吾绘卷更新后,打开就见开门红报错 “由于目标计算机积极拒绝,无法连接”。 进度卡在40%进不去游戏。

去贴吧转了一圈看大家说关闭360可解,可我装的是火绒呀。按照经验此类问题应该是端口冲突导致游戏无法监听特定端口,排查到原因是由于开启Hyper-V后Windows预留了游戏监听的接口导致监听失败。下面给出解决方案,以及排查过程。

0x1 解决方案

管理员模式打开命令行或PowerShell,依次输入以下命令:

net stop winnat

netsh int ipv4 add excludedportrange protocol=tcp startport=51827 numberofports=1

net start winnat

命令来自https://github.com/docker/for-win/issues/3171#issuecomment-873970535

修复命令

0x2 排查过程

首先查看游戏打印的日志,报错的调用链中有一行:GameData.GameDataBridge.GameDataBridge.CheckConnection(),那么就反编译游戏看看这段代码的逻辑吧。

DnSpy定位到对应函数,代码如下:

// Token: 0x06001C33 RID: 7219 RVA: 0x00151408 File Offset: 0x0014F608
public static bool CheckConnection()
{
  bool isCompleted = GameDataBridge._connectionTask.IsCompleted;
  bool result;
  if (isCompleted)
  {
    bool flag = !GameDataBridge._client.Connected;
    if (flag)
    {
      TaskStatus status = GameDataBridge._connectionTask.Status;
      StringBuilder stringBuilder = new StringBuilder(string.Format("CheckConnection: Connection task completed but connection not established: {0}.", status));
      bool flag2 = status == TaskStatus.Faulted;
      if (flag2)
      {
        stringBuilder.Append(string.Format(" Exception:\n{0}.", GameDataBridge._connectionTask.Exception));
      }
      throw new Exception(stringBuilder.ToString());
    }
    GameDataBridge._readingThread.Start();
    GameDataBridge._writingThread.Start();
    GameDataBridge._connectionTask = null;
    GameDataBridge._connectionTimer = null;
    result = true;
  }
  else
  {
    bool flag3 = GameDataBridge._connectionTask.Exception != null;
    if (flag3)
    {
      throw new Exception(string.Format("CheckConnection: {0}", GameDataBridge._connectionTask.Exception));
    }
    bool flag4 = GameDataBridge._connectionTimer.ElapsedMilliseconds >= 3000L;
    if (flag4)
    {
      throw new Exception("CheckConnection: Timeout when connecting to GameData module.");
    }
    result = false;
  }
  return result;
}

可以看到异常就是在这里抛出的,逻辑是GameDataBridge._connectionTask检测失败后抛出异常,查看此Task的初始化过程:

public static void Initialize()
{
  GameDataBridge.ShouldDisconnect = false;
  GameDataBridge._gameDataModuleInitializationState = 0;
  GameDataBridge._client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
  {
    NoDelay = true,
    SendBufferSize = 1048576,
    ReceiveBufferSize = 1048576
  };
  GameDataBridge._connectionTask = GameDataBridge._client.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 51827));
  GameDataBridge._connectionTimer = Stopwatch.StartNew();
  GameDataBridge._readingThread = new Thread(new ThreadStart(GameDataBridge.ReadInterProcessMessages))
  {
    IsBackground = false,
    Name = "ReadInterProcessMessages"
  };
  GameDataBridge._writingThread = new Thread(new ThreadStart(GameDataBridge.WriteInterProcessMessages))
  {
    IsBackground = false,
    Name = "WriteInterProcessMessages"
  };
}

第11行可以看出游戏尝试连接了一个固定端口51827,看到这个端口范围顿时想到可能是Hyper-V的问题,因为之前排查rclone无法授权的问题有遇到过53682端口无法监听。当时排查了很长时间直到谷歌到有老哥指向Windows 10本身的问题。按印象中的关键词再次搜索最终定位到GitHub问题:https://github.com/docker/for-win/issues/3171

根据回复的命令查看Windows预留端口:

netsh int ipv4 show excludedportrange tcp

发现51827确实在列表中:

命令结果

那么再根据Issue回复中老哥的解决方案排除此端口即可。排除后再次查看预留端口:

排除后结果

可以看到后面多了个*号,此端口现在为用户自行预留而非被系统预留。应用就可以正常监听此端口了。具体监听逻辑在另一个进程./Backend/GameData.exe的依赖库GameData.dll里:

namespace GameData.GameDataBridge{
  // Token: 0x06000C47 RID: 3143 RVA: 0x000850FC File Offset: 0x000832FC
  public static void Initialize()
  {
    GameDataBridge._gameDataModuleInitializationState = 0;
    GameDataBridge._listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    GameDataBridge._listener.Bind(new IPEndPoint(IPAddress.Loopback, 51827));
    GameDataBridge._listener.Listen(1);
    GameDataBridge._handler = GameDataBridge._listener.Accept();
    GameDataBridge._handler.NoDelay = true;
    GameDataBridge._handler.SendBufferSize = 1048576;
    GameDataBridge._handler.ReceiveBufferSize = 1048576;
    GameDataBridge._readingThread = new Thread(new ThreadStart(GameDataBridge.ReadInterProcessMessages))
    {
      IsBackground = false,
      Name = "ReadInterProcessMessages"
    };
    GameDataBridge._readingThread.Start();
    GameDataBridge._writingThread = new Thread(new ThreadStart(GameDataBridge.WriteInterProcessMessages))
    {
      IsBackground = false,
      Name = "WriteInterProcessMessages"
    };
    GameDataBridge._writingThread.Start();
  }
}
最后修改:2022 年 09 月 30 日
如果觉得我的文章对你有用,请随意赞赏