🙬 ESP32forth Sockets 🙮
🙚 🙘
August 13, 2022
🙬 Berkeley Sockets 🙮
• THE Internet Networking API
• Released in 1983, free of AT&T in 1989
• File descriptors as network endpoints
• Multi-protocol and Multi-transport
• Designed to support concurrency
🙬 Multi-Transport 🙮
AF_INET - IPv4
AF_INET6 - IPv6
AF_UNIX - domain socket
🙬 IPv4 & IPv6 🙮
IPv4 192.168.0.1
IPv6 2001:0db8:85a3:0000:0000:8a2e:0370:7334
2001:db8:85a3:::8a2e:370:7334
🙬 Multi-Protocol 🙮
SOCK_STREAM - TCP
SOCK_DGRAM - UDP
🙬 TCP 🙮
• Transmission Control Protocol
• Bi-directional byte stream
• Connection oriented
• Multiple inbound connections
• Reliable delivery
• Auto-adapts to network conditions
🙬 TCP Use-cases 🙮
• HTTP / HTTPS
• SSH
• Telnet
• FTP
🙬 UDP 🙮
• User Datagram Protocol
• Variable length packets
• Up to 65,527 bytes
• About ~500 bytes practical
• Connectionless
• Unreliabled, packets can be:
• lost
• corrupt
• repeated
• out of order
🙬 UDP Use-cases 🙮
• DNS
• DHCP
• Video/Audio streaming
🙬 struct sockaddr_in 🙮
[ ][ ][ ][ ]
------------------------
[ family ][ port ]
------------------------
[ address ]
------------------------
[ unused ]
------------------------
[ unused ]
------------------------
🙬 struct sockaddr_in 🙮
[ ][ ][ ][ ]
------------------------
[ 16 ][fam ][ port ]
------------------------
[ address ]
------------------------
[ unused ]
------------------------
[ unused ]
------------------------
struct sockaddr_in {
short sin_family; // e.g. AF_INET
unsigned short sin_port; // e.g. htons(1234)
struct in_addr sin_addr; // IP address big-endian
char sin_zero[8]; // unused
};
struct sockaddr_in {
char sin_len; // sizeof(sockaddr_in)
char sin_family; // e.g. AF_INET
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // IP address big-endian
char sin_zero[8]; // unused
};
🙬 struct sockaddr_in 🙮
[ ][ ][ ][ ][ ][ ][ ][ ]
------------------------------------------------
[ family ][ port ][ flow info ]
------------------------------------------------
[ address ]
------------------------------------------------
[ scope id ]
------------------------
🙬 Big-Endian 🙮
ntohs htons
ntohl htonl
sockaddr addr ( AF_INET pre-set )
->addr@ ( a -- ip )
->addr! ( ip a -- )
->port@ ( a -- port )
->port! ( port a -- )
🙬 Making a TCP Connection 🙮
gethostbyname(hostname)
socket(domain, type, protocol)
connect(sock, addr, addrlen)
send(sock, data, datalen, flags)
recv(sock, data, datalen, flags)
close(sock)
🙬 Making a TCP Connection 🙮
gethostbyname - lookup DNS
socket - make a socket
connect - connect to an address
send - send data
recv - receive data
close - close connection
🙬 Looking up a Host 🙮
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */
( Lookup a Host )
z" google.com" gethostbyname constant google.com
google.com ->h_addr ip.
142.251.46.238 ok
( Fill in an address + port )
sockaddr googleaddr
80 googleaddr ->port!
google.com ->h_addr googleaddr ->addr!
( Create a socket and connect )
AF_INET SOCK_STREAM 0 socket value sock
sock googleaddr sizeof(sockaddr_in) connect throw
( Send an HTTP request )
s" GET / HTTP/1.0" sock write-file throw
: semit ( ch s -- ) swap >r rp@ swap 1 swap write-file throw rdrop ;
: semit ( ch s -- ) swap >r rp@ 1 0 send 0< throw rdrop ;
: scr 13 sock semit 10 sock semit ;
scr scr
( Read and print part of reply )
here 100000 sock read-file throw constant len
here len type
🙬 A TCP Server 🙮
socket(domain, type, protocol)
bind(sock, addr, addrlen)
listen(sock, backlog)
accept(sock, addr, *addrlen)
send(sock, data, datalen, flags)
recv(sock, data, datalen, flags)
close(sock)
🙬 A TCP Server 🙮
socket - make a socket
bind - bind it to a port
listen - listen for up to n connections
accept - accept a connection (new socket)
send - send data
recv - receive data
close - close the connection
🙬 Server Challenges 🙮
• Management multiple connections
• Blocking on connections
• poll / select
• fd_set etc. absent
🙬 A UDP Client/Server 🙮
gethostbyname(hostname)
socket(domain, type, protocol)
bind(sock, addr, addrlen)
sendto(sock, data, datalen, flags, addr, addrlen)
recvfrom(sock, data, datalen, flags, addr, *addrlen)
close(sock)
🙬 A UDP Client/Server 🙮
gethostbyname - lookup other hosts
socket - make a socket
bind - bind it to an incoming port
sendto - send a packet
recvfrom - receive a packet
close - close the binding
( Create a addres to listen on )
sockaddr incoming
9999 incoming ->port!
( Create a socket and bind to address )
AF_INET SOCK_DGRAM 0 socket value sockfd
sockfd non-block throw ( Optionally make non-blocking )
sockfd incoming sizeof(sockaddr_in) bind throw
( Read an incoming packet )
sockaddr received
variable received-len sizeof(sockaddr_in) received-len !
sockfd msg len 0 received received-len recvfrom to len
received ->addr@ ip. ." :" received ->port@ .
space space msg swap type cr
( Send an outgoing packet )
sockaddr outgoing
: say ( port -- "host" )
( Setup address )
outgoing ->port!
bl parse s>z gethostbyname ->h_addr outgoing ->addr!
( Sent rest of the parse buffer as the message )
sockfd tib >in @ + #tib @ >in @ -
0 outgoing sizeof(sockaddr_in) sendto drop
#tib @ >in !
;
🙬 Visual Editor (BONUS) 🙮
• visual edit /spiffs/filename
• Arrows / PgUp / PgDn
• Ctrl-S to save
• Ctrl-X to exit
• Very wasteful, WIP
DEMO & QUESTIONS?
🏵
Thank you!