Example demonstrating how to connect a browser (which utilizes Websockets
) to a TCP
server (in this case, a C++
server using Berkeley sockets) using websockify
as a proxy.
I had been working on a pet project (a multi-player version of pacman) to teach myself C++
, concurrency (e.g. threads, locks, condition variables), and networking. I had built a client-server architecture (both written in C++
) which utilized typical Berkeley sockets that come out of the box with Linux and C++
to communicate via TCP/IP
. Given this is a real-time game server, the app was not RESTful (i.e. not using HTTP) and needed to use full duplex long-lived connections and the server was stateful.
As part of this, I wanted to expand this project to make the client into a Web-based application using React for the front-end + Flask as the api for some adjacent RESTful services (e.g. landing page, creating users, etc). HOWEVER, I soon discovered that it is nontrivial to connect a TCP
server to a browser! Fortunately, modern browsers have the concept of WebSockets
which I stumbled upon that are similiar in concept to traditional Berkeley sockets - in particular, allowing full duplex communication over a single TCP connection - but are less generic (i.e. WebSockets are application level protocols over TCP
- just like HTTP
). You can read more about them Here.
As such, I needed to figure out how to connect my C++ game server using Berkeley sockets with TCP to a Browser using WebSockets
As I looked into how to solve this problem, there were a few things I stumbled upon.
1. Socket.IO
- Socket.IO is a common library that implements an even higher level protocol on top WebSockets.
- Serveral common Web Frameworks / languages appear to hav good support for
Socket.IO
, includingflask
which you can see here andnode
. Unfortunately, I could not find a server side implementation ofSocket.IO
in C++ (though there are some clients available). So this was a no go, but might be a good option if you have apython
ornode
application.
2. Refactoring my C++ server to directly use WebSockets
- I could have used the
WebSocket
API directly in my C++, which would have required me to refactor my existing codebase to use the higher level protocol. This would have required either implementing theWebSocket
protocol by hand or using a 3rd party implementation ofWebSockets
. I did not want to reinvent the wheel by reimplementing the handshake and overallWebSocket
protocol, so the only approach I considered was looking for a 3rd party app. - This approach had 2 drawbacks, however. First, I could not find a well-accepted library which implements the handshake and overhead code (i.e. this was not in the stdlib). Second, I would have had to re-write my game server (which I had already built to rely on TCP) which would have taken considerable effort.
3. Using websockify
as a middleware proxy to connect a traditional TCP server to a WebSocket
websockify
"at its most basic level, just translatesWebSocket
traffic to normal socket traffic."- This was perfect for my issue, since I could keep my
TCP
-based game server untouched and rely on the proxy server to facilitate the translation between the higher levelWebSocket
protocol and the lower levelTCP
. - The link the
node.js
version of thewebsockify
server is here. (Note: the main version of the project is written inpython
and can be found here. - This project was spun out of the
noVPC
project (which is anHTML5
-based version of a remote-linux desktop) which was solving this exact problem I had (theVPC
server usedTCP
but the browser usesWebSockets
). - Unfortunately, I had a lot of trouble getting this setup and the =examples found online are relatively few and far between, so I thought it would be helpful to provide the code and how-to of the proofs of concepts I did to get everything working
This repo includes a couple of files, which are described below:
1. echo-server-linux.cpp
- A basic
TCP
server running onLinux
- Opens up a Berkeley socket, waits for clients to connect on
port 5000
and send a message and echos back[client message] + "from the server"
. - Complie and run with:
g++ echo-server-linux.cpp -o echo-server-linux
and run with.\echo-server-linux
.
2. echo-server-win.cpp
- A basic
TCP
server running onWindows
- Opens up a
WinSock
, waits for clients to connect onport 5000
and send a message and echos back[client message]
. - Complile and run with:
g++ echo-server-win.cpp -o echo-server-win -lWS2_32
and run withecho-server-win.exe
. ' g++
can be installed forWindows
by following the instructions here. '- I made some minor modifications to the code from here to build this.
3. index.html
- A static
HTML
file with someJavaScript
embedded. - Creates a basic form for the user to type commands, also opens up a
WebSocket
which points tolocalhost:27016
. Whatever the user types is sent to theWebSocket
and logged inred
, whatever is recieved from theWebSocket
is logged inblue
. - The
index.html
file is mostly taken from Miguel Grinberg's example, which he built to demonstrate how to useflask
'sWebSocket
functionality (flask-sock
).
Then, we need to do the following steps to setup the proof of concept:
1. Run the TCP
echo server and launch a front-end server to serve the static web app (index.html
).
- Open up a terminal and run one of the echo servers (either the Linux or the Windows one - compilation and running instructions defined above).
- Open up another terminal and launch a front-end server to serve the
index.html
file. You can do this with any basic web server (I have run this proof of concept with the off the shelfreact-create-app
code / dev-server for instance).websockify
also includes functionality to serve static files (in addition to being a proxy), so I will use that functionality below rather than spinning up a separate server to provide the static content.
2. Download and configure websockify
.
- We are going to use the
node.js
version ofwebsockify
. I have only tested this onWindows
, though I would expect it should work anywherenode.js
runs. This blog from 2014 was very helpful in building this example. - Download the
zip
file by clicking this link and extract the files. - Move the
websockify-js-master
directory to be at the same level as thisREADME.md
. - Navigate to
\websockify-js-master\websockify
. - Run
npm install
to install the dependencies.
3. Run websockify
- Run
node websockify.js --web ../../ localhost:27016 localhost:5000
in the\websockify-js-master\websockify
directory. - The
--web [DIR]
command runs a mini web server on the same port where the proxy is running (../../
is whereindex.html
is located). - The
localhost:27016 localhost:5000
command instructswebsockify
to acceptWebSocket
connections on27016
and forward them to traditionalTCP
sockets on5000
.
4. Check the proof of concept
- Open a browser and type
http://localhost:27016/index.html
. - A basic website show appear with a
form
to type. Type any text and click enter. - The text should show up in
red
as>>> [text]
and then again inblue
as<<< [text]
. Thered
message is what was sent to theWebSocket
. Theblue
message is what was recieved from theWebSocket
. You should also see theTCP
servers are logging some items in the terminals which demonstrate that they are being pinged.