The socket used for communication with the IoT server is provided via the socket field of the mco_edge_params_t
structure when creating the edge database. The interface is defined by the following structure:
typedef struct mco_edge_socket_t_ {
MCO_RET (*f_connect)(struct mco_edge_socket_t_ *s);
// Establish a connection to the server.
MCO_RET (*f_send)(struct mco_edge_socket_t_ *s, const void *buf, mco_size_t n_bytes);
// Send n_bytes of data from buffer buf.
MCO_RET (*f_recv)(struct mco_edge_socket_t_ *s, void *buf, mco_size_t min_bytes,
mco_size_t max_bytes, mco_size_t *rcvd_bytes);
// Receive between min_bytes and max_bytes into buf.
// The actual number of bytes received is returned via rcvd_bytes.
MCO_RET (*f_disconnect)(struct mco_edge_socket_t_ *s);
// Close the connection to the server.
timer_unit deadline;
// Absolute timeout (system timer value). connect(), send(), and recv() must
// complete before this deadline (either successfully or with an error).
} mco_edge_socket_t;
Note on the relationship between the deadline and the timeout parameter of the mco_edge_synk() function. A timeout is relative-it means “wait up to N microseconds from now.” A deadline is absolute-it means “finish by this specific time.” mco_edge_sync() takes a timeout, converts it into a deadline, and from that point on everything operates against that fixed deadline. The edge runtime coordinates multiple socket operations within the mco_edge_sync() function under one overall time budget. Also note that the The socket::deadline is not a parameter-it’s an internal runtime variable. Applications only need to know or care about it if it is necessary to implement a new socket type. It is exposed through the edge interfa because the socket structure “lives" outside the edge buffer / database, and its sizeof() has to be “known” to the application.
Currently, two implementations of this interface are available: the POSIX socket and the TLS socket.
The POSIX socket (mco_net_socket_t) is initialized using the mco_edge_create_net4_socket() function:
MCO_RET mco_edge_create_net4_socket(const char *ip, int port, mco_net_socket_t *s);
Where:
ip is the IP address of the IoT server as a string (e.g., "192.168.0.1").port is the server port number.s is the pointer to the socket structure to be initialized.The TLS socket (mco_mbedtls_socket_t), based on the mbedTLS library, is initialized using the mco_edge_create_mbedtls_socket() function:
MCO_RET mco_edge_create_mbedtls_socket(mco_mbedtls_socket_params_t *p,
mco_mbedtls_socket_t *ssl);
The configuration parameters are passed via the mco_mbedtls_socket_params_t structure:
// Helper structure for representing binary data
typedef struct {
const unsigned char *ptr;
unsigned int len;
} mco_mbedtls_bindata_t;
typedef struct {
mco_edge_socket_t *bio; // Base I/O socket (e.g., mco_net_socket_t)
const char *hostname; // Common Name (CN) for verification
int authmode; // Certificate verification mode:
// MBEDTLS_SSL_VERIFY_NONE,
// MBEDTLS_SSL_VERIFY_OPTIONAL, or
// MBEDTLS_SSL_VERIFY_REQUIRED
mbedtls_ssl_protocol_version tls_version; // TLS version:
// MBEDTLS_SSL_VERSION_TLS1_2 or
// MBEDTLS_SSL_VERSION_TLS1_3
mco_mbedtls_bindata_t ca_cert; // Trusted CA certificate(s):
// DER-encoded or concatenated PEM blocks
mco_mbedtls_bindata_t own_cert; // Client certificate (DER or PEM)
mco_mbedtls_bindata_t own_pkey; // Private key (DER or PEM)
const int *ciphersuites; // NULL-terminated list of IANA
// ciphersuite identifiers, or NULL
} mco_mbedtls_socket_params_t;
The following example demonstrates how to create a POSIX socket, connect to the IoT server, and perform basic data synchronization:
#include <mcoedgenet.h>
#include "mindb.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define CHECK(call) \
{ \
MCO_RET rc_ = call; \
if (rc_ != MCO_S_OK) { \
printf("Call %s at %s:%d failed with error %d\n", \
#call, __FILE__, __LINE__, rc_); \
abort(); \
} \
}
#define MEM_BUF_SIZE (1024)
unsigned char memory[MEM_BUF_SIZE];
const char db_name[] = "rdb";
void errhandler(MCO_RET errcode)
{
printf("Fatal error %d\n", errcode);
exit(1);
}
int main(int argc, char *argv[])
{
mco_net_socket_t net_sock;
mco_edge_params_t edge_params;
mco_edge_error_set_handler(errhandler);
CHECK(mco_edge_start());
mco_edge_params_init(&edge_params);
// Create socket: use args if provided, otherwise defaults
CHECK(mco_edge_create_net4_socket(
argc > 2 ? argv[1] : "127.0.0.1",
argc > 2 ? atoi(argv[2]) : 15000,
&net_sock));
edge_params.recv_buf_size = 128;
edge_params.send_buf_size = 128;
edge_params.socket = &net_sock.es;
CHECK(mco_edge_open(db_name, mindb_get_dictionary(),
memory, sizeof(memory), &edge_params));
{
mco_edge_h eh = 0;
mco_trans_h t = 0;
Sensor sensor = {0};
int controller_id = 111;
mco_size_t total_space, used_space;
CHECK(mco_edge_connect(db_name, &eh));
for (int i = 0; i < 100; ++i) {
mco_datetime ts = mco_system_get_current_time() / 1000;
CHECK(mco_edge_trans_start(eh, &t));
for (int id = 1; id < 3; ++id) {
// Check if buffer is full
if (mco_edge_has_space(t, Sensor_code) == MCO_S_BUSY) {
// Sync and clear acknowledged transactions
CHECK(mco_edge_sync(eh,
MCO_EDGE_SYNC_WAIT | MCO_EDGE_SYNC_AUTO_CLEAR,
2000));
}
CHECK(Sensor_new(t, &sensor));
CHECK(Sensor_ts_put(&sensor, ts));
CHECK(Sensor_controller_id_put(&sensor, controller_id));
CHECK(Sensor_sensor_id_put(&sensor, id));
CHECK(Sensor_value_put(&sensor, (rand() % 10000) / 1000.));
}
CHECK(mco_edge_trans_commit(t));
mco_edge_total_space(eh, &total_space);
mco_edge_used_space(eh, &used_space);
printf("[%d] Insert sensor pack, ts = %llu. Used %d of %d bytes\n",
controller_id, ts, (int)used_space, (int)total_space);
sleep(1);
}
// Final sync
CHECK(mco_edge_sync(eh, MCO_EDGE_SYNC_WAIT, 2000));
CHECK(mco_edge_disconnect(eh));
}
CHECK(mco_edge_close(db_name));
CHECK(mco_edge_stop());
return 0;
}