Sockets

From RFO-BASIC! Manual
Manual contents (Statement index)
Language features The basicsArraysData structuresInterrupt routinesUser-defined functions
Interfaces Audio playerBluetoothFilesGraphicsHTMLKeyboardSocketsSQLTelecom
Programming notes Coexisting with Android

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.

Web-based client

There are many servers on the web. The BASIC program Sample_Programs/f31_socket_time.bas, 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 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 12.34.56.78. 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 .com 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.

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 12.34.56.78. They see one another as internal IP addresses. For example, 192.168.0.0 (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 (12.34.56.78:8000), you might specify that your server is at internal IP address 192.168.0.7.

LAN connections

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.

The program Sample_Programs/f32_tcp_ip_sockets.bas, 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[edit]

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

SOCKET.CLIENT.CONNECT[edit]

Try to connect to a server

Synopsis
SOCKET.CLIENT.CONNECT <server_sexp>, <port_nexp> {, <wait_lexp>}
Description

The SOCKET.CLIENT.CONNECT statement is the first Socket statement the client calls. The <server_sexp> parameter is the specific server to which to connect. It can be any one of the following:

  • 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 <port_nexp> 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 <wait_lexp> 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 <wait_lexp> 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[edit]

Check the status of the socket

Synopsis
SOCKET.CLIENT.STATUS <status_nvar>
Description

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

Value Meaning
0 Idle — The program has made no attempt to establish a connection, or a successful connection has been dropped.
2 Connecting — The program has called SOCKET.CLIENT.CONNECT but the connection is not yet made.
3 Connected — The connection is open and the client can exchange information with the server, following whatever rules the two nodes have for the dialogue.

SOCKET.CLIENT.CLOSE[edit]

Close the connection

Synopsis
SOCKET.CLIENT.CLOSE
Description

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

Preparing the server[edit]

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[edit]

Create the connection

Synopsis
SOCKET.SERVER.CREATE <port_nexp>
Description

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

SOCKET.SERVER.CONNECT[edit]

Connect to a client

Synopsis
SOCKET.SERVER.CONNECT {<wait_lexp>}
Description

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.

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

If <wait_lexp> 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 <wait_lexp> 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[edit]

Check the status of the socket

Synopsis
SOCKET.SERVER.STATUS <status_nvar>
Description

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:

Value Meaning
-1 Uncreated — The program has not yet called SOCKET.SERVER.CREATE.
0 Idle — The program has made no attempt to establish a connection, or a successful connection has been dropped.
1 Listening — The program has called SOCKET.SERVER.CONNECT but no client has yet tried to make a connection with it.
3 Connected — The connection is open and the server can exchange information with the client, following whatever rules the two nodes have for the dialogue.

SOCKET.SERVER.DISCONNECT[edit]

Disconnect from the current client

Synopsis
SOCKET.SERVER.DISCONNECT
Description

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.

SOCKET.SERVER.CLOSE[edit]

Close the connection to the client

Synopsis
SOCKET.SERVER.CLOSE
Description

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

Obtaining IP addresses[edit]

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[edit]

Get the IP of the server

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

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.

SOCKET.SERVER.CLIENT.IP[edit]

Get the IP of the client

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

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.

SOCKET.MY.IP[edit]

Get the program's own IP

Synopsis
SOCKET.MYIP <ip_svar>
Description

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

Example

The BASIC program Sample_Programs/f32_tcp_ip_sockets.bas, 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.

Multiple IPs[edit]

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.

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

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>.

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.

Example

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

SOCKET.MYIP MyIPs$[], count

Exchanging text lines[edit]

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[edit]

Write a line from the client to the server

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

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.

SOCKET.SERVER.READ.READY[edit]

See if a line has arrived from the client

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

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.

SOCKET.SERVER.READ.LINE[edit]

Read a line from the client

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

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.

SOCKET.SERVER.WRITE.LINE[edit]

Write a line from the server to the client

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

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.

SOCKET.CLIENT.READ.READY[edit]

See if a line has arrived from the server

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

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.

SOCKET.CLIENT.READ.LINE[edit]

Read a line from the server

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

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.

Exchanging bytes[edit]

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[edit]

Write bytes from the client to the server

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

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

SOCKET.SERVER.READ.BYTES[edit]

Read bytes from the client to the server

Synopsis
SOCKET.SERVER.READ.BYTES <bytes_svar>

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[edit]

Write bytes from the server to the client

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

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

SOCKET.CLIENT.READ.BYTES[edit]

Read bytes from the server to the client

Synopsis
SOCKET.CLIENT.READ.BYTES <bytes_svar>

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[edit]

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 0xFFFF 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 0xFFFF as an indication that the transfer was complete.

SOCKET.CLIENT.WRITE.FILE[edit]

Write an entire file to the server

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

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.

Example

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

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

SOCKET.SERVER.READ.FILE[edit]

Read an entire file from the client

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

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.

Example

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

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

SOCKET.SERVER.WRITE.FILE[edit]

Write an entire file to the client

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

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.

SOCKET.CLIENT.READ.FILE[edit]

Read an entire file from the server

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

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.

Sources[edit]

  • De Re BASIC! (v1.91), pages 120-125.