Non blocking sockets in R?

Viewed 314

I'm trying to use sockets to communicate between R sessions and can't get them to behave as the help file suggests they should (?read.socket).

In one Rstudio session I run the following

sock_server <- make.socket(port = 22222, server = TRUE)
write.socket(sock_server, "Ahoy-hoy")

and then in another I run

sock <- make.socket(port = 22222, server = FALSE)
read.socket(sock, loop = FALSE)

and I do indeed see the friendly greeting.

However, if I rerun the read.socket line without sending anything else it hangs, only returning when further information is sent. The option loop = FALSE is supposed to control this behaviour and presumably return an empty string if nothing is waiting to be read, but it doesn't change anything.

I've tried this on MacOS and Windows and it is the same in both cases.

Any ideas how to get sockets to behave?


I've now tried it with various combinations of RStudio, RGui, and R from the command line and it behaves the same in all of them as far as I can see.

My ultimate goal is to have something in R which can handle a FIX connection from an external host. It basically works using these socket functions, apart from the above issue which is very difficult to work around.

I'm open to using whatever else people use for these things.


Here's a second attempt which works! But unfortunately only on Mac, Windows does something strange.

Using a binary connection with a different set of socket functions in base R, on the server side you do

sock_server <- socketConnection(port = 22222, server = TRUE, open = 'a+b')
fix_message <- gsub("\\|","\001","8=FIX.4.4|9=106|35=0|34=1234|49=abc|56=def|10=999|")
writeBin(charToRaw(fix_message), sock_server)

and on the client

sock <- socketConnection(port = 22222, server = FALSE, open = 'a+b')
rawToChar(readBin(sock, raw(), n = 32))

Note that the message I'm sending has no newlines in it (this is a feature of FIX messages).

On a Mac this works as expected - running the readBin line twice gives you the whole message which you can paste back together, however on Windows the second part of the message vanishes!

# Mac
> rawToChar(readBin(sock, raw(), n = 32))
[1] "8=FIX.4.4\0019=106\00135=0\00134=1234\00149="
> rawToChar(readBin(sock, raw(), n = 32))
[1] "abc\00156=def\00110=999\001"

# Windows
> rawToChar(readBin(sock, raw(), n = 32))
[1] "8=FIX.4.4\0019=106\00135=0\00134=1234\00149="
> rawToChar(readBin(sock, raw(), n = 32))
[1] ""

I'm using R 4.0.2 on both platforms. Anyone have any ideas how to do this on Windows?

2 Answers

The solution I ended up with was pretty much a combination of the two methods above.

The server side does

sock_server <- socketConnection(port = 22222, server = TRUE, open = 'a+b')
fix_message <- gsub("\\|","\001","8=FIX.4.4|9=106|35=0|34=1234|49=abc|56=def|10=999|")
writeBin(charToRaw(fix_message), sock_server)

and the client side does

sock <- socketConnection(port = 22222, server = FALSE, blocking = FALSE, open = 'a+b')
readLines(sock, warn = FALSE)
#> [1] "8=FIX.4.4\0019=106\00135=0\00134=1234\00149=abc\00156=def\00110=999\001"
readLines(sock, warn = FALSE)
#> character(0)

No lost messages, no blocking, and it works on both the Mac and Windows machines I've been using.

I also tried future package :

library("future")
plan(multiprocess)


mySocketRead<- function()
{
  sock <- make.socket(port = 22222, server = FALSE)
  msg <-read.socket(sock, loop = FALSE)
  #print(msg)
  print("read done ")
}


# normal non-blocking
res %<-% mySocketRead()

print("here we go")

But this example is also blocking while read, so it seems to be an general problem of the read.socket function.

I got 2 more ideas:

idea A: maybe you exchange your server and client with each other, because if the server is always blocking in an forever read loop it could be more acceptable, but this depends on your usecase.

idea B: use the socketConnection implementation and transform the string you want to send, to something less problematic string. I do think, your "lost string" from mac to windows appears, because you do use "4\0019" string literals, which could might be “interpreted” wrong by different socket implementation. In general its useful to maybe use the base64encode and base64decode functions maybe you should also add an checksum to ensure your transmission was succesful.

require(base64enc)

#send
fix_message <- gsub("\\|","001","8=FIX.4.4|9=106|35=0|34=1234|49=abc|56=def|10=999|")
fix_message_base64 <- base64encode(charToRaw(fix_message))
writeBin(charToRaw(fix_message_base64), sock_server)

#receive
fix_message_input_base64_raw <-readBin(sock, raw(), n = 32)
fix_message_input_raw <-base64decode(fix_message_base64)
fix_message_input <-rawToChar(fix_message_input_raw)

if this is not helful, you could might debug your network traffic with wireshark.

Old not working but is an example howto wait X seconds for function Y to finsish :

I would think use socket inside a function and wait until this function timeout. This will be also blocking, but not for ever.


require(R.utils)

## function that can take a long time
socketRead<- function()
{
    sock <- make.socket(port = 22222, server = FALSE)
    read.socket(sock, loop = FALSE)
    return("read")
}

# call read function
waitingTimeInSeconds=10
withTimeout(socketRead(), timeout = waitingTimeInSeconds, onTimeout = "error")

find the docu of the function withTimout here

Related