2

我正在尝试在 Ruby 服务器和与 Unreal Engine 4 接口的 C++ 库之间实现简单的本地 TCP 通信。

我为 ruby​​ 服务器使用了一些示例代码,它使用了socket库并实例化了TCPServer

require 'socket'

puts "Starting up server..."
# establish the server
## Server established to listen for connections on port 2008
server = TCPServer.new(15300)
# setup to listen and accept connections
while (session = server.accept)
 #start new thread conversation
 ## Here we will establish a new thread for a connection client
 Thread.start do
   ## I want to be sure to output something on the server side
   ## to show that there has been a connection
   puts "log: Connection from #{session.peeraddr[2]} at
          #{session.peeraddr[3]}"
   puts "log: got input from client"
   ## lets see what the client has to say by grabbing the input
   ## then display it. Please note that the session.gets will look
   ## for an end of line character "\n" before moving forward.
   input = session.gets
   puts input
   ## Lets respond with a nice warm welcome message
   session.puts "Server: Welcome #{session.peeraddr[2]}\n"
   # reply with goodbye
   ## now lets end the session since all we wanted to do is
   ## acknowledge the client
   puts "log: sending goodbye"
   session.puts "Server: Goodbye\n"
 end  #end thread conversation
end   #end loop

这个应用程序,如果用 ruby​​ 客户端测试,工作得非常好。

这是客户端:

require 'socket'
# establish connection
## We need to tell the client where to connect
## Conveniently it is on localhost at port 15300!
clientSession = TCPSocket.new( "localhost", 15300 )
puts "log: starting connection"
#send a quick message
## Note that this has a carriage return. Remember our server
## uses the method gets() to get input back from the server.
puts "log: saying hello"
clientSession.puts "Client: Hello Server World!\n"
#wait for messages from the server
## You've sent your message, now we need to make sure
## the session isn't closed, spit out any messages the server
## has to say, and check to see if any of those messages
## contain 'Goodbye'. If they do we can close the connection
 while !(clientSession.closed?) &&
          (serverMessage = clientSession.gets)
  ## lets output our server messages
  puts serverMessage
  #if one of the messages contains 'Goodbye' we'll disconnect
  ## we disconnect by 'closing' the session.
  if serverMessage.include?("Goodbye")
   puts "log: closing connection"
   clientSession.close
  end
 end #end loop

服务器输出:

Starting up server...
log: Connection from ::1 at
          ::1
log: got input from client
Client: Hello Server World!
log: sending goodbye

但是当我尝试使用 C++11 和 Unreal Engine 4 中的 TCP 函数连接到服务器时,我没有从用 ruby​​ 实现的服务器得到任何类型的响应(甚至没有“来自...的连接”)。

为了了解问题是什么,我尝试运行一些网络分析,从最简单的(Window 的资源监视器)到最复杂的(ZenMap)。在任何情况下,都没有在选定端口打开(端口 15300)的情况下运行单个服务。我已经仔细检查了每一个可能的原因(例如防火墙、其他安全软件),但没有阻止 ruby​​ 解释器。

为了理解为什么这么简单的应用程序不起作用,我开始使用 Wireshark。就在那时我注意到没有本地环回接口,这需要我使用 RawCap(它设法捕获本地通信并将它们转储到文件中)。使用它,我在运行 ruby​​ 客户端/服务器对和 Cygwin Socat/Unreal Engine 4 期间设法转储了我机器上的本地通信。我发现非常令人困惑:端口上没有打开的本地 TCP 套接字ruby 对(端口 15300),但虚幻引擎 4 打开的 TCP 端口在那里(以及端口 15301,我用于测试的端口)。

更新01

我已经更改了我的虚幻引擎 4 应用程序中的代码,因为第一个版本使用了一个包含的 TCPsocket 构建器,它bind默认使用而不是connect. 现在代码是:

bool UNetworkBlueprintLibrary::NetworkSetup(int32 ServerPort) {

    bool res = false;

    // Creating a Socket pointer, wich will temporary contain our 
    FSocket* Socket = nullptr;
    ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);

    // Instead of that useless FTcpSocketBuilder I will try to create the socket by hand and then debug it... >:(
    if (SocketSubsystem != nullptr) {
        // Try to create a stream socket using the system-intependent interface
        Socket = SocketSubsystem->CreateSocket(NAME_Stream, TEXT("Server Socket"), false);

        if (Socket != nullptr) {
            FIPv4Address ServerAddress = FIPv4Address(127, 0, 0, 1);
            TSharedRef<FInternetAddr> LocalAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
            uint16 castServerPort = (uint16)ServerPort;

            // Attach received port and IPAddress to Internet Address pointer
            LocalAddress->SetIp(ServerAddress.GetValue());
            LocalAddress->SetPort(castServerPort);

            bool SocketCreationError = !Socket->Connect(*LocalAddress);

            if (SocketCreationError)
            {
                GLog->Logf(TEXT("Failed to create Server Socket as configured!"));

                SocketSubsystem->DestroySocket(Socket);

                Socket = nullptr;
            }
        }
    }



    if (Socket != nullptr){
        UNetworkBlueprintLibrary::ServerSocket = Socket;
        res = true;
    }
    else {
        res = false;
    }

    return res;
} 

为了测试 Unreal Engine 4 中的代码是否正常工作,我socat在 Cygwin 下使用过:

socat tcp-listen:15300 readline

一旦我的虚幻引擎 4 应用程序启动,我就可以通过套接字发送数据。我已经确认这是可行的,消除了我在虚幻引擎 4 中开发的应用程序没有通过 TCP 套接字进行通信的可能性。

鉴于这个积极的结果,我再次尝试使用在这个问题之上报告的服务器代码,删除等待来自客户端的消息的行,但结果是相同的:服务器甚至不输出它应该打印的调试行当有连接时:

puts "log: Connection from #{session.peeraddr[2]} at #{session.peeraddr[3]}"

更新 02

再一次回来!

我已经设法通过本地 TCP 套接字进行通信rubyUnreal Engine 4我需要让 ruby​​ 服务器正常工作是明确提供本地地址,如下所示:

server = TCPServer.new("127.0.0.1", 15300)

不过,即使这件事解决了,我还是对我第一次提出的一些问题感兴趣。

更新 03

只是我在最后一个小时内所做的一个考虑。

当我没有为服务器提供明确的服务器地址时,此命令

   puts "log: Connection from #{session.peeraddr[2]} at
          #{session.peeraddr[3]}"

回来

log: Connection from ::1 at
          ::1

这确实是正确的!::1是的 IPv6 版本127.0.0.1,所以看起来它ruby假设它必须创建一个 IPv6 套接字,而不是 IPv4。Unreal Engine 4不提供 IPv6 接口,因此这可能是应用程序无法运行的原因。

现在localhost转储时通信结束,显示端口 15300 上的以下通信:

No.     Time        Source                Destination           Protocol Length Info
    105 7.573433    127.0.0.1             127.0.0.1             TCP      52     9994→15300 [SYN] Seq=0 Win=8192 Len=0 MSS=65495 WS=256 SACK_PERM=1
    106 7.573433    127.0.0.1             127.0.0.1             TCP      52     15300→9994 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=65495 WS=256 SACK_PERM=1
    107 7.573433    127.0.0.1             127.0.0.1             TCP      40     9994→15300 [ACK] Seq=1 Ack=1 Win=8192 Len=0
    108 7.583434    127.0.0.1             127.0.0.1             TCP      66     15300→9994 [PSH, ACK] Seq=1 Ack=1 Win=8192 Len=26
    109 7.583434    127.0.0.1             127.0.0.1             TCP      40     9994→15300 [ACK] Seq=1 Ack=27 Win=7936 Len=0
    110 7.583434    127.0.0.1             127.0.0.1             TCP      56     15300→9994 [PSH, ACK] Seq=27 Ack=1 Win=8192 Len=16
    111 7.583434    127.0.0.1             127.0.0.1             TCP      40     9994→15300 [ACK] Seq=1 Ack=43 Win=7936 Len=0
    208 16.450941   127.0.0.1             127.0.0.1             TCP      52     9995→15300 [SYN] Seq=0 Win=8192 Len=0 MSS=65495 WS=256 SACK_PERM=1
    209 16.451941   127.0.0.1             127.0.0.1             TCP      52     15300→9995 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=65495 WS=256 SACK_PERM=1
    210 16.451941   127.0.0.1             127.0.0.1             TCP      40     9995→15300 [ACK] Seq=1 Ack=1 Win=8192 Len=0
    211 16.456941   127.0.0.1             127.0.0.1             TCP      66     15300→9995 [PSH, ACK] Seq=1 Ack=1 Win=8192 Len=26
    212 16.456941   127.0.0.1             127.0.0.1             TCP      40     9995→15300 [ACK] Seq=1 Ack=27 Win=7936 Len=0
    213 16.456941   127.0.0.1             127.0.0.1             TCP      56     15300→9995 [PSH, ACK] Seq=27 Ack=1 Win=8192 Len=16
    214 16.456941   127.0.0.1             127.0.0.1             TCP      40     9995→15300 [ACK] Seq=1 Ack=43 Win=7936 Len=0

当包裹显示LEN26 或 16 时,意味着他们正在发送

Server: Welcome 127.0.0.1.

或者

Server: Goodbye.

我只是想分享这些信息,因为它对答案并没有真正有用,但显示了 IPv6 通信如何不通过标准localhost虚拟适配器。

问题

我想了解的是:

  • 由于没有本地环回接口,ruby 如何通过本地 TCP 套接字进行通信?
4

0 回答 0