我正在尝试在Ruby服务器和与虚幻引擎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和虚幻引擎4中的TCP函数连接到服务器时,我没有从ruby中实现的服务器获得任何响应(甚至不是“来自......的连接”)
要了解问题是什么,我尝试运行一些netwrok分析,从最简单的(Window的资源监视器)开始,到最复杂的(ZenMap)。在任何情况下,都没有一个服务在所选端口打开的情况下运行(端口15300)。我已经仔细检查了每一个可能的原因(例如防火墙,其他安全软件),但没有阻止ruby解释器。
为了理解为什么这么简单的应用程序无法正常工作,我开始使用Wireshark。就在那时我注意到没有本地环回接口,这需要我使用RawCap(它设法捕获本地通信并将它们转储到文件中)。使用它我在ruby客户端/服务器对和Cygwin Socat /虚幻引擎4的运行期间设法将本地通信转储到我的机器上。我发现的是非常令人费解的:端口上没有打开的本地TCP套接字用于ruby pair(端口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;
}
为了测试虚幻引擎4中的代码是否正常工作,我在Cygwin下使用了socat
:
socat tcp-listen:15300 readline
一旦我的虚幻引擎4应用程序启动,我就可以通过套接字发送数据。我已经确认这是有效的,消除了我在虚幻引擎4中开发的应用程序没有通过TCP套接字进行通信的可能性。
鉴于这个积极的结果,我再次尝试使用在此问题之上报告的服务器代码,删除等待来自客户端的消息的行,但结果是相同的:服务器甚至不输出调试行它应该在有连接时打印:
puts "log: Connection from #{session.peeraddr[2]} at #{session.peeraddr[3]}"
更新02
再次回来!
我设法让ruby
和Unreal Engine 4
通过本地TCP套接字进行通信。我需要使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
是127.0.0.1
的IPv6版本,因此看起来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
当一个包显示26或16的LEN
时,表示他们正在发送
Server: Welcome 127.0.0.1.
或
Server: Goodbye.
我只想分享这些信息,因为它对答案并不实用,但显示了IPv6通信如何不通过标准localhost
虚拟适配器。
问题
我想了解的是: