我正在尝试在 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 套接字进行通信ruby
。Unreal 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
当包裹显示LEN
26 或 16 时,意味着他们正在发送
Server: Welcome 127.0.0.1.
或者
Server: Goodbye.
我只是想分享这些信息,因为它对答案并没有真正有用,但显示了 IPv6 通信如何不通过标准localhost
虚拟适配器。
问题
我想了解的是:
- 由于没有本地环回接口,ruby 如何通过本地 TCP 套接字进行通信?