Send Binary (Image) Using a TCP Socket

Author: Carl Sassenrath
Return to REBOL Cookbook

Ok, so someone recently asked me, "what's a simple way to send an image between computers, in binary, using TCP sockets?" Since there was no simple example given, I quickly wrote this one up...

Sending binary data over a network is different than sending text. In text mode, you can wait for each line to be received, then process the line. But in binary, how do you know when you've received all the data? There is no "line terminator" nor can you use any other character to indicate the end (because they may appear in the binary data you want to send). So, what's the trick to sending binary? How do you know when all the data has been received?

One easy solution to this problem is to send the length of the data at the beginning of the transmission. That length can be used by the receiver to know when all the data has been received.

With that in mind you are ready to write the sender (also called the "client"). This code must read the binary file and send its data. But, it first inserts in front of the data a small REBOL text block that provides the file name and data length. (Often called a "header".) The text is null (zero byte) terminated to mark its end.

    REBOL [Title: "Sender"]

    ;-- Read the data:
    file: %image.jpg
    data: read/binary file

    ;-- Open the binary TCP socket:
    print "Opening to send..."
    server: open/binary/no-wait tcp://

    ;-- Send the data:
    print ["Sending" file "..."]
    insert data append remold [file length? data] #""
    insert server data

    ;-- Wait for the server to respond, then close.
    wait server
    close server
    ask "Done"

Notice the WAIT SERVER line. If you close the socket before all the data has been sent, the receiver could miss the end!

Now you can write the receiver code (also called the "server").

    REBOL [Title: "Receiver"]

    ;-- Start listening for connections:
    print "waiting for connection"
    port: open/binary/no-wait tcp://:8000
    wait port

    ;-- Wait for a client to connect:
    client: first port
    print "client connected, waiting for data"
    wait client

    ;-- Read what we've got so far and decode the header:
    data: copy client
    start: find data #""
    info: load to-string copy/part data start

    ;-- Use the length to read the rest of the data:
    print ["reading" info/1 "with" info/2 "bytes now"]
    remove/part data next start
    while [info/2 > length? data][
        append data copy client

    ;-- Save the data to the file that was provided:
    ;insert info/1 "x" ; <--uncomment this while you are testing
    print ["got" length? data "bytes, writing" info/1]
    write/binary info/1 data

    ;-- Tell client we are done and let client close TCP:
    insert client "done"
    wait client
    close client
    close port
    ask "Done"

Above, a portion of the data is received and converted to a REBOL block to discover the file name and data length. The rest of the data is then received and saved into the file. Note again that the client is the one that finally terminates the connection.

To try this example, change the file name in the sender to that of any image you have. Next, run the receiver code (you are starting a server). Leave that running. Now, run the sender and poof! The file is transferred.

Of course, if you want to do much else, there is more to learn about this whole client/server process. See the manual or search the web for REBOL server examples.

Hopefully, this example will get you started in the right direction.

2006 REBOL Technologies REBOL.com REBOL.net