Sockets

Sockets enable exchanges of information between exactly one client and exactly one server at a time. RFO-BASIC! has a separate set of statements for clients and servers. At any time, the BASIC program either plays the role of the client or of the server. The server waits for a connection from any client, usually indefinitely; the client initiates a connection to a specific server, which it identifies by hostname or Internet Protocol (IP) address. Once the connection is made, the client and server can exchange information. The client and server must be coordinated to know who initiates each transmission and what the information means.

Transmissions can be of text lines, of arbitrary 8-bit bytes, and of entire files. The three communication modes involve separate groups of statements. Mixing them risks losing data and having the receiver fail to recognize that a transmission is complete.

The medium of communication can be:
 * A local area network (LAN) using Ethernet or WiFi. (Some wireless LANs do not support client-server traffic.)
 * The Internet.

The medium uses the Transmission Control Protocol and the Internet Protocol (TCP/IP). This is invisible to the BASIC program except that the client may use an IP address to select a server.

There are many servers on the web. The BASIC program, included in the BASIC installation, is a client designed to connect to one of the United States time servers and get the current time of day. These time servers publish their own command-and-reply sequence, and the sample program incorporates it. However, it is also an instructive example of a client written in BASIC.
 * Web-based client


 * Web-based server

Be sure your Internet service features a static IP address. If you have a dynamic IP address, where your provider can assign you a different IP address every time you reconnect or repair the connection, you cannot publish your current IP address as a reliable way for clients to reach your server.  You can write a server in BASIC that is available to clients anywhere in the world. However, clients must know how to address your server in order to open a connection to it. One way is to use your server's IP address, a number in the form. You can obtain your IP address by surfing to the following web page: https://canihazip.com/s

A server can capture this information into a string variable by executing: GRABURL ip$,"https://canihazip.com/s"

The server would still have to communicate this address to potential clients somehow, before they could open a connection to it.

You can obtain a website name (on the  or other domain) for a fee, which covers the cost of maintaining name servers around the world that clients can use to translate your website name into your IP address so they can connect.

Internal IP addresses can also change. If you switched your  off, and then back on later, it might come back as. This depends on the time interval for which addresses are "leased," which is a setting in your router.  Several devices can be connected to the Internet through your router. The rest of the world sees them all as a single IP address, say. They see one another as internal IP addresses. For example,  (where the last two numbers can be anything) is unused on the Internet but reserved for local devices. A BASIC program can use the SOCKET.MYIP statement to find its own internal IP address. Use port tunneling or port mapping to inform your router, when an external client looks for a server at a certain port, which of your devices to connect to. For example, if a client on the web tries to connect to a streaming music server at port 8000, you might specify that your server is at internal IP address.

A common way to use Sockets is for two Android devices to communicate, not using the Internet but using your own router. The client can select a server using its internal IP address. A BASIC program can use the SOCKET.MYIP statement to find its own internal IP address. If a device has a hostname, a local client can use the hostname to connect to the server.
 * LAN connections

The program, included in the BASIC installation, demonstrates the socket commands. This program can play the role of the client or the server. You need two Android devices to run this program. After you give the client the information it needs to select the server, all that happens is that the client sends one specific message to the server; the server sends one specific response, and then drops the connection. It will be evident how you can enhance the server to interpret the arrived message, do useful work, and issue an informative reply.

Preparing the client
The client is the partner that tries to connect to a specific server.

SOCKET.CLIENT.CONNECT
Try to connect to a server

SOCKET.CLIENT.CONNECT ,  {, }
 * Synopsis

The SOCKET.CLIENT.CONNECT statement is the first Socket statement the client calls. The  parameter is the specific server to which to connect. It can be any one of the following:
 * Description
 * A URL of a website on the Internet (such as "canihazip.com")
 * A numeric IP address of a device on the Internet (such as "12.34.56.78")
 * A numeric IP address of a device on your LAN (such as "192.168.0.7")
 * A hostname of a device on your LAN (such as "///homecontrol").

The  parameter is the port number to use. Ports are numbers assigned to specific uses of Sockets, and even to specific applications and games, by the Internet Assigned Numbers Authority. (See List of TCP and UDP port numbers.) BASIC does not enforce any particular use of any port; BASIC's only involvement with the port number is that the client's choice must match the server's.

If  is nonzero or omitted, SOCKET.CLIENT.CONNECT does not return until the connection is made or there is a run-time error. The program would use this mode only if it had no other work than to wait for a connection. It is a run-time error if:
 * The specified network address does not respond at all.
 * The user presses the BACK key, which can be trapped with ONBACKKEY:.
 * Several minutes elapse without success at making a connection. (This error is not guaranteed to occur.)

If  is present and has the value 0, SOCKET.CLIENT.CONNECT returns immediately even if a connection is not yet made. The program would use this mode if it had other things to do, or conditions to check, while waiting for the connection. The program must use SOCKET.CLIENT.STATUS periodically to determine if a connection is made, and must not read or write until a connection is made.

SOCKET.CLIENT.STATUS
Check the status of the socket

SOCKET.CLIENT.STATUS 
 * Synopsis

The SOCKET.CLIENT.STATUS statement puts a numeric value in  to indicate the status of the socket. It is one of the following:
 * Description

SOCKET.CLIENT.CLOSE
Close the connection

SOCKET.CLIENT.CLOSE
 * Synopsis

The SOCKET.CLIENT.CLOSE statement closes an open connection and returns the client-side Socket unit to the Idle state.
 * Description

Preparing the server
The server is the partner that waits for a client to make a connection.

Several clients can try to connect to a server. RFO-BASIC! maintains a queue of these clients. When a server finishes servicing one client and drops the connection, calling SOCKET.SERVER.CONNECT connects to the next client in the queue.

SOCKET.SERVER.CREATE
Create the connection

SOCKET.SERVER.CREATE 
 * Synopsis

The SOCKET.SERVER.CREATE statement obtains the resources to enable server-side communication. It also declares that the server will accept connections only on . Success changes the connection status from Uncreated to Idle.
 * Description

SOCKET.SERVER.CONNECT
Connect to a client

SOCKET.SERVER.CONNECT {}
 * Synopsis

The SOCKET.SERVER.CONNECT statement begins to wait for a client to connect to the server. The client must know the hostname or IP address of the server, and specify in its SOCKET.CLIENT.CONNECT statement the same port number that the server published using SOCKET.SERVER.CREATE.
 * Description

The statement changes the connection status from Idle to Listening. When a client connects, the status changes to Connected.

If  is nonzero or omitted, SOCKET.SERVER.CONNECT does not return until the connection is made or there is a run-time error. The program would use this mode only if it had no other work than to wait for a connection.

If  is present and has the value 0, SOCKET.SERVER.CONNECT returns immediately even if a connection is not yet made. The program would use this mode if it had other things to do, or conditions to check, while waiting for the connection. The program must use SOCKET.SERVER.STATUS periodically to determine if a connection is made, and must not read or write until a connection is made.

SOCKET.SERVER.STATUS
Check the status of the socket

SOCKET.SERVER.STATUS <status_nvar>
 * Synopsis

The SOCKET.SERVER.STATUS statement puts a numeric value in <status_nvar> to indicate the status of the socket. It is one of the following:
 * Description

SOCKET.SERVER.DISCONNECT
Disconnect from the current client

SOCKET.SERVER.DISCONNECT
 * Synopsis

The SOCKET.SERVER.DISCONNECT statement closes the connection to the client. The status of the connection changes from Connected back to Idle. The server can call SOCKET.SERVER.CONNECT to listen for another client; if there is another client trying to connect, SOCKET.SERVER.CONNECT accepts this connection.
 * Description

SOCKET.SERVER.CLOSE
Close the connection to the client

SOCKET.SERVER.CLOSE
 * Synopsis

The SOCKET.SERVER.CLOSE statement disconnects from any current client, closes the server, and dismisses its resources.
 * Description

Obtaining IP addresses
Clients and servers can use the respective statements in this section to obtain their own IP address or that of their partner in the connection. Such an IP address is a device's address on the Internet; except that, if the device's only access to the Internet is over a Local Access Network (LAN), then the IP address returned is the internal IP address that is private to the LAN, not the IP address by which the entire LAN is known to the rest of the Internet.

SOCKET.CLIENT.SERVER.IP
Get the IP of the server

SOCKET.CLIENT.SERVER.IP <ip_svar>
 * Synopsis

The client uses SOCKET.CLIENT.SERVER.IP to get, in <ip_svar>, the IP address of the server to which it is currently connected. The utility of this statement is limited, as the client had to know it to connect to the server in the first place.
 * Description

SOCKET.SERVER.CLIENT.IP
Get the IP of the client

SOCKET.SERVER.CLIENT.IP <ip_svar>
 * Synopsis

The server uses SOCKET.SERVER.CLIENT.IP to get, in <ip_svar>, the IP address of the client to which it is currently connected. A server may service many different clients and, using this statement, could have different rules when it is contacted by specific devices.
 * Description

SOCKET.MY.IP
Get the program's own IP

SOCKET.MYIP <ip_svar>
 * Synopsis

Either the client or the server uses SOCKET.MYIP to get, in <ip_svar>, its own IP address.
 * Description

The BASIC program, included in the BASIC installation, calls SOCKET.MYIP to get its own IP address when operated as a client. It displays this on the screen so that the operator of the server can type the address to specify the client.
 * Example

Multiple IPs
Usually, an Android device has only one IP. In unusual cases, the device may have multiple IPs. For example, after opening a WiFi connection, the device might still have a cellphone connection open. Alternative syntax for SOCKET.MYIP lets a BASIC program capture into an array all the IPs that pertain to it.

SOCKET.MYIP <ips_sarr>{, <number_nvar>}
 * Synopsis

This form of SOCKET.MYIP captures, into <ips_sarr>, all the IPs that pertain to the caller's device. If the caller provides a numeric variable <number_nvar>, SOCKET.MYIP sets it to the number of IPs it returned in <ips_sarr>.
 * Description

If the caller's device has no active IPs, then SOCKET.MYIP creates a string array with only one element, sets it to the empty string, and if the caller provides a <number_nvar>, sets it to 0. In all other cases, <number_nvar> is identical to the length of the array that SOCKET.MYIP creates.

This statement obtains the active IPs and how many there are:
 * Example

SOCKET.MYIP MyIPs$[], count

Exchanging text lines
Clients and servers can exchange lines of text, from a string expression at one end into a string variable at the other end. Text lines can contain arbitrary UTF-16 (two-byte) characters. Each text line ends with the linefeed character, but the sender does not provide it and it is not given to the receiver. The Socket facility assembles the line, and the receiving BASIC program need not participate in the process, but only test periodically whether the line has completely arrived.

The client can intermix the statements in this section with other modes of communication, but a partner only detects that a line is ready when a newline character arrives.

SOCKET.CLIENT.WRITE.LINE
Write a line from the client to the server

SOCKET.CLIENT.WRITE.LINE <line_sexp>
 * Synopsis

The client calls SOCKET.CLIENT.WRITE.LINE to send <line_sexp> to the server as UTF-16 characters, which will make a read operation at the server report completion. The Socket facility appends a newline character as the terminator. If <line_sexp> contains additional newline characters, the server will receive it as more than one line.
 * Description

SOCKET.SERVER.READ.READY
See if a line has arrived from the client

SOCKET.SERVER.READ.READY <ready_lvar>
 * Synopsis

The server calls SOCKET.SERVER.READ.READY to sense whether a complete line has arrived from the client, which means that a newline character has arrived. If <ready_lvar> is FALSE (0), then a line is not ready for reading. If <ready_lvar> is TRUE (nonzero), then a line is ready.
 * Description

SOCKET.SERVER.READ.LINE
Read a line from the client

SOCKET.SERVER.READ.LINE <line_svar>
 * Synopsis

The server calls SOCKET.SERVER.READ.LINE to receive, into <line_svar>, a line from the client. If a complete line is not ready, SOCKET.SERVER.READ.LINE suspends the program until it is. If the program needs to perform other actions and ought not be suspended, it should loop and periodically call SOCKET.SERVER.READ.READY to sense whether a line is ready. If so, calling SOCKET.SERVER.READ.LINE will not suspend the program.
 * Description

SOCKET.SERVER.WRITE.LINE
Write a line from the server to the client

SOCKET.SERVER.WRITE.LINE <line_sexp>
 * Synopsis

The server calls SOCKET.CLIENT.WRITE.LINE to send <line_sexp> to the client as UTF-16 characters, which will make a read operation at the client report completion. The Socket facility appends a newline character as the terminator. If <line_sexp> contains additional newline characters, the client will receive it as more than one line.
 * Description

SOCKET.CLIENT.READ.READY
See if a line has arrived from the server

SOCKET.CLIENT.READ.READY <ready_lvar>
 * Synopsis

The client calls SOCKET.SERVER.READ.READY to sense whether a complete line has arrived from the server, which means that a newline character has arrived. If <ready_lvar> is FALSE (0), then a line is not ready for reading. If <ready_lvar> is TRUE (nonzero), then a line is ready.
 * Description

SOCKET.CLIENT.READ.LINE
Read a line from the server

SOCKET.CLIENT.READ.LINE <line_svar>
 * Synopsis

The client calls SOCKET.CLIENT.READ.LINE to receive, into <line_svar>, a line from the server. If a complete line is not ready, SOCKET.CLIENT.READ.LINE suspends the program until it is. If the program needs to perform other actions and ought not be suspended, it should loop and periodically call SOCKET.CLIENT.READ.READY to sense whether a line is ready. If so, calling SOCKET.CLIENT.READ.LINE will not suspend the program.
 * Description

Exchanging bytes
Clients and servers can exchange 8-bit bytes with the statements in this section. The sender assembles the bytes to send using a BASIC string variable. The bytes can be 8-bit text or arbitrary binary data. However, if the BASIC string contains any character with nonzero high-order bytes, that information is not transmitted.

Exchanges of bytes do not provide encoding, and do not automatically send a newline character.

If a sender uses a .WRITE.BYTES statement and explicitly ends the byte string with a newline character, the receiver can read it with a .READ.LINE statement.

SOCKET.CLIENT.WRITE.BYTES
Write bytes from the client to the server

SOCKET.CLIENT.WRITE.BYTES <bytes_sexp>
 * Synopsis

The SOCKET.CLIENT.WRITE.BYTES statement sends <bytes_sexp> to the server as 8-bit bytes.
 * Description

SOCKET.SERVER.READ.BYTES
Read bytes from the client to the server

SOCKET.SERVER.READ.BYTES <bytes_svar>
 * Synopsis

The server calls SOCKET.SERVER.READ.BYTES to deposit such bytes as have been received since the last call into <bytes_svar>. If no bytes have been received since the last call, SOCKET.SERVER.READ.BYTES sets <bytes_svar> to the empty string.

SOCKET.SERVER.WRITE.BYTES
Write bytes from the server to the client

SOCKET.SERVER.WRITE.BYTES <bytes_sexp>
 * Synopsis

The SOCKET.SERVER.WRITE.BYTES statement sends <bytes_sexp> to the client as 8-bit bytes.
 * Description

SOCKET.CLIENT.READ.BYTES
Read bytes from the server to the client

SOCKET.CLIENT.READ.BYTES <bytes_svar>
 * Synopsis

The server calls SOCKET.CLIENT.READ.BYTES to deposit such bytes as have been received since the last call into <bytes_svar>. If no bytes have been received since the last call, SOCKET.CLIENT.READ.BYTES sets <bytes_svar> to the empty string.

Exchanging files
Bug. In version 1.91, following a file exchange, subsequent SOCKET. statements might not work correctly. See Github 237. Example code is at Forum 5098. The bug is fixed in OLI-BASIC. </DIV> Clients and servers can exchange entire files using the statements in this section. Clients and servers must coordinate and both be ready for a file transfer. If the sender sends a file but the receiver prepares to receive text lines or bytes, then unspecified failures may occur.

File contents are exchanged without context. There is nothing requiring that the receiver store the information with the filename by which the sender knows it, although if sender and receiver coordinate, they can also transmit the filename. There is also nothing about the exchange of a file that tells the receiver its file type or how to interpret its contents.

When sender and receiver are exchanging a file, BASIC uses the character  to mark the end of the file. The sender does not have to add this character and the receiver does not see it. If the sender used SOCKET.*.WRITE FILE but the receiver used SOCKET.*.READ.LINE (despite much slower speed), the receiver could note receipt of  as an indication that the transfer was complete.

SOCKET.CLIENT.WRITE.FILE
Write an entire file to the server

SOCKET.CLIENT.WRITE.FILE <file_nexp>
 * Synopsis

The SOCKET.CLIENT.WRITE.FILE statement writes an entire file to the server. The <file_nexp> parameter is the file index of a file opened for read by BYTE.OPEN.
 * Description

The following code opens an image file and sends the entire file to the server:
 * Example

BYTE.OPEN R, File1, "image.jpg" SOCKET.CLIENT.WRITE.FILE File1 BYTE.CLOSE File1

SOCKET.SERVER.READ.FILE
Read an entire file from the client

SOCKET.SERVER.READ.FILE <file_nexp>
 * Synopsis

The SOCKET.SERVER.READ.FILE statement reads an entire file from the client and stores it in a local file. The <file_nexp> parameter is the file index of a file opened for write by BYTE.OPEN.
 * Description

The following code reads a file from the client and stores it locally as an image file:
 * Example

BYTE.OPEN W, File2, "seascape.jpg" SOCKET.SERVER.READ.FILE File2 BYTE.CLOSE File2

SOCKET.SERVER.WRITE.FILE
Write an entire file to the client

SOCKET.SERVER.WRITE.FILE <file_nexp>
 * Synopsis

The SOCKET.SERVER.WRITE.FILE statement writes an entire file to the client. The <file_nexp> parameter is the file index of a file opened for read by BYTE.OPEN.
 * Description

SOCKET.CLIENT.READ.FILE
Read an entire file from the server

SOCKET.CLIENT.READ.FILE <file_nexp>
 * Synopsis

The SOCKET.CLIENT.READ.FILE statement reads an entire file from the server and stores it in a local file. The <file_nexp> parameter is the file index of a file opened for write by BYTE.OPEN.
 * Description