Connection: Managing Modem Data Connections
This tutorial demonstrates how you can get network connection information, such as the default gateway and proxy configuration, IPv4 and IPv6 addresses, cellular connection state, and data transfer statistics.
Warm-up
Become familiar with the Connection API basics by learning about:
-
Initializing Connections
Initialize the connection module for use.
-
Getting Network Connection Details
Get the current connection type, IP address, and proxy information.
-
Getting Connection Information
Obtain cellular and Wi-Fi connection information with data transfer statistics, such as the amount of total sent/received data and last sent/received data.
-
Registering Property Change Callbacks
Register a callback for receiving notifications about connection property changes.
- IP sockets
-
Initializing a Socket
Initialize a socket for use and check the default connection.
-
Retrieving the Address Family for a Client-side Socket
Get the IP addresses for the hostname.
-
Retrieving the Address Family for a Server-side Socket
Get the IP addresses for the hostname.
-
Creating the Client-side Socket and Managing the Remote Host Connection
Create the socket, and communicate with the remote host.
-
Creating the Server-side Socket and Managing the Connection
Create the socket, and communicate with the client.
-
Closing the Socket
Close the socket and release resources.
-
Initializing a Socket
Initializing Connections
To initialize connections:
-
To use the functions and data types of the Connection API (in mobile and wearable applications), include the <net_connection.h> header file in your application:
#include <net_connection.h>
-
To be able to use all connection functions, you must create a handle that contains information about the connection. Use the connection static variable that stores the connection handle.
static connection_h connection;
Create the connection handle using the connection_create() function that allows you to obtain the connection state and data transfer information:
int error_code; error_code = connection_create(&connection); if (error_code != CONNECTION_ERROR_NONE) return;
-
When no longer needed, destroy the created connection handle:
connection_destroy(connection);
Getting the Network Connection Details
To get the type of the current connection, IP address, and proxy information:
- To get the type of the current profile for data connection, use the connection_get_type() function. The second parameter is the network type defined in the connection_type_e enumerator (in mobile and wearable applications).
int error_code; connection_type_e net_state; error_code = connection_get_type(connection, &net_state); if (error_code == CONNECTION_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "Network connection type : %d", net_state); }
To monitor changes in the connection type, register and define a callback:
connection_set_type_changed_cb(connection, __connection_changed_cb, NULL); static void __connection_changed_cb(connection_type_e type, void* user_data) { dlog_print(DLOG_INFO, LOG_TAG, "Type changed callback, connection type : %d", type); }
- To get the connection IPv4 address, use the connection_get_ip_address() function. The Connection API supports both IPv4 and IPv6, as defined in the connection_address_family_e enumerator (in mobile and wearable applications).
The IP address can be printed using the dlog util tool (as in the following example), or shown to the user in another way. Free the memory allocated for the ip_addr temporary variable.
char *ip_addr = NULL; error_code = connection_get_ip_address(connection, CONNECTION_ADDRESS_FAMILY_IPV4, &ip_addr); if (error_code == CONNECTION_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "IP address : %s", ip_addr); free(ip_addr); }
- To get the connection proxy information, use the connection_get_proxy() function. The following example prints the proxy address using the dlog util tool.
Free the memory allocated for the proxy_addr variable.
error_code = connection_get_proxy(connection, address_family, &proxy_addr); if (error_code == CONNECTION_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "Proxy address : %s", proxy_addr); free(proxy_addr); }
Getting the Connection Information
To obtain cellular and Wi-Fi connection information with data transfer statistics, such as the amount of total sent or received data and last sent or received data (only cellular and Wi-Fi statistics information is supported):
- To get the cellular connection state, use the connection_get_cellular_state() function. The function fills the second parameter with the current state, whose possible values are defined in the connection_cellular_state_e enumerator (in mobile and wearable applications).
In the following example, a switch statement is used to show the cellular state:
connection_cellular_state_e cellular_state; connection_get_cellular_state(connection, &cellular_state); switch (cellular_state) { case CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE: dlog_print(DLOG_INFO, LOG_TAG, "Out of service"); break; case CONNECTION_CELLULAR_STATE_FLIGHT_MODE: dlog_print(DLOG_INFO, LOG_TAG, "Flight mode"); break; case CONNECTION_CELLULAR_STATE_ROAMING_OFF: dlog_print(DLOG_INFO, LOG_TAG, "Roaming is turned off"); break; case CONNECTION_CELLULAR_STATE_CALL_ONLY_AVAILABLE: dlog_print(DLOG_INFO, LOG_TAG, "Call only"); break; case CONNECTION_CELLULAR_STATE_AVAILABLE: dlog_print(DLOG_INFO, LOG_TAG, "Available"); break; case CONNECTION_CELLULAR_STATE_CONNECTED: dlog_print(DLOG_INFO, LOG_TAG, "Connected"); break; default: dlog_print(DLOG_INFO, LOG_TAG, "error"); break; }
- To get the Wi-Fi connection state, use the connection_get_wifi_state() function. The function fills the second parameter with the current state, whose possible values are defined in the connection_wifi_state_e enumerator (in mobile and wearable applications).
In the following example, a switch statement is used to show the Wi-Fi state:
connection_wifi_state_e wifi_state; connection_get_wifi_state(connection, &wifi_state); switch (wifi_state) { case CONNECTION_WIFI_STATE_DEACTIVATED: dlog_print(DLOG_INFO, LOG_TAG, "Deactivated state"); break; case CONNECTION_WIFI_STATE_DISCONNECTED: dlog_print(DLOG_INFO, LOG_TAG, "Disconnected state"); break; case CONNECTION_WIFI_STATE_CONNECTED: dlog_print(DLOG_INFO, LOG_TAG, "Connected state"); break; default: dlog_print(DLOG_INFO, LOG_TAG, "error"); break; }
- To get connection statistics, use the connection_get_statistics() function.
Connection statistics include the amount of total sent and received data and the last sent and received data. The function parameters determine which statistics are received, and for which connection type.
- The second parameter defines the connection type using the connection_type_e enumerator (in mobile and wearable applications).
- The third parameter defines the statistic type using the connection_statistics_type_e enumerator (in mobile and wearable applications).
The following example reads all statistics for cellular and Wi-Fi connections:
long long last_received_size; error_code = connection_get_statistics(connection, CONNECTION_TYPE_CELLULAR, CONNECTION_STATISTICS_TYPE_LAST_RECEIVED_DATA, &last_received_size); // Handle statistics long long last_sent_size; error_code = connection_get_statistics(connection, CONNECTION_TYPE_CELLULAR, CONNECTION_STATISTICS_TYPE_LAST_SENT_DATA, &last_sent_size); // Handle statistics long long total_received_size; error_code = connection_get_statistics(connection, CONNECTION_TYPE_CELLULAR, CONNECTION_STATISTICS_TYPE_TOTAL_RECEIVED_DATA, &total_received_size); // Handle statistics long long total_sent_size; error_code = connection_get_statistics(connection, CONNECTION_TYPE_CELLULAR, CONNECTION_STATISTICS_TYPE_TOTAL_SENT_DATA, &total_sent_size); // Handle statistics error_code = connection_get_statistics(connection, CONNECTION_TYPE_WIFI, CONNECTION_STATISTICS_TYPE_LAST_RECEIVED_DATA, &last_received_size); // Handle statistics error_code = connection_get_statistics(connection, CONNECTION_TYPE_WIFI, CONNECTION_STATISTICS_TYPE_LAST_SENT_DATA, &last_sent_size); // Handle statistics error_code = connection_get_statistics(connection, CONNECTION_TYPE_WIFI, CONNECTION_STATISTICS_TYPE_TOTAL_RECEIVED_DATA, &total_received_size); // Handle statistics error_code = connection_get_statistics(connection, CONNECTION_TYPE_WIFI, CONNECTION_STATISTICS_TYPE_TOTAL_SENT_DATA, &total_sent_size); // Handle statistics
Registering Property Change Callbacks
To register callback functions that are called when information changes:
- Define callback functions.
In this use case, the registered callbacks are the __ip_changed_cb() and __proxy_changed_cb() functions, used for address changes. When an address changes, an information message is printed in the file (or shown to the user in another way). The message contains information on which address has been changed and what the new value is.
static void __ip_changed_cb(const char* ipv4_address, const char* ipv6_address, void* user_data) { dlog_print(DLOG_INFO, LOG_TAG, "%s callback, IPv4 address : %s, IPv6 address : %s", (char *)user_data, ipv4_address, (ipv6_address ? ipv6_address : "NULL")); } static void __proxy_changed_cb(const char* ipv4_address, const char* ipv6_address, void* user_data) { dlog_print(DLOG_INFO, LOG_TAG, "%s callback, IPv4 address : %s, IPv6 address : %s", (char *)user_data, ipv4_address, (ipv6_address ? ipv6_address : "NULL")); }
- Register the defined callback functions.
You have to register the previously defined callback functions using the connection_set_ip_address_changed_cb() and connection_set_proxy_address_changed_cb() functions. The last parameter (user_data) is set to a message which is printed in the callback.
error_code = connection_set_ip_address_changed_cb(connection, __ip_changed_cb, "IP addr changed:"); if (error_code != CONNECTION_ERROR_NONE) { // Error handling } error_code = connection_set_proxy_address_changed_cb(connection, __proxy_changed_cb, "Proxy IP addr changed:"); if (error_code != CONNECTION_ERROR_NONE) { // Error handling }
- Unregister the callback functions.
When the callbacks are no longer needed, unregister them with the applicable unset functions:
error_code = connection_unset_ip_address_changed_cb(connection); if (error_code != CONNECTION_ERROR_NONE) { // Error handling } error_code = connection_unset_proxy_address_changed_cb(connection); if (error_code != CONNECTION_ERROR_NONE) { // Error handling }
Initializing a Socket
To initialize a client or server-side socket for use:
- To use the functions and data types of the Socket API, include the following header files in your application:
#include <sys/stat.h> #include <arpa/inet.h> #include <netdb.h> #include <net/if.h>
- Declare the necessary variables:
int main(int argc, char **argv) { int rv = 0; int ip_type = -1; char user_url[100] = {0,}; char user_port[10] = {0,}; char user_msg[200] = {0,}; char *local_ipv4 = NULL; char *local_ipv6 = NULL; char *interface_name = NULL; connection_type_e net_state; connection_h connection = NULL; connection_profile_h profile_h = NULL;
- Include the required header file and create a connection handle.
- Check whether the default connection is available:
connection_type_e net_state; rv = connection_get_type(connection, &net_state); if (rv != CONNECTION_ERROR_NONE || net_state == CONNECTION_TYPE_DISCONNECTED) { dlog_print(DLOG_INFO, LOG_TAG, "Not connected %d\n", rv); connection_destroy(connection); return -1; }
- Check the address type of the default connection.
The address type can be IPv4 or IPv6.
int ip_type = -1; char *local_ipv4 = NULL; char *local_ipv6 = NULL; connection_profile_h profile_h = NULL; rv = connection_get_current_profile(connection, &profile_h); if (rv != CONNECTION_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "Fail to get profile handle %d\n", rv); connection_destroy(connection); return -1; } rv = connection_profile_get_ip_address(profile_h, CONNECTION_ADDRESS_FAMILY_IPV6, &local_ipv6); if (rv == CONNECTION_ERROR_NONE && g_strcmp0(local_ipv6, "::") != 0) { ip_type = CONNECTION_ADDRESS_FAMILY_IPV6; dlog_print(DLOG_INFO, LOG_TAG, "IPv6 address : %s\n", local_ipv6); } // If both IPv4 and IPv6 types are set, the IPv4 type is used as default here rv = connection_profile_get_ip_address(profile_h, CONNECTION_ADDRESS_FAMILY_IPV4, &local_ipv4); if (rv == CONNECTION_ERROR_NONE && g_strcmp0(local_ipv4, &0.0.0.0&) != 0) { ip_type = CONNECTION_ADDRESS_FAMILY_IPV4; dlog_print(DLOG_INFO, LOG_TAG, "IPv4 address : %s\n", local_ipv4); } if (ip_type != CONNECTION_ADDRESS_FAMILY_IPV6 && ip_type != CONNECTION_ADDRESS_FAMILY_IPV4) { dlog_print(DLOG_INFO, LOG_TAG, "No IP address!\n"); // Error handling } connection_profile_get_network_interface_name(profile_h, &interface_name); dlog_print(DLOG_INFO, LOG_TAG, "Interface Name: %s\n", interface_name); }
Retrieving the Address Family for a Client-side Socket
To get the hostname IP addresses to connect to the server-side socket:
- Define the user URL and message to be sent.
- Retrieve the IP addresses:
{ struct sockaddr_in6 *addr6; struct addrinfo hints; struct addrinfo *result; char user_url[100] = {0,}; char user_port[10] = {0,}; memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(user_url, user_port, &hints, &result) != 0) { dlog_print(DLOG_INFO, LOG_TAG, "getaddrinfo() error\n"); // Error handling } }
Retrieving the Address Family for a Server-side Socket
To get the hostname IP addresses to connect:
struct addrinfo hints; struct addrinfo *result, *rp; char buf[257]; int sockfd = -1; int csockfd = -1; int count = 0; int rwcount = 0; memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(default_ip, argv[1], &hints, &result) != 0) { printf("getaddrinfo() error\n"); goto done; }
Creating the Client-side Socket and Managing the Remote Host Connection
To create the socket and connect to a remote host:
- Find the proper address family and create the socket:
int sockfd = -1; struct addrinfo *rp; rp = result; for (rp = result; rp != NULL; rp = rp->ai_next) { if (rp->ai_family == AF_INET && ip_type == CONNECTION_ADDRESS_FAMILY_IPV4) { if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { dlog_print(DLOG_INFO, LOG_TAG, "socket error\n"); freeaddrinfo(result); } dlog_print(DLOG_INFO, LOG_TAG, "IPv4\n"); } else if (rp->ai_family == AF_INET6 && ip_type == CONNECTION_ADDRESS_FAMILY_IPV6) { if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) { dlog_print(DLOG_INFO, LOG_TAG, "socket error\n"); freeaddrinfo(result); } dlog_print(DLOG_INFO, LOG_TAG, "IPv6\n"); } }
- Connect to the remote host:
- Use the IPv4 socket:
if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) < 0) { dlog_print(DLOG_INFO, LOG_TAG, "connect() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); }
- Use the IPv6 socket.
The interface index is needed for the IPv6 connection.
char *interface_name = NULL; connection_profile_get_network_interface_name(profile_h, &interface_name); dlog_print(DLOG_INFO, LOG_TAG, "Interface Name: %s\n", interface_name); addr6 = (struct sockaddr_in6 *)rp->ai_addr; addr6->sin6_scope_id = if_nametoindex(interface_name); if ((sockfd = connect(sockfd, (struct sockaddr *)addr6, rp->ai_addrlen)) < 0) { dlog_print(DLOG_INFO, LOG_TAG, "connect() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); }
- Use the IPv4 socket:
- Manage messages:
- Send a message to the remote host:
if ((count = write(sockfd, user_msg, 200)) < 0) { dlog_print(DLOG_INFO, LOG_TAG, "write() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); } dlog_print(DLOG_INFO, LOG_TAG, "Sent count: %d, msg: %s\n", count, user_msg);
- Read a message from the remote host:
char buf[257]; memset(buf, 0x00, 257); if ((count = read(sockfd, buf, 256)) < 0) { dlog_print(DLOG_INFO, LOG_TAG, "read() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); } buf[count] = '\0'; dlog_print(DLOG_INFO, LOG_TAG, "\nRead: %s\n", buf);
- Send a message to the remote host:
Creating the Server-side Socket and Managing the Connection
To create the socket and manage the connection to a client:
- Find the proper address family and create the socket:
int sockfd = -1; struct addrinfo *rp; rp = result; for (rp = result; rp != NULL; rp = rp->ai_next) { if (rp->ai_family == AF_INET && ip_type == CONNECTION_ADDRESS_FAMILY_IPV4) { if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket error\n"); freeaddrinfo(result); goto done; } printf("IPv4\n"); } else if (rp->ai_family == AF_INET6 && ip_type == CONNECTION_ADDRESS_FAMILY_IPV6) { if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) { printf("socket error\n"); freeaddrinfo(result); goto done; } printf("IPv6\n"); } }
- Bind the found address:
if (bind(sockfd, rp->ai_addr, rp->ai_addrlen) != 0) { printf("bind() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); goto done; }
- Listen for client-side connections.
Mark the sockfd socket as a passive socket.
if (listen(sockfd, 5) != 0) { printf("listen() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); goto done; }
- Manage messages:
- Read a message from the client:
char buf[257]; memset(buf, 0x00, 257); if ((count = read(csockfd, buf, 256)) < 0) { printf("read() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); close(csockfd); goto done; } buf[count] = '\0'; printf("\nRead: %s\n", buf);
- Echo the received message back to the client:
if ((count = write(csockfd, buf, 256)) < 0) { printf("write() error: %s\n", strerror(errno)); freeaddrinfo(result); close(sockfd); close(csockfd); goto done; } printf("sent count: %d, msg: %s\n", count, buf); close(csockfd);
- Read a message from the client:
Closing the Socket
To close the client or server-side socket and release the resources:
freeaddrinfo(result); close(sockfd); done: connection_profile_destroy(profile_h); connection_destroy(connection); free(local_ipv6); free(local_ipv4); free(interface_name); return 0;