mirror of
https://github.com/DualBrain/QB64.git
synced 2023-11-19 13:10:13 +00:00
204 lines
8.7 KiB
Markdown
204 lines
8.7 KiB
Markdown
[Home](https://qb64.com) • [Forums](https://qb64.boards.net/) • [News](news.md) • [GitHub](https://github.com/QB64Official/qb64) • [Wiki](wiki.md) • [Samples](samples.md) • [InForm](inform.md) • [GX](gx.md) • [QBjs](qbjs.md) • [Community](community.md) • [More...](more.md)
|
|
|
|
## Articles - TCP/IP communications
|
|
|
|
*by Luke*
|
|
|
|
```text
|
|
|
|
QB64 - TCP/IP Communications
|
|
|
|
Conceptual overview
|
|
-------------------
|
|
|
|
TCP is a protocol for communicating over a network. It guarantees that:
|
|
|
|
- Data will arrive
|
|
- It will arrive in order
|
|
- It will arrive error-free
|
|
|
|
Obviously, if someone cuts the cable, nothing can be done.
|
|
|
|
A TCP connection is established between two parties, a client and server. When
|
|
started, the server will listen on a specific port. By listening on different
|
|
ports, multiple servers can be run on the same machine. When a client wishes to
|
|
connect, it requests a connection to the server, specifying its IP address (or a
|
|
text equivalent like www.google.com) and the port the server is on. The server
|
|
then accepts the connection request, and the connection is established.
|
|
|
|
Aside on ports: ports are numbers between 1 and 65535, and their use allows
|
|
multiple servers to run on the same machine. The client needs to know the port
|
|
the server is using beforehand - luckily, common services have standard ports:
|
|
HTTP (for internet pages): 80
|
|
SSH (secure remote access): 22
|
|
Telnet (insecure remote access): 23
|
|
SMTP (Send email): 25
|
|
On some systems, ports below 1024 may be protected, meaning a server can't
|
|
listen on them without administrator/superuser privliges.
|
|
|
|
TCP structures the data sent as a stream. This has many consequences:
|
|
- Individual send operations do not mean data will arrive as separate chunks at
|
|
the other end. You cannot tell where one chunk ends and the next starts.
|
|
- But one send operation does not guarantee only one read operation will be
|
|
needed. TCP may split the data, and it may arrive bit by bit, meaning more
|
|
than one read operation is needed to collect it all.
|
|
Although this may sound limiting, the associated issues can be solved by either
|
|
sending a special signal to note the end of the data (NUL and Linefeed are popular
|
|
choices), or by first sending the size of the data as a pre-agreed numeric
|
|
data type, then sending the data itself.
|
|
|
|
TCP is full-duplex, meaning both parties can send and receive data. It is not
|
|
necessary to read all data before you can send any yourself.
|
|
|
|
-------------------------------------------------------
|
|
|
|
_OPENCLIENT - Connect to a listening server
|
|
-----------
|
|
handle& = _OPENCLIENT("TCP/IP:1234:host")
|
|
Where 1234 can be any port number, and host can be any IPv4 address or any domain
|
|
name, upon which DNS resolution will be performed.
|
|
|
|
If you are connecting to a server, this is typically the first command used. It
|
|
establishes the connection, and returns a handle to refer to the connection by
|
|
later on. If handle& = 0, this indicates an error occurred connecting.
|
|
|
|
|
|
_OPENHOST - Begin listening on a specific port
|
|
---------
|
|
server& = _OPENHOST("TCP/IP:1234")
|
|
Where 1234 can be any port number, but may be subject to Operating System
|
|
permission restrictions.
|
|
|
|
This is typically the first command a server will call, in the startup phase.
|
|
The program is then ready to accept connections to port 1234, by use of
|
|
_OPENCONNECTION.
|
|
|
|
server& is a handle that refers to listening on a particular port, and does not
|
|
relate to any particular client. If server& = 0, an error occured.
|
|
|
|
|
|
_OPENCONNECTION - Accept incoming connection from a client
|
|
handle& = _OPENCONNECTION(server&)
|
|
Where server& is a handle returned by _OPENHOST.
|
|
|
|
Checks for any connection requests from clients, and accepts the next waiting
|
|
one, if any. If a new connection is established, handle& is non-zero, and is
|
|
a handle& used to refer to that particular connection. That is, each client
|
|
connection will have a different handle&.
|
|
|
|
If server& = 0 there are no clients attempting to connect, or establishing a
|
|
connection failed.
|
|
|
|
If clients attempt to connect faster than a server can accept their connections,
|
|
they will be queued. The size of the queue is limited by the Operating System -
|
|
if it is exceeded, clients will likely be refused a connection straight away.
|
|
|
|
In typical operation, a server will repeatedly call this function to check for
|
|
any new clients.
|
|
|
|
|
|
_CONNECTIONADDRESS$ - Get address information about a connection
|
|
-------------------
|
|
info$ = _CONNECTIONADDRESS$(h&)
|
|
Where h& is a handle returned by any of _OPENCLIENT, _OPENHOST, _OPENCONNECTION.
|
|
|
|
In all three cases, the string returned as the format "TCP/IP:1234:address",
|
|
where 1234 is any port number and address is a IPv4 address or textual domain
|
|
name.
|
|
|
|
_OPENCLIENT handles: Returns the same string passed to _OPENCLIENT
|
|
_OPENHOST handles: Returns the listening port, and the public IP address of the
|
|
machine. _CONNECTIONADDRESS$ knows about NAT, so if a machine is behind a router
|
|
performing NAT, the address returned is the public address of the router, not
|
|
the local address of the machine.
|
|
_OPENCONNECTION handles: Returns the port from which the client is connecting,
|
|
and their IP address. In many cases, a client will be connecting from a randomly
|
|
assigned port number, usually towards the upper end of the range.
|
|
|
|
_CONNECTED - Check if a connection is still active
|
|
----------
|
|
bool& = CONNECTED(h&)
|
|
Where h& is a handle returned by _OPENCLIENT, _OPENHOST, _OPENCONNECTION, and
|
|
bool& is a boolean value (-1 = true, 0 = false).
|
|
|
|
Returns the state of the connection, as far as the program can tell. _CONNECTED
|
|
will only notice a disconnection when a read or write (GET or PUT) operation
|
|
does not succeed due to the connection being closed or otherwise broken. Thus:
|
|
- Simply calling _CONNECTED in a loop without any I/O is useless.
|
|
- Even if the connection is closed, GET may still succeed if there is data
|
|
in the incoming buffer. In this case, _CONNECTED will still return true.
|
|
|
|
Since GET and PUT do not raise any errors, or return a value indicating
|
|
success/failure, _CONNECTED can be called after them to get an idea of what
|
|
happened.
|
|
|
|
If h& has already been closed with CLOSE, an error is raised. A connection that
|
|
is found to be disconnected with _CONNECTED must still be closed with CLOSE.
|
|
|
|
Handles returned by _OPENHOST are always considered connected, since they are
|
|
not true connections.
|
|
|
|
|
|
CLOSE - Close a connection
|
|
-----
|
|
CLOSE #h& 'The # is optional
|
|
|
|
Like with a file, this is how one shuts down a connection or listen. h& may be
|
|
a handle returned by _OPENCLIENT, _OPENHOST, _OPENCONNECTION. Depending on the
|
|
application, it may be useful to arrange for one end to send a 'acknowledge'
|
|
signal, to confirm the connection is ready to close.
|
|
|
|
Especially in longrunning server programs, closing used connections is important
|
|
for preventing memory leaks.
|
|
|
|
|
|
GET - Read data from a connection
|
|
---
|
|
GET #h&, , fixed_len
|
|
GET #h&, , text$
|
|
Where h& is a handle returned by _OPENCLIENT, _OPENCONNECTION (not _OPENHOST),
|
|
and fixed_len/text$ is the variable to hold the data.
|
|
|
|
Receives data from the connection, if any is present. The behaviour differs
|
|
depending on whether the receiving variable is fixed length (numbers, UDT's
|
|
including _MEM, arrays, fixed-length strings) or a variable length string.
|
|
|
|
If the data type is fixed, GET checks if there is sufficient data available to
|
|
fill it. If there is, the appropriate number of bytes are read from the stream,
|
|
and EOF(h) is set to 0. If insufficient (or no) data is available, no bytes are
|
|
read from the stream, and EOF(h) is set to -1. The value of the receiving
|
|
variable may or may not be changed, so don't count on either.
|
|
|
|
If the data type is a variable length string, GET will read all available bytes
|
|
from the stream and return them in the string. EOF(h) is always set to 0, even
|
|
if no bytes are read (an empty string is returned in that case). However, you
|
|
should be prepared to accept fragmented or joined-together data, due to the
|
|
nature of TCP.
|
|
|
|
In all cases, GET is non-blocking, meaning it does not wait for data to arrive.
|
|
(Some programming languages have blocking network functions, which will not
|
|
return until data arrives). Consequentially, the following code is common to
|
|
read a piece of data:
|
|
|
|
DO
|
|
GET #h, , num&&
|
|
LOOP WHILE EOF(h&)
|
|
|
|
EOF(h) will return -1 (true) if the GET cannot retrieve a sufficient number of
|
|
bytes (in this case, 8) from the stream, and 0 (false) when it succeeds.
|
|
|
|
|
|
PUT - Send data on a connection
|
|
---
|
|
PUT #h&, , var
|
|
Where h& is a handle returned by _OPENCLIENT, _OPENCONNECTION (not _OPENHOST),
|
|
and var is the variable to send (a numeric type, string, array of fixed length
|
|
data, or UDT including _MEM).
|
|
|
|
The counterpart to GET, PUT sends data to the other party. Usage is straght
|
|
forward, although note that:
|
|
- You cannot send an array of variable strings.
|
|
- Sending a _MEM block sends the _MEM header itself, not the data it refers to.
|
|
|
|
```
|