FSocket in HTML returns false even connection is a success

Currently I’m trying to export a simple topdown template to html5 with a custom written c++ class using FSocket to enable blueprinting tcp sockets for UE4 designers in our company. We have a node.js backend that accepts both TCP and Websocket connections on different ports.

When I run the current game in editor (or export for mac osx), a TCP connection is made and data send and receive has no problems.

However after exporting to HTML5, the websocket connection is made, yet the FSocket->Connect() always return false. There are no logs or errors about the condition of the connection on browser console log, but I can see a successful connection made on the server part.

This is the header that UE4 is sending when game is run in HTML5.

<< IN: 127.0.0.1|56135
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1:8000
Origin: http://127.0.0.1:55966
Sec-WebSocket-Protocol: binary
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: MKHvPf1T2PK1Y/j1RaRzxw==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12

Here is the code I’m using to connect to the current backend.

UTCPSocketNetworking *UTCPSocketNetworking::TCPConnect ()
{
  // Do not try to connect again
  if ( this->IsConnected ) {
    printGreen("TCPConnect -- Already connected");
    OnSocketConnected.Broadcast();
    return this;
  }

  //<editor-fold desc="Check missing variables">
  {
    // Not connected yet
    IsConnected = false;

    if ( ServerIP == "" ) {
      printRed("TCPConnect -- ERROR -- Server ip or name is not set yet");
      OnSocketCouldNotConnect.Broadcast();
      return this;
    }

    if ( !ServerPort ) {
      printRed("TCPConnect -- ERROR -- Server port is not set yet");
      OnSocketCouldNotConnect.Broadcast();
      return this;
    }

    if ( SocketName == "" ) {
      printRed("TCPConnect -- ERROR -- Socket name is not set yet");
      OnSocketCouldNotConnect.Broadcast();
      return this;
    }
  }
  //</editor-fold>

  //<editor-fold desc="Create socket">
  {
    FIPv4Address ip;
    FIPv4Address::Parse(ServerIP, ip);

    auto LocalAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
    LocalAddress->SetIp(ip.GetValue());
    LocalAddress->SetPort(ServerPort);
    
    Socket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, this->SocketName, false);
    if ( !Socket->Connect(*LocalAddress) ) {
      printRed("TCPConnect -- ERROR -- Could not connect to local address");
      OnSocketCouldNotConnect.Broadcast();
      return this;
    }
  }
  //</editor-fold>

  IsConnected = true;
  OnSocketConnected.Broadcast();

  return this;
}
  • This method is called from a blueprint.
  • Socket is an FSocket.
  • ServerIP, ServerPort and SocketName are variables visible for blueprinting.
  • IsConnected is a private variable for checking if socket is connected on c++ side.
  • OnSocketCouldNotConnect and OnSocketConnected are delegates declared via DECLARE_DYNAMIC_MULTICAST_DELEGATE and are working in each case.
  • Experimental HTML5Networking is enabled in Unreal Engine plugins.
  • Tried to run the game in Google, Firefox and Safari, all resulting with the same fail.

Is there any other way of using the FSocket to enable HTML5 networking / websockets ? Are there any tutorials/codes that I can look at for understanding the concept as the documentation does not exist.

Couple of things -

HTML5 networking plugin has nothing to do with what you are trying to do. It is only used for multiplayer. you can safely disable that.

emscripten toolkit wraps websocket with the bsd networking api with a best effort implementation, which is then wrapped by FSocket classes.

The problem which I think you are facing is that there’s is no synchronous connect for web sockets and hence the synchronous Connect ( which works on all other platforms as advertised) returns false.

http://kripken.github.io/emscripten-site/docs/porting/guidelines/api_limitations.html#api-limitations

Thanks for the reply. So if i’m understanding this correctly, I need to move the socket connection to another thread, and return the received data to main (game) thread with a delegate (or event maybe). Will FRunnableThread be able to achieve this?

just threading it might not work - HTML5 is single threaded.

basically you need to make this bit of code async


if ( !Socket->Connect(*LocalAddress) ) {
printRed(“TCPConnect – ERROR – Could not connect to local address”);
OnSocketCouldNotConnect.Broadcast();
return this;
}
}

This could be replaced by just
Socket->Connect(*LocalAddress);

And in the tick function you poll for the connectivity and fire the events when the socket actually connects.

After changing the Socket->Connect and trying to poll via tick created other problems.

TCP is still working fine. But this time if the server is unreachable, Unreal Engine is crashing.

After checking, I found out that Socket’s connection state is returning SCS_Connected and not SCS_ConnectionError. As the code thinks the connectin was a success, it tries to push messages and crashes.

Also on HTML part, I’m receiving another error from js file. Attached a screen capture, but in short version the error is “Assertion failed: exceptfds not supported”.

Any ideas?

You can cross reference the minified callstack with the symbols file in - it should be in the same location as the testGame.js to see what exactly is the callstack

emscripten/library.js at 667dcd241886fdb878e248d95d8e03abb09c80b7 · emscripten-core/emscripten · GitHub - basically exceptfs as a paremter in the select call are is not supported.

With these two pieces of information you should be able to debug.

It maybe that you are using a Select call or FSocket wrapper for the select call automatically adds exceptfds parameter.

If all fails - Look at WebsSocket.cpp specially in code within #PLATFORM_HTML5, we don’t use FSocket but just RAW bsd. you maybe able to use just raw BSD for HTML5 code path. for use case, its ugly but may work for your use case.

Debugging was taking too long, files are too big for processing. So I decided to take your second suggestion to heart. And it works :slight_smile:

I created a new class and implemented most of the functionality from WebSocket.cpp. TCP and websocket, both are connecting, sending and receiving data. Just used the “PLATFORM_HTML5_BROWSER” parts… Thanks for that.

But there is something strange with the sent and received buffers. The sent buffer contains some characters (4 chars to be exact) before the data. Something like this:

L\u0000\u0000\u0000{"namespace": "Login","action": "login","data":{"token": "0"}}

There is nothing on the end. Also the received data gets truncated by the same size, I presume the “L…” is needed on the server part? The returned buffer becomes:

mespace":"ContinueLogin","action":"savetoken","data":{"token":"127.0.0.1:55404"}}

{"na part from the beginning gets removed…

Do you have any idea where these additional bytes are coming from, and the incoming data gets truncated?

I see that you are padding the data when you are using libwebsocket implementation. Is it possible that sys/socket has something build in?

EDIT:
Oh, I’m converting FString to std string. Could this be the problem?

std::string MyStdString(TCHAR_TO_UTF8(*UE4Str));

EDIT 2:
I’ve replaced TCHAR_TO_UTF8 to TCHAR_TO_ANSI and the 3 byte order marks disappeared. However the “L” is still standing strong. Could this be coming from the StringCast?

Also I’m using built in JSON classes as you can guess. Could TJsonWriter be responsible for the L char?

Just saw that the truncation of the incoming message is caused by the OnRawReceive implementation. WebSocket.cpp has a few lines of code for checking if data is waiting.

	uint8 Buffer[1024]; // should be at MAX PACKET SIZE. 
	int Result = recv(SockFd, Buffer, sizeof(uint32), 0);

	uint32 DataToBeRead = 0;*(uint32*)Buffer;

As the buffer is read by sizeof(uint32), I’m loosing some data.

  • Added a regex on server side to remove the accessive strings right before { … }
  • I did not realize the padding was being used to determine the data size :slight_smile: Nice solution. Implementing this on the server side as well.

Great code, loving this :slight_smile: