I socket sono punti di comunicazione sullo stesso computer o su computer diversi per lo scambio di dati.
I socket sono supportati da Unix, Windows, Mac e molti altri sistemi operativi.
Un socket Unix viene utilizzato in un framework di applicazioni client-server. Un server è un processo che esegue alcune funzioni su richiesta di un client.
La maggior parte dei protocolli a livello di applicazione, come FTP, SMTP e POP3, utilizzano i socket per stabilire la connessione tra client e server e quindi per lo scambio di dati.
La maggior parte delle applicazioni di rete utilizza l'architettura client-server, che si riferisce a due processi o due applicazioni che comunicano tra loro per scambiare alcune informazioni. Uno dei due processi agisce come processo client e un altro processo agisce come server.
La funzione
SockGetErrorrecupera l'errore corrente relativo alla funzione chiamata.
Fare riferimento a
man linux socket + <function>(cioèacceptolisten) sul web per ottenere l'elenco degli errori.
In altri casi il socket non può trasmettere/ricevere dati.
Quando si configura un server socket in modalità bloccante, utilizzare un task con tempo 0ms. La modalità bloking congela l'esecuzione fino a quando sono disponibili i byte.
Se il server è in modalità non bloccante, utilizzare un task con un tempo di 20 ms o superiore (USERSPACE o SLOW).
Se si vogliono usare i socket nella simulazione, ricordarsi di aprire la politica del firewall dell'applicazione a tutte le reti (private e pubbliche).
Questa sezione descrive le funzioni socket fondamentali necessarie per scrivere un client e un server TCP completi.
Quando un processo client vuole connettersi a un server, deve avere un modo per identificare il server a cui vuole connettersi. Se il client conosce l'indirizzo Internet a 32 bit dell'host su cui risiede il server, può contattarlo. Ma come fa il client a identificare il particolare processo server in esecuzione su quell'host?
Per risolvere il problema dell'identificazione di un particolare processo server in esecuzione su un host, sia TCP che UDP hanno definito un gruppo di porte note.
Per il nostro scopo, una porta sarà definita come un numero intero compreso tra 1024 e 65535. Questo perché tutti i numeri di porta inferiori a 1024 sono considerati noti (ad esempio, telnet usa la porta 23, http usa 80, ftp usa 21, ecc.)
Le assegnazioni delle porte ai servizi di rete si trovano nel file /etc/services. Se si sta scrivendo il proprio server, bisogna fare attenzione ad assegnare una porta al proprio server. Bisogna assicurarsi che questa porta non sia assegnata a nessun altro server.
Normalmente è prassi assegnare un numero di porta superiore a 5000. Ma ci sono molte organizzazioni che hanno scritto server con numeri di porta superiori a 5000 (ad esempio, Yahoo Messenger funziona su 5050, SIP Server funziona su 5060, ecc.)
You can find the most updated list of internet ports and associated service at IANA - TCP/IP Port Assignments.
Following is the diagram showing the complete Client and Server interaction

Per eseguire l'I/O di rete, la prima cosa che un processo deve fare è chiamare la funzione socket, specificando il tipo di protocollo di comunicazione desiderato e la famiglia di protocollo, ecc.
DINT := SockCreate(SOCKET_ADDRESS_FAMILY, SOCKET_TYPE, SOCKET_LEVEL);
La funzione SockCreate restituisce un descrittore di socket che può essere utilizzato nelle chiamate di sistema successive o -1 in caso di errore.
| Parametro | Descrizione |
|---|---|
SOCKET_ADDRESS_FAMILY |
Famiglia di protocolli ed è una delle costanti riportate di seguito:AF_INET Protocolli IPv4.AF_INET6 Protocolli IPv6.AF_LOCAL Protocolli di dominio Unix.AF_ROUTE Routing Sockets.AF_KEY Ket socket. |
SOCKET_TYPE |
Tipo di socket desiderato. Può assumere uno dei seguenti valori:SOCK_STREAM Stream socket.SOCK_DGRAM Datagram socket.SOCK_SEQPACKET Sequenced packet socket.SOCK_RAW Raw socket. |
SOCKET_LEVEL |
Deve essere impostato sul tipo di protocollo specifico indicato di seguito, oppure 0 per selezionare il default del sistema per la combinazione data di famiglia e tipoIPPROTO_TCP Protocollo di trasporto TCP.IPPROTO_UDP Protocollo di trasporto UDP.IPPROTO_SCTP Protocollo di trasporto SCTP. |
SOCKET_ADDRESS_FAMILY: SoloAF_INETè supportato daSirius-ESPLC.
SOCKET_TYPE: SoloSOCK_STREAMè supportato dal PLCSirius-ES.
SOCKET_LEVEL: SoloIPPROTO_TCPè supportato dal PLCSirius-ES.
SockBind(DINT_HANDLE , SOCKADDR);
La funzione SockBind assegna un indirizzo di protocollo locale a un socket. Nei protocolli Internet, l'indirizzo di protocollo è la combinazione di un indirizzo IPv4 a 32 bit o di un indirizzo IPv6 a 128 bit, insieme a un numero di porta TCP o UDP a 16 bit. Questa funzione è chiamata solo dal server TCP.
| Parametro | Descrizione |
|---|---|
DINT_HADLE |
Descrittore della presa restituito dalla funzione SockCreate. |
SOCKADDR |
Tipo di dato incorporato che contiene l'indirizzo IP e la porta di destinazione. Fare riferimento alla dichiarazione seguente. Per il server il SIN_ADDR deve essere vuoto (significa 127.0.0.1 o localhost). |
SOCKADDR : STRUCT
SIN_FAMILY : SOCKET_ADDRESS_FAMILY;
SIN_PORT : UINT;
SIN_ADDR : STRING;
END_STRUCT;
DINT := SockAccept(DINT_HANDLE , ref(SOCKADDR));
La funzione SockAccept è chiamata da un server TCP per restituire la prossima connessione completata dalla parte anteriore della coda delle connessioni completate.
Questa chiamata restituisce un descrittore non negativo in caso di successo, altrimenti restituisce -1 in caso di errore. Si presume che il descrittore restituito sia un descrittore di socket del client e tutte le operazioni di lettura e scrittura saranno eseguite su questo descrittore per comunicare con il client.
| Parametro | Descrizione |
|---|---|
DINT_HANDLE |
Descrittore di socket restituito dalla funzione SockCreate. |
SOCKADDR |
Dati del client connesso. |
BOOL := SockConnect(DINT_HANDLE , SOCKADDR);**
La funzione SockConnect è utilizzata da un client TCP per stabilire una connessione con un server TCP.
Questa chiamata restituisce TRUE se la connessione al server avviene con successo, altrimenti restituisce FALSE in caso di errore.
| Parametro | Descrizione |
|---|---|
DINT_HADLE |
Descrittore di socket restituito dalla funzione SockCreate. |
SOCKADDR |
Tipo di dato incorporato che contiene l'indirizzo IP e la porta di destinazione. Fare riferimento alla dichiarazione seguente. |
SOCKADDR : STRUCT
SIN_FAMILY : SOCKET_ADDRESS_FAMILY;
SIN_PORT : UINT;
SIN_ADDR : STRING;
END_STRUCT;
SockSelect(DINT_HANDLE, ref(FD_SET), ref(FD_SET), ref(FD_SET), TIMEVAL);**
SockSelect function indicates which of the specified file descriptors is ready for reading, ready for writing, or has an error condition pending.
When an application calls SockRecv, it is blocked until data arrives for that socket. An application could be doing other useful processing while the incoming data stream is empty. Another situation is when an application receives data from multiple sockets.
Calling SockRecv on a socket that has no data in its input queue prevents immediate reception of data from other sockets. The select function call solves this problem by allowing the program to poll all the socket handles to see if they are available for non-blocking reading and writing operations.
FD_SET : STRUCT
FD_COUNT : UDINT;
FD_ARRAY : ARRAY [0..63] OF DINT;
END_STRUCT;
Vedere l'esempio sock_available per vedere come usare SockSelect (fare riferimento al progetto demo Sirius-ES).
DINT := SockSend(DINT_HANDLE, ref(buffer), buffer_length, FLAGS);
La funzione SockSend è usata per inviare dati su stream socket o datagram socket connessi.
Questa chiamata restituisce il numero di byte inviati, altrimenti restituisce `-1' in caso di errore.
| Parametro | Descrizione |
|---|---|
DINT_HANDLE |
Descrittore di socket restituito dalla funzione SockCreate. |
buffer |
Contiene dati, possono essere array, stringhe o strutture di dati. |
buffer_length |
Numero di byte di lunghezza, se si invia un tipo di dato deve essere -1. |
FLAGS |
È impostato a 0. |
DINT := SockRecv(DINT_HANDLE, ref(buffer), buffer_length, FLAGS);
La funzione SockRecv è usata per ricevere dati su stream socket o datagram socket connessi.
Questa chiamata restituisce il numero di byte letti nel buffer, altrimenti restituisce -1 in caso di errore.
| Parametro | Descrizione |
|---|---|
DINT_HANDLE |
Descrittore di socket restituito dalla funzione SockCreate. |
buffer |
Bbuffer in cui leggere le informazioni. |
buffer_length |
È la lunghezza massima del buffer. |
FLAGS |
È impostato a 0. |
SockClose(DINT_HANDLE);
La funzione SockClose chiude il socket specificato nel descrittore del socket.
| Parametro | Descrizione |
|---|---|
DINT_HANDLE |
Descrittore di socket restituito dalla funzione SockCreate. |
SockIoctl(DINT_HANDLE , SOCKET_COMMAND, SOCKET_VALUE);
La funzione SockIoctl imposta o ottiene informazioni sul socket specificato dal descrittore del socket.
| SOCKET_COMMAND | Descrizione |
|:--- |
| FIONBIO | Imposta la modalità di blocco (0 bloking, 1 non bloccante).
| FIONREAD | Ottenere i byte nel buffer.
BOOL := SockSetOption(DINT_HADLE, SOCKET_LEVEL, SOCKET_OPTION, VALUE);
La funzione SockSetOption manipola le opzioni per il socket a cui fa riferimento il descrittore di file sockfd. Le opzioni possono esistere a più livelli di protocollo; sono sempre presenti al livello di socket più alto.
| Parametro | Descrizione |
|---|---|
DINT_HADLE |
Descrittore del socket restituito dalla funzione SockCreate |
SOCKET_LEVEL |
Opzione del livello di lavoro, fare riferimento alla dichiarazione seguente. |
SOCKET_OPTION |
Modalità di lavoro, fare riferimento alla dichiarazione seguente. |
SOCKET_LEVEL |
Descrizione |
|---|---|
IPPROTO_TCP |
Livello TCP |
SOL_SOCKET |
Livello di socket |
IPPROTO_DEFAULT |
Default di SO |
SOCKET_OPTION |
Descrizione |
|---|---|
TCP_NODELAY |
Inviare senza bufferizzare i dati |
SO_REUSEADDR |
Riutilizzare l'IP per client e server |
STRING := SockGetError(server_socket);
SockGetError function return the error description of the specified socket
Questo è il processo che riceve una richiesta dai client. Dopo aver ricevuto una richiesta dal client, questo processo esegue l'elaborazione necessaria, raccoglie le informazioni richieste e le invia al client richiedente.
Una volta fatto, diventa pronto a servire un altro client. I processi server sono sempre all'erta e pronti a servire le richieste in arrivo. Per eseguire l'I/O di rete, la prima cosa che un processo deve fare è chiamare la funzione socket, specificando il tipo di protocollo di comunicazione desiderato e la famiglia di protocolli, ecc.
VAR
server_handle : DINT;
addr : SOCKADDR;
blocking : BOOL;
error : STRING;
server_port : UINT;
cient_number : UINT;
END_VAR;
IF (server_handle < 0) THEN
(* create a new socket *)
server_handle := SockCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP);
IF (server_handle >= 0) THEN
(* blocking := true set to blocking mode *)
SockIoctl(server_handle , FIONBIO, bool_to_dint(not blocking));
IF (SockSetOption(server_handle, SOL_SOCKET, SO_REUSEADDR, 1) = false) THEN
(* error *)
error := SockGetError(server_handle);
SockClose(server_handle);
server_handle:= -1;
return;
END_IF;
addr.SIN_FAMILY := AF_INET;
addr.SIN_PORT := server_port;
addr.SIN_ADDR := '';
IF (SockBind(server_handle, addr) = false) THEN
(* error *)
error := SockGetError(server_handle);
SockClose(server_handle);
server_handle:= -1;
return;
END_IF;
(* accept clients *)
IF (SockListen(server_handle, cient_number) = false) THEN
(* error *)
error := SockGetError(server_handle );
SockClose(server_handle );
server_handle := -1;
return;
END_IF;
END_IF;
END_IF;
VAR
server_handle : DINT;
client_handle : DINT;
client_address : SOCKADDR;
END_VAR;
(* accept incoming connections from one client*)
IF (server_handle >= 0) THEN
IF (client_handle < 0) THEN
client_handle := SockAccept(server_handle, ref(client_address));
END_IF;
(* server_socket error *)
IF (client_handle = -2) THEN
close := true;
END_IF;
END_IF;
VAR
client_handle : DINT;
was_connected: BOOL;
bocking : BOOL;
END_VAR;
IF (client_handle >= 0) THEN
IF (not was_connected) THEN
was_connected := true;
(* blocking := true set to blocking mode *)
SockIoctl(client_handle, FIONBIO, bool_to_dint(not blocking));
END_IF;
available := sock_available(client_handle);
IF (available > 0) THEN
available := sock_recv(client_handle, ref(buffer), available);
IF (available > 0) THEN
(* do something with the request *)
END_IF;
ELSIF (available < 0) THEN
close := true;
END_IF;
END_IF;
VAR
client_handle : DINT;
available: DINT;
send: BOOL;
END_VAR;
IF send THEN
send := false;
IF (g_client_socket1 >= 0) THEN
buffer := 'sending response';
available := len(buffer) + 1;
available := sock_send(client_handle, ref(buffer), available);
END_IF;
END_IF;
VAR
server_handle : DINT;
END_VAR;
IF (server_handle >= 0) THEN
SockClose(server_handle);
server_handle := -1;
END_IF;
Questo è il processo che tipicamente effettua una richiesta di informazioni.
Dopo aver ricevuto la risposta, questo processo può terminare o eseguire altre elaborazioni.
VAR
client_handle : DINT;
error: STRING;
server_port : UINT := 1234;
server_address : STRING := '10.20.30.40';
END_VAR;
(* create a socket and save the handle *)
client_handle := SockCreate(AF_INET, SOCK_STREAM, IPPROTO_DEFAULT);
IF (client_handle >= 0) THEN
IF (SockSetOption(client_handle , IPPROTO_TCP, TCP_NODELAY, 1) = false) THEN
error := SockGetError(client_handle );
SockClose(client_handle );
client_socket := -1;
ELSE
(* blocking := true set to blocking mode *)
SockIoctl(client_handle , FIONBIO, bool_to_dint(not blocking));
END_IF;
END_IF;
IF (client_handle >= 0) THEN
addr.SIN_FAMILY := AF_INET;
addr.SIN_PORT := server_port;
addr.SIN_ADDR := server_address;
(* try connect to server *)
IF (SockConnect(client_handle, addr) = false) THEN
error := SockGetError(client_handle);
END_IF;
END_IF;
VAR
client_handle : DINT;
send : BOOL;
buffer : STRING;
length : DINT;
END_VAR;
IF send THEN
send := false;
IF (client_handle >= 0) THEN
buffer := 'sending request';
length := len(buffer) + 1;
length := sock_send(client_socket, ref(buffer), length);
END_IF;
END_IF;
VAR
client_handle : DINT;
buffer : STRING;
length : DINT;
END_VAR;
IF (client_handle >= 0) THEN
length := sock_available(client_handle);
IF (length > 0) THEN
length := sock_recv(client_socket, ref(buffer), length);
IF (length > 0) THEN
(* do something with the response buffer *)
END_IF;
ELSIF (length < 0) THEN
(* error occurred or server disconnected *)
END_IF;
END_IF;
VAR
client_handle : DINT;
END_VAR;
IF (client_handle >= 0) THEN
SockClose(client_handle );
client_handle := -1;
END_IF;
VAR
readfds : FD_SET;
timeout : TIMEVAL;
END_VAR
VAR_INPUT
socket : DINT;
END_VAR
timeout.TV_USEC := 1000 * 10;
(* wait for incoming bytes *)
sock_available := SockSelect(socket, ref(readfds), NULL, NULL, timeout);
IF (sock_available > 0) THEN
(* how many bytes we can read *)
sock_available := SockIoctl(socket, FIONREAD, 0);
(* no bytes avalilable, probably disconnected *)
IF (sock_available <= 0) THEN
sock_available := -2;
END_IF;
END_IF;