mongoose – The Industrious Squirrel https://blog.chadweisshaar.com Tue, 26 May 2015 22:17:28 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.3 https://blog.chadweisshaar.com/wp-content/uploads/2016/07/favicon.png mongoose – The Industrious Squirrel https://blog.chadweisshaar.com 32 32 Mongoose for Unity https://blog.chadweisshaar.com/2015/05/26/mongoose-for-unity/ https://blog.chadweisshaar.com/2015/05/26/mongoose-for-unity/#comments Tue, 26 May 2015 22:17:28 +0000 http://gator3305.temp.domains/~cweissha/blog/?p=753 Continue reading "Mongoose for Unity"]]> Mongoose is a C library for embedding a simple web server in another application. We used Mongoose for all of our web-enabled Torque games and I wanted to be able to continue using it for new games in Unity.

There is an existing extension called UniWeb which provides a web server in unity, but their code doesn’t support web sockets. I’ve just built Mongoose in windows, but it is Linux and Mac compatible as well. However, it wont support any of the mobile platforms.

This post will describe the steps for building Mongoose 5.6 as a windows native DLL, then wrapping it for use in C#, and finally including it in a Unity project.

To create a DLL from the mongoose source code available on github, I created a DLL project in visual studio, set it to 64 bit, and added __declspec(dllexport) declarations for all the functions I want available from C#.

The C# wrapper requires defining all the data types that are passed to the exported functions. There are two data structures declared in the C code which look like this:


struct mg_connection {
const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL

char remote_ip[48]; // Max IPv6 string length is 45 characters
char local_ip[48]; // Local IP address
unsigned short remote_port; // Client's port
unsigned short local_port; // Local port number

int num_headers; // Number of HTTP headers
struct mg_header {
  const char *name; // HTTP header name
  const char *value; // HTTP header value
} http_headers[30];

char *content; // POST (or websocket message) data, or NULL
size_t content_len; // Data length

int is_websocket; // Connection is a websocket connection
int status_code; // HTTP status code for HTTP error handler
int wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_create_server()
void *connection_param; // Placeholder for connection-specific data
void *callback_param;
};

I am building 64 bit applications, so the C# wrapper for this structure looks like this:


// mongoose.h :: struct mg_connection { struct mg_header }
[StructLayout(LayoutKind.Sequential)]
public struct MongooseHeader
{
  [MarshalAs(UnmanagedType.LPStr)]
  public string name;
  [MarshalAs(UnmanagedType.LPStr)]
  public string value;
};

// mongoose.h :: struct mg_connection
[StructLayout(LayoutKind.Sequential)]
public struct MongooseConnection
{
[MarshalAs(UnmanagedType.LPStr)]
public string request_method;
[MarshalAs(UnmanagedType.LPStr)]
public string uri;
[MarshalAs(UnmanagedType.LPStr)]
public string http_version;
[MarshalAs(UnmanagedType.LPStr)]
public string query_string;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)]
public char[] remote_ip;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)]
public char[] local_ip;
[MarshalAs(UnmanagedType.U2)]
public UInt16 remote_port;
[MarshalAs(UnmanagedType.U2)]
public UInt16 local_port;

[MarshalAs(UnmanagedType.I4)]
public int num_headers;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public MongooseHeader[] http_headers;

public IntPtr content;
public System.UInt64 content_len;

[MarshalAs(UnmanagedType.I4)]
public int is_websocket;
[MarshalAs(UnmanagedType.I4)]
public int status_code;
[MarshalAs(UnmanagedType.I4)]
public int wsbits;

public IntPtr server_param;
public IntPtr connection_param;
public IntPtr callback_param;
};

Anything that is a char* gets converted to a string, unsigned short becomes UInt16, int is the same, but needs to be marked as I4 to have the right size. The void* pointers are converted to IntPtr which is the C# unmanaged pointer type.

I replicated the enums that I’m going to use in C#:


    public enum MongooseResult { MG_FALSE, MG_TRUE, MG_MORE };
    private enum MongooseEvent
    {
      MG_POLL = 100,  // If callback returns MG_TRUE connection closes
                      // after all of data is sent
      MG_CONNECT,     // If callback returns MG_FALSE, connect fails
      MG_AUTH,        // If callback returns MG_FALSE, authentication fails
      MG_REQUEST,     // If callback returns MG_FALSE, Mongoose continues with req
      MG_REPLY,       // If callback returns MG_FALSE, Mongoose closes connection
      MG_RECV,        // Mongoose has received POST data chunk.
                      // Callback should return a number of bytes to discard from
                      // the receive buffer, or -1 to close the connection.
      MG_CLOSE,       // Connection is closed, callback return value is ignored
      MG_WS_HANDSHAKE,  // New websocket connection, handshake request
      MG_WS_CONNECT,  // New websocket connection established
      MG_HTTP_ERROR   // If callback returns MG_FALSE, Mongoose continues with err
    };

    // Possible websocket opcodes. These are available as the right four bits of
    // the websocket status in the connection after a MG_REQUEST coming from a
    // websocket. The first four bits are:
    // bit 0. 1 if this is the last frame in the message
    // bits 1-3. reserved
    private enum MongooseWebsockOpcode
    {
      WEBSOCKET_OPCODE_CONTINUATION = 0x0,
      WEBSOCKET_OPCODE_TEXT = 0x1,
      WEBSOCKET_OPCODE_BINARY = 0x2,
      WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
      WEBSOCKET_OPCODE_PING = 0x9,
      WEBSOCKET_OPCODE_PONG = 0xa
    };

The functions are treated much like the data structures. The original function signatures from the C code:

typedef int (* mg_handler_t)(struct mg_connection *, enum mg_event);
// Server management functions
DLL_API struct mg_server * mg_create_server(void *server_param, mg_handler_t handler);
DLL_API time_t mg_poll_server(struct mg_server *, int milliseconds);
DLL_API const char * mg_set_option(struct mg_server *, const char *opt, const char *val);
DLL_API const char * mg_get_option(const struct mg_server *server, const char *name);
DLL_API void mg_send_status(struct mg_connection *, int status_code);
DLL_API void mg_send_header(struct mg_connection *, const char *name, const char *val);
DLL_API size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
DLL_API size_t mg_websocket_write(struct mg_connection *, int opcode,
                                  const char *data, size_t data_len);
DLL_API void mg_destroy_server(struct mg_server *);
DLL_API struct mg_connection * mg_next(struct mg_server *, struct mg_connection *);

Are wrapped as static extern functions in C#:

[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr mg_create_server(IntPtr user_data, MongooseEventHandler eh);
[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall)]
private static extern int mg_poll_server(IntPtr server, int milli);
[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern IntPtr mg_set_option(IntPtr server, string name, string value);
[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern IntPtr mg_get_option(IntPtr server, string name);

[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern ulong mg_send_header(IntPtr conn, string name, string value);
// Note: This could be binary data in the C++ code.
[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern ulong mg_send_data(IntPtr conn, string data, int length);
[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern ulong mg_websocket_write(IntPtr conn, int opCode, string data, ulong dataLength);
[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall)]
private static extern void mg_destroy_server(IntPtr server);

[DllImport("Mongoose", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr mg_next(IntPtr server, IntPtr connection);

Most of this is standard. I am using the StdCall calling convention and the Ansi character set since this is calling C code instead of C++. The mg_create_server function takes an event handler callback function and returns a pointer to the server. The C library manages the memory for the server pointer, but the event handler that the C# code creates to pass to this function needs to be protected from garbage collection.

The code to do this looks like:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int MongooseEventHandler(IntPtr c, int ev);

private IntPtr _server;
private MongooseEventHandler _delegateInstance;

public bool startWebServer(string documentRoot, int port)
{
  _delegateInstance = new MongooseEventHandler(EventHandler);
  _server = mg_create_server(IntPtr.Zero, _delegateInstance);
}

Another bit of conversion code is required for any data that is passed to the C# code by pointer from the C code. Examples are the mg_get_option function that returns a const char* and the event handler that takes a takes an mg_connection*. This memory can’t be accessed directly by the managed C# code, so it need to be Marshaled (copied) into managed memory like this:

public string getOption(string option)
{
  return Marshal.PtrToStringAnsi(mg_get_option(_server, option));
}
private int EventHandler(IntPtr conn_ptr, int ev)
{
  MongooseConnection conn = (MongooseConnection)
    System.Runtime.InteropServices.Marshal.PtrToStructure(
      conn_ptr, typeof(MongooseConnection));
...
}

I wrote a C# class to wrap these functions and to do some basic connection management and provide default behavior. The wrapper class will keep a list of connections that are active so that it can detect dropped connections during the poll. I also abstracted away the mg_connection structure for most functions and just pass the client’s port number as the key to reference a connection. I also setup some defaults (no authentication, default error handling and serving up files from the local filesystem under a given web root). Here is the whole class is you are interested: Mongoose.cs (there is a tiny bit of Unity logging code, if you aren’t using Unity, just remove those lines)

Finally, to use this web server in Unity, I just had to create a behavior that starts, stops and polls the web server using the wrapper class. For default behavior, this code looks like:

public class TestWebserver : MonoBehaviour {

  class WebSocketServer : Mongoose.WebServer
  {
     // To create custom behavior, override the default handlers here
  }
  private WebSocketServer _webServer;

  void Start () {
    Debug.Log("Starting webserver");
    _webServer = new WebSocketServer(this);
    _webServer.startWebServer("web", 8080);
  }
	
  // Update is called once per frame. Poll the webserver, don't wait for connections, just proccess what is already waiting
  void Update () {
    _webServer.poll(0);
  }

  void OnDestroy()
  {
    Debug.Log("Stoping webserver");
    _webServer.close();
  }
}

Add this behavior to any game object and you have a webserver serving files from <Unity Application Dir>/web on port 8080. A little bit more code allows for websocket connection handling and communication.

]]>
https://blog.chadweisshaar.com/2015/05/26/mongoose-for-unity/feed/ 1
Web controls for Fire Platoon https://blog.chadweisshaar.com/2014/02/13/web-controls-for-fire-platoon/ https://blog.chadweisshaar.com/2014/02/13/web-controls-for-fire-platoon/#respond Fri, 14 Feb 2014 05:18:56 +0000 http://gator3305.temp.domains/~cweissha/blog/?p=574 Continue reading "Web controls for Fire Platoon"]]> We have decided to move the player controls off the touch table and onto individual web-enabled devices in Fire Platoon. There are a couple reasons for this decision. Most importantly, we didn’t have enough space to put even four player’s controls onto the screen and still have legible icons on the main map of the building. Second, we think that players will feel more connected to their in-game fire fighter if they are holding the controls in their hands. With off-screen controls, we can make the game map bigger and support more players (probably 8)

We are going to use web sockets to send messages between the game and the player’s web clients. Web sockets are now supported by all the web browsers, so anyone with a smartphone or tablet should be able to connect to the game and play without downloading an app.

We have been using mongoose 3.7 as our built-in web server. We have had a couple problems with it and we decided to upgrade to the latest version which is 5.2. There have been significant changes to how the web server operates. In 3.7 it used a thread pool to handle the connections, which limited the number of simultaneous connections (not really a problem for us), and forced us to pass data received from a client to the main thread for processing. Mongoose 5.2 uses asynchronous IO and has significantly better web socket handling. The new web socket interface allows us to detect when a client disconnects and to force a disconnect. We didn’t expect to see much performance difference, but we were pleasantly surprised that the new version connects quite a bit faster.

Integrating mongoose into Torque was significantly easier than the first time I did it, but it was significantly different because it needed to poll every tick and it consolidated many event handlers into one. If anyone is interested, here is the source code:

WebServer for Torque 2D

Note: this post was written 3/23/2014 and retroactively published to 2/13/2014 which is when I did the work.
]]>
https://blog.chadweisshaar.com/2014/02/13/web-controls-for-fire-platoon/feed/ 0
Web sockets and Mongoose https://blog.chadweisshaar.com/2012/12/08/web-sockets-and-mongoose/ https://blog.chadweisshaar.com/2012/12/08/web-sockets-and-mongoose/#comments Sun, 09 Dec 2012 05:03:31 +0000 http://gator3305.temp.domains/~cweissha/blog/?p=265 Continue reading "Web sockets and Mongoose"]]> A couple of weeks ago, I experimented with using bluetooth to communicate hidden information from a game to a phone held by the players. While the prototype worked, there were issues with client side logic and lack of support for iOS.

Another way to achive the goal of displaying hidden information on the phone would be to create a web page that the phone could access. In the past, we have done this by having the game state in a MySQL database and php scripts to access the data. The game server and clients would poll the data watching for updates and sending their moves. This system was cumbersome, laggy and prone to error.

However, the web page is a nice way to provide the data to the clients. It is easy to layout a good looking interface, you can use javascript to allow the player to interact with the game and all phones have a web browser. The problem lies in the communication back to the C++ game.

One solution to this problem would be to use web sockets to send data back and forth between the clients and the game. Ideally, the C++ game would serve out the web pages that the clients display too so that people buying the game wouldn’t have to know how to setup a web server.

Mongoose is a simple web server with web socket support that can be compiled into a C++ application. With a couple of minor tweaks I got the mongoose code integrated into Torque.

Adding web sockets was a bit more problematic. The support for raw web socket connections comes with Mongoose, but I needed to write the code to send and receive the messages in the format expected by the chrome WebSocket class.

This code is surprisingly messy. The first byte is always 0x81. The second byte is a length, but the most significant bit is an indicator of whether there is a mask. If that second byte is less than 126 it is just the length of the data. If it is equal to 126 then the next two bytes are the length if it is equal to 127, then the next four bytes are the length. What is even stranger is that if the mask is set, then all the data sent is XORed with the  a prior byte. Here is the code I ended up with to read a message:

  // Read the message from the client
  // For now we will assume that this is a string of text
  // Read in the header and size
  unsigned char header[10];
  int totalRead = 0;
  while ( totalRead < 2 )
  {
    int bytesRead = mg_read(conn, header+totalRead, 2-totalRead);
    if ( bytesRead <=0 )
      return nullptr;
    totalRead += bytesRead;
  }
  // Check the data received
  if ( header[0] != 0x81 )
    return nullptr;

  long long messageLength = header[1] & 127;
  int maskLength = (header[1] & 128) ? 4 : 0;
  if ( messageLength == 126 ) // Large message
  {
    totalRead = 0;
    while ( totalRead < 2 )
    {
      int bytesRead = mg_read(conn, header+totalRead, 2-totalRead);
      if ( bytesRead <=0 )
        return nullptr;
      totalRead += bytesRead;
    }
    messageLength = (((int) header[0]) << 8) + header[1];
  }
  else if ( messageLength > 126 ) // Very large message
  {
    totalRead = 0;
    while ( totalRead < 8 )
    {
      int bytesRead = mg_read(conn, header+totalRead, 8-totalRead);
      if ( bytesRead <=0 )
        return nullptr;
      totalRead += bytesRead;
    }
    messageLength =  (((long long) htonl(*(int*)&header[0])) << 32) | htonl(*(int*)&header[4]);
  }

  // Now read the data
  unsigned char* buf = new unsigned char[messageLength+maskLength];
  totalRead = 0;
  while ( totalRead < messageLength+maskLength )
  {
    int bytesRead = mg_read(conn, buf+totalRead, messageLength+maskLength-totalRead);
    if ( bytesRead <=0 )
    {
      delete[] buf;
      return nullptr;
    }
    totalRead += bytesRead;
  }
  char* message = new char[messageLength+1];
  for ( int i=0; i<messageLength; ++i )
  {
    int xor = (maskLength==0 ? 0 : buf[i%4]);
    message[i] = buf[i+maskLength] ^ xor;
  }
  message[messageLength] = '\0';
  delete[] buf;
  return message;

With Mongoose integrated and web sockets working, our Torque games can communicate in real-time with web based clients. And since Awesomium is also integrated into our engine, the same web page can be viewed by the players sitting at the touch table. We needed to make a couple improvements to Awesomium so that people could actually interact with the web page:

  • To get TUIO and mouse events to the web page, Awesomium requires that touch/mouse events be injected into its system. The mouse events were straight-forward, but the touch event requires a list of new, changed and unchanged touch points every time a touch is added, removed or moved.
  • To allow a user to enter data into a web page, we need to provide a virtual keyboard and inject keyboard events into Awesomium
]]>
https://blog.chadweisshaar.com/2012/12/08/web-sockets-and-mongoose/feed/ 2