Preamble

I have long been involved in projects that involve various networking endeavors. Earlier I published here posts on how to make a telegram bot or create a web socket connection. I also implement standard protocols in Wolfram. I always try to do as much as possible in the Wolfram language itself and less with third-party technologies. This is because I always want to reduce the number of dependencies. While developing another server or protocol, I discovered that the built-in sockets that are implemented using the zeromq library have some problems.
The main problems were only two. First, it was just a monstrously slow data exchange. Secondly, there were constant crashes and errors when trying to send large amounts of data. When I say terrible speed, I’m not kidding. The built-in sockets that users have to use are so slow that I sometimes think it’s easier to send a TCP packet by pigeon!
From left to right, pigeons named Java, NodeJS, and Wolfram
I couldn’t put up with it anymore and so I decided to implement sockets interaction myself. In the following I will try to tell you what I got in the end.

Native example

But before we move on to look at my implementation we need some kind of example for comparison. First, I will create a server that runs on native Wolfram sockets (using ZeroMQ). And then I’ll do the same thing with performance measurements using my own implementation. Let’s get started!
First, we create a socket, which is a server:
In[]:=
server1=SocketOpen[8001]
Out[]=
SocketObject
Local IPAddress: 127.0.0.1
Local Port: 8001
Protocol: TCP (Server)
UUID: TCPSERVER-3ab19f6c-9682-4727-9f44-afdcad9d9944
Number of connected clients:
0

That’s great. Now let’s create a special object that will listen for incoming connections using the handler function:
In[]:=
listener1=SocketListen[server1,handler]
Out[]=
SocketListener
Endpoint: 127.0.0.1
Messages:
Missing[NotAvailable]

It’s all working. To test how fast the server is, let’s define the handler so that it accepts an HTTP request and responds with a fixed HTML page. First, let’s just create a picture:
In[]:=
f[n_,x_]:=Abs[((1/Pi)^(1/4)HermiteH[n,x])/(E^(x^2/2)Sqrt[2^nn!])]^2;
In[]:=
image=Plot[Evaluate@​​Append[Table[f[n,x]+n+1/2,{n,0,7}],x^2/2],{x,-4,4},Filling->Table[n->n-1/2,{n,1,8}],ImageSize->Large,PlotTheme->"Scientific"]
Out[]=
Export it to a string:
In[]:=
svg=ExportString[image,"SVG"];
And we’ll pre-compile the server’s response:
In[]:=
response="HTTP/1.1 200 OK\r\n\content-type: image/svg+xml\r\n\content-length: "<>ToString[StringLength[svg]]<>"\r\n\connection: keep-alive\r\n\\r\n"<>svg;
To keep the experiment clean, let’s see how many bytes we will send to the client:
In[]:=
UnitConvert[Quantity[N@ByteCount[response],"Bytes"],"Kilobytes"]
Out[]=
365.856
kB
And the last step - let’s define a handler function that will respond to absolutely any request with this image:
In[]:=
handler[assoc_Association]:=​​Module[{client,request,svg},​​client=assoc["SourceSocket"];​​request=ByteArrayToString[assoc["DataByteArray"]];​​​​Echo[request,"Request:"];​​WriteString[client,response];​​]
Now if I open my browser to http://127.0.0.1:8001, I see the following:
Our test image in the browser.
Another method we’ll use for measurements is a direct connection from the Wolfram Language. Here’s how to do it. First, we create a connection:
In[]:=
client1=SocketConnect[8001]
Out[]=
SocketObject
Foreign IPAddress: 127.0.0.1
Foreign Port: 8001
Protocol: TCP (Client)
UUID: 5991ddc8-2a86-44ce-8238-be23dff9a20f

And send the data to the server:
In[]:=
WriteString[client1,"hi"]
Checking to see if there’s anything to read:
In[]:=
While[!SocketReadyQ[client1],Pause[0.001]]
Create an empty array for the data:
In[]:=
data=ByteArray[{}]
Out[]=
ByteArray
0 bytes

The last step is to read the data that came in:
In[]:=
While[SocketReadyQ[client1],data=Join[data,SocketReadMessage[client1]]]
And let’s see what read:
In[]:=
data
Out[]=
ByteArray
365.8 kB

Let’s make sure it’s the same data:
It’s all working! That’s just great. Now let’s move on to the main part of the article and try to repeat the same thing using the package functions.

Installation

Finally we can move on to the main part of the article. Without further ado, let’s install the package:
Import package functions:
The package contains a total of 4 public functions:
◼
  • CSocketObject - socket representation
  • ◼
  • CSocketListener - listener object
  • ◼
  • CSocketOpen - opening server socket
  • ◼
  • CSocketConnect - connecting to local/remote server
  • These functions are analogs of the same functions, but only without the prefix C. In addition, the following operations are overridden in the package:
    ◼
  • Close[csocket] - closes the socket
  • ◼
  • BinaryWrite[csocket, byteArray] - writes binary data
  • ◼
  • WriteString[csocket, string] - writes string
  • ◼
  • SocketReadyQ[csocket] - checks if there is data to read
  • ◼
  • SocketReadMessage[csocket] - reads incoming data from the buffer
  • ◼
  • SocketListen[csocket, handler] - creates an object to listen to the socket
  • ◼
  • DeleteObject[clistener] - deletes the listener
  • As a result, thanks to the above functionality, we can repeat all the same things we did using native sockets.

    CSockets example

    Open the neighboring port for listening:
    Create a listener object using the same handler:
    Create a client-side connection:
    Writing the data:
    Checking that the data has arrived:
    Empty data:
    The last step is to read the data that came in:
    And let’s see what read:
    Let’s make sure it’s the same data:
    Everything works exactly the same. Now let’s compare performance!

    Performance

    Browser test

    The very first performance test in the browser - let’s just open the developer tools and see how fast the image loads:
    With native sockets: 2.69 seconds (first line in the table - column “Время”)
    With C sockets: 0.041 seconds (first line in the table - column “Время”)

    Program test

    Now we do the same thing as above, but with time measurement in Mathematica:
    And run the test:
    Time comparision:
    Fifty times!!!

    Problems

    It is currently one of the initial versions of my package, but there are people who are already actively using it. There are some problems with it. Now the package is under active development and you can help me if you write your wishes, try to use it or find bugs. I already know some bugs and I am busy fixing them. Some I don’t know about.
    One important issue is that part of the package is written in C and since I’m using Windows - I can’t test it on all architectures, Wolfram Lanuage versions and operating systems. Other Wolfram Language users could help me a lot with this.

    Development

    You can just fork the project on github. This is a completely standard package for the Wolfram language: https://github.com/KirillBelovTest/CSockets.
    To build library files for your operating system you can use the Scripts/BuidLibrary.wls file. Of course, you will need an installed compiler for the C language.

    Conclusion

    Thank you all for your attention! I hope that the tools I develop will benefit the Wolfram community. I am open to discussion, criticism, and suggestions.

    CITE THIS NOTEBOOK

    CSockets: Creating fast server for TCP/HTTP/custom protocols​
    by Kirill Belov​
    Wolfram Community, STAFF PICKS, August 28, 2023
    ​https://community.wolfram.com/groups/-/m/t/2999974