Linux Socket timeout works on WSL, but not on Ubuntu - linux

I try to run a TCP client without a server. The idea is simply to periodically try to connect.
For this, the client tries to connect to port 1500 on localhost.
Piece of code:
// Create socket
if ((create_socket=socket (AF_INET, SOCK_STREAM, PF_UNSPEC)) > 0)
printf ("Socket created\n");
address.sin_family = AF_INET;
address.sin_port = htons (1500);
inet_aton (argv[1], &address.sin_addr);
// Connect to server
connect ( create_socket,
(struct sockaddr *) &address,
sizeof (address));
FD_ZERO(&fdset);
FD_SET(create_socket, &fdset);
tv.tv_sec = 2; /* 2 seconds timeout */
tv.tv_usec = 0;
rv = select(create_socket + 1, NULL, &fdset, NULL, &tv);
if (rv == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(create_socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0)
{
printf ("Connection with server (%s) established \n",
inet_ntoa (address.sin_addr));
}
else
{
printf("Error on connect: unsuccessfull\n");
close (create_socket);
continue;
}
}
else if (rv == 0)
{
printf("Timeout on connect\n");
close (create_socket);
continue;
}
else
{
printf("Error on connect\n");
close (create_socket);
continue;
}
I've set it up in Ubuntu 18.04 on WSL. There, the code waits on select for the defined timeout of 2 seconds and returns appropriate return values. (0 on timeout, 1 on connect).
The return value of connect is -1 on WSL and VMware.
In Ubuntu 18 (VMware) there is no pause in that line. In any case, even without any server listening on that port, I get immediately a return value of 1.
Why is there this difference?
There is a similar behavior later on in that code:
tv.tv_sec = 2;
tv.tv_usec = 0;
if (setsockopt(create_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv) < 0)
{
printf("Error on setsockopt SO_RCVTIMEO");
exit(EXIT_FAILURE);
}
// INNER LOOP: Receive data
do
{
size = recv(create_socket, buffer, BUF-1, 0);
if( size > 0)
{
buffer[size] = '\0';
printf ("Message received: %s\n", buffer);
}
else if (size == -1)
{
// on VMware, errno is 107 if there is no server, but coming to that line was not intended
printf ("Timeout\n");
}
else //
{
printf("Server offline\n");
// GO BACK TO OUTER LOOP and reconnect
break;
}
Here, in WSL the recv takes up to 2 seconds, while waiting for any incoming data. (But only if the aforementioned block (connect, select) indicates a valid connection)
In VMware I directly get the feedback. (even without connection)
Does it simply work on WSL by chance?
The argument contains the server IP and is 127.0.0.1.
lsof shows no connection.
Update 2020-11-18
Here's the full code as requested by Bodo
#include <iostream>
#include <vector>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF 1024
using namespace std;
int main (int argc, char **argv) {
int create_socket;
char *buffer = (char*)malloc(BUF);
struct sockaddr_in address;
int size;
int rv;
struct timeval tv;
fd_set fdset;
// HERE STARTS THE OUTER LOOP - Connect and restart connection
do
{
// Create socket
if ((create_socket=socket (AF_INET, SOCK_STREAM, PF_UNSPEC)) > 0)
printf ("Socket created\n");
address.sin_family = AF_INET;
address.sin_port = htons (15000);
inet_aton ("127.0.0.1", &address.sin_addr);
// Connect to server
int flags = fcntl(create_socket, F_GETFL, 0);
if (flags == -1) return false;
rv = connect ( create_socket,
(struct sockaddr *) &address,
sizeof (address));
printf ("Connect. rv = %i\n", rv);
if (rv == -1)
{
switch (errno)
{
case ECONNREFUSED: printf ("errno = %i (ECONNREFUSED)\n", errno); break;
default: printf ("errno = %i (ECONNREFUSED)\n", errno); break;
}
}
FD_ZERO(&fdset);
FD_SET(create_socket, &fdset);
tv.tv_sec = 2;
tv.tv_usec = 0;
rv = select(create_socket + 1, NULL, &fdset, NULL, &tv);
if (rv == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(create_socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0)
{
printf ("Connection with server (%s) established \n",
inet_ntoa (address.sin_addr));
}
else
{
printf("Error on connect: unsuccessfull\n");
close (create_socket);
continue;
}
}
else if (rv == 0)
{
printf("Timeout on connect\n");
close (create_socket);
continue;
}
else
{
printf("Error on connect\n");
close (create_socket);
continue;
}
if (setsockopt(create_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv) < 0)
{
printf("Error on setsockopt SO_RCVTIMEO");
exit(EXIT_FAILURE);
}
// INNER LOOP: Receive data
do
{
size = recv(create_socket, buffer, BUF-1, 0);
if( size > 0)
{
buffer[size] = '\0';
printf ("Data received: %s\n", buffer);
}
else if (size == -1)
{
printf ("Timeout\n");
}
else //
{
printf("Server offline\n");
// GO BACK TO OUTER LOOP and reconnect
break;
}
} while (strcmp (buffer, "quit\n") != 0);
close (create_socket);
} while (strcmp (buffer, "quit\n") != 0);
return EXIT_SUCCESS;
}
In WSL the output is
Socket created
Connect. rv = -1
errno = 111 (ECONNREFUSED)
then nothing for 2 seconds
afterwards
Timeout on connect
Socket created
Connect. rv = -1
errno = 111 (ECONNREFUSED)
and again nothing for 2 seconds ...
Output in VMware
Socket created
Connect. rv = -1
errno = 111 (ECONNREFUSED)
Connection with server (127.0.0.1) established
Timeout
Timeout
Timeout
Timeout
Where no timeout is fulfilled.
The idea of timeout has been to try to connect on a regular basis, but not as fast as possible.

Obviously there is something wrong when errno = 111 (ECONNREFUSED) is followed by Connection with server (127.0.0.1) established.
When connect returns -1 and errno is NOT EINPROGRESS you should not use selectand getsockopt(...SO_ERROR...). According to https://man7.org/linux/man-pages/man2/connect.2.html, this is only documented for EINPROGRESS.
Both on real Linux and WSL you get errno = 111 (ECONNREFUSED) after a failed connect. I consider the timeout in WSL wrong as the error (conection refused) was already reported, so it does not make sense to wait for a result. But as the behavior is not specified, it may be implementation dependent.
If you want to have a delay before the next connection attempt, you should not use select but for example sleep followed by repeating the loop.
I suggest something like this:
rv = connect ( create_socket,
(struct sockaddr *) &address,
sizeof (address));
printf ("Connect. rv = %i\n", rv);
if (rv == -1)
{
switch (errno)
{
case ECONNREFUSED: printf ("errno = %i (ECONNREFUSED) %s\n", errno, strerror(errno)); break;
default: printf ("errno = %i (other) %s\n", errno, strerror(errno)); break;
}
if(errno != EINPROGRESS)
{
sleep(10); // chose a suitable delay before next connection attempt
continue;
}
}

Related

Socket bind() failed with Invalid argument error for program running on MacOS

I'm trying to run a simple C program which uses the bind() function to bind a IPv4/IPv6 address to a socket.
Below is the code:
int main(int argc, char *argv[])
{
int socket_fd = -1, addrlen = 0, af;
struct sockaddr_storage addr = {0};
unsigned connections = 0;
pthread_t workers[WORKER_NUM] = { 0 };
int client_sock_fds[WORKER_NUM] = { 0 };
char ip_string[64];
if (argc > 1 && strcmp(argv[1], "inet6") == 0) {
af = AF_INET6;
init_sockaddr_inet6((struct sockaddr_in6 *)&addr);
}
else {
af = AF_INET;
init_sockaddr_inet((struct sockaddr_in *)&addr);
}
printf("[Server] Create socket\n");
socket_fd = socket(af, SOCK_STREAM, 0);
if (socket_fd < 0) {
perror("Create socket failed");
goto fail;
}
printf("[Server] Bind socket\n");
addrlen = sizeof(addr);
if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
perror("Bind failed");
goto fail;
}
printf("[Server] Listening on socket\n");
if (listen(socket_fd, 3) < 0) {
perror("Listen failed");
goto fail;
}
printf("[Server] Wait for clients to connect ..\n");
while (connections < WORKER_NUM) {
client_sock_fds[connections] =
accept(socket_fd, (struct sockaddr *)&addr, (socklen_t *)&addrlen);
if (client_sock_fds[connections] < 0) {
perror("Accept failed");
break;
}
if (sockaddr_to_string((struct sockaddr *)&addr, ip_string,
sizeof(ip_string) / sizeof(ip_string[0]))
!= 0) {
printf("[Server] failed to parse client address\n");
goto fail;
}
printf("[Server] Client connected (%s)\n", ip_string);
if (pthread_create(&workers[connections], NULL, run,
&client_sock_fds[connections])) {
perror("Create a worker thread failed");
shutdown(client_sock_fds[connections], SHUT_RDWR);
break;
}
connections++;
}
if (connections == WORKER_NUM) {
printf("[Server] Achieve maximum amount of connections\n");
}
for (int i = 0; i < WORKER_NUM; i++) {
pthread_join(workers[i], NULL);
}
printf("[Server] Shuting down ..\n");
shutdown(socket_fd, SHUT_RDWR);
sleep(3);
printf("[Server] BYE \n");
return EXIT_SUCCESS;
fail:
printf("[Server] Shuting down ..\n");
if (socket_fd >= 0)
close(socket_fd);
sleep(3);
return EXIT_FAILURE;
}
static void
init_sockaddr_inet(struct sockaddr_in *addr)
{
/* 0.0.0.0:1234 */
addr->sin_family = AF_INET;
addr->sin_port = htons(1234);
addr->sin_addr.s_addr = htonl(INADDR_ANY);
}
static void
init_sockaddr_inet6(struct sockaddr_in6 *addr)
{
/* [::]:1234 */
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(1234);
addr->sin6_addr = in6addr_any;
}
When this program is compiled and run on Linux it works without any errors. But when the same program is compiled and run on MacOS, the bind() function returns an "Invalid Argument" error.
I checked the man page for the bind() function to see what the possible reasons for this error could be. The three possible reasons were:
The socket was already bound to another address
The addrlen value passed to the function is incorrect
The addr is not a valid address for this socket's domain
I was able to verify that it wasn't due to the first and third reason.
My question would be as to why does the addrlen value passed to the bind() function throw an "Incorrect argument" error when ran on a Unix system but works perfectly fine when ran on a Linux system?
The addrlen value you pass to bind() must exactly match the address family you specify in socket(). That means addrlen must be set to sizeof(sockaddr_in) for AF_INET, and sizeof(sockaddr_in6) for AF_INET6. Using sizeof(sockaddr_storage) is the wrong value, as sockaddr_storage is designed to be large enough to hold all possible sockaddr_... types, so its size may be larger than sockaddr_in6.
On the other hand, when calling accept(), you need to set addrlen to the full size of addr beforehand, so it knows how much memory it has to work with when writing the client's address to addr. addrlen will be adjusted to the actual size written. However, you can't simply type-cast an int* into a socklen_t*, so addrlen needs to be an actual socklen_t type. Besides, bind() is expecting a socklen_t anyway, not an int.
Try something more like this instead:
int main(int argc, char *argv[])
{
int socket_fd = -1, af;
socklen_t addrlen; // <-- add this!
struct sockaddr_storage addr = {0};
unsigned connections = 0;
pthread_t workers[WORKER_NUM] = { 0 };
int client_sock_fds[WORKER_NUM] = { 0 };
char ip_string[64];
if (argc > 1 && strcmp(argv[1], "inet6") == 0) {
af = AF_INET6;
addrlen = sizeof(struct sockaddr_in6); // <-- add this!
init_sockaddr_inet6((struct sockaddr_in6 *)&addr);
}
else {
af = AF_INET;
addrlen = sizeof(struct sockaddr_in); // <-- add this!
init_sockaddr_inet((struct sockaddr_in *)&addr);
}
printf("[Server] Create socket\n");
socket_fd = socket(af, SOCK_STREAM, 0);
if (socket_fd < 0) {
perror("Create socket failed");
goto fail;
}
printf("[Server] Bind socket\n");
if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
perror("Bind failed");
goto fail;
}
printf("[Server] Listening on socket\n");
if (listen(socket_fd, 3) < 0) {
perror("Listen failed");
goto fail;
}
printf("[Server] Wait for clients to connect ..\n");
while (connections < WORKER_NUM) {
addrlen = sizeof(addr); // <-- add this!
client_sock_fds[connections] =
accept(socket_fd, (struct sockaddr *)&addr, &addrlen);
if (client_sock_fds[connections] < 0) {
perror("Accept failed");
break;
}
if (sockaddr_to_string((struct sockaddr *)&addr, ip_string,
sizeof(ip_string) / sizeof(ip_string[0])) != 0) {
printf("[Server] failed to parse client address\n");
goto fail;
}
printf("[Server] Client connected (%s)\n", ip_string);
if (pthread_create(&workers[connections], NULL, run,
&client_sock_fds[connections]) != 0) {
perror("Create a worker thread failed");
shutdown(client_sock_fds[connections], SHUT_RDWR);
break;
}
connections++;
}
if (connections == WORKER_NUM) {
printf("[Server] Achieve maximum amount of connections\n");
}
for (int i = 0; i < connections; i++) { // <-- needs to be the actual thread count, not WORKER_NUM!
pthread_join(workers[i], NULL);
}
printf("[Server] Shuting down ..\n");
shutdown(socket_fd, SHUT_RDWR);
sleep(3);
printf("[Server] BYE \n");
return EXIT_SUCCESS;
fail:
printf("[Server] Shuting down ..\n");
if (socket_fd >= 0)
close(socket_fd);
sleep(3);
return EXIT_FAILURE;
}
That being said, you should use getaddrinfo() instead to initialize the sockaddr_... that you pass to bind(). You should not be initializing it manually at all.
...
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc, char *argv[])
{
int socket_fd = -1, res;
...
struct addrinfo hints = { 0 };
struct addrinfo *addrs = NULL;
if (argc > 1 && strcmp(argv[1], "inet6") == 0)
hints.ai_family = AF_INET6;
else
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
printf("[Server] Initializing socket address\n");
res = getaddrinfo(NULL, "1234", &hints, &addrs);
if (res != 0) {
fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(res));
goto fail;
}
printf("[Server] Create socket\n");
socket_fd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (socket_fd < 0) {
perror("Create socket failed");
freeaddrinfo(addrs);
goto fail;
}
printf("[Server] Bind socket\n");
if (bind(socket_fd, addrs->ai_addr, addrs->ai_addrlen) < 0) {
perror("Bind failed");
freeaddrinfo(addrs);
goto fail;
}
freeaddrinfo(addrs);
...
}

Socket changes from SOCK_STREAM to SOCK_DGRAM after binding?

On a Linux machine, I was having trouble with a program called kwallet (part of KDE) not starting on login even though I set it up to with PAM, so I checked out the source code to diagnose the problem. It turns out that the program creates a server that listens on a TCP socket bound to a file.
But the problem is, while the socket is a TCP socket when it's first instantiated, as soon as it's bound to the file, it suddenly becomes a UDP socket, so the call to listen on it fails.
Anyone have any idea what's happening here? Do I just have a faulty installation or something?
Here's the relevant code, with some of the lines I added to help diagnose the problem:
static void execute_kwallet(pam_handle_t *pamh, struct passwd *userInfo, int toWalletPipe[2], char *fullSocket)
{
//In the child pam_syslog does not work, using syslog directly
//keep stderr open so socket doesn't returns us that fd
int x = 3;
//Close fd that are not of interest of kwallet
for (; x < 64; ++x) {
if (x != toWalletPipe[0]) {
close (x);
}
}
//This is the side of the pipe PAM will send the hash to
close (toWalletPipe[1]);
//Change to the user in case we are not it yet
if (drop_privileges(userInfo) < 0) {
syslog(LOG_ERR, "%s: could not set gid/uid/euid/egit for kwalletd", logPrefix);
free(fullSocket);
goto cleanup;
}
int envSocket;
if ((envSocket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
syslog(LOG_ERR, "%s: couldn't create socket", logPrefix);
free(fullSocket);
goto cleanup;
}
int socket_type;
socklen_t optlen;
optlen = sizeof socket_type;
if(getsockopt(envSocket, SOL_SOCKET, SO_TYPE, &socket_type, &optlen) < 0) {
syslog(LOG_INFO, "%s-Couldn't read socket option: %d-%s\n", logPrefix, errno, strerror(errno));
} else {
syslog(LOG_INFO, "%s-Socket type is %d\n", logPrefix, socket_type);
if(socket_type != SOCK_STREAM)
syslog(LOG_INFO, "%s-Socket is not SOCK_STREAM.\n", logPrefix);
}
struct sockaddr_un local;
local.sun_family = AF_UNIX;
if (strlen(fullSocket) > sizeof(local.sun_path)) {
syslog(LOG_ERR, "%s: socket path %s too long to open",
logPrefix, fullSocket);
free(fullSocket);
goto cleanup;
}
strcpy(local.sun_path, fullSocket);
free(fullSocket);
fullSocket = NULL;
unlink(local.sun_path);//Just in case it exists from a previous login
syslog(LOG_DEBUG, "%s: final socket path: %s", logPrefix, local.sun_path);
size_t len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(envSocket, (struct sockaddr *)&local, len) == -1) {
syslog(LOG_INFO, "%s-kwalletd: Couldn't bind to local file\n", logPrefix);
goto cleanup;
}
optlen = sizeof socket_type;
if(getsockopt(envSocket, SOL_SOCKET, SO_TYPE, &socket_type, &optlen) < 0) {
syslog(LOG_INFO, "%s-Couldn't read socket option: %d-%s\n", logPrefix, errno, strerror(errno));
} else {
syslog(LOG_INFO, "%s-Socket type is %d\n", logPrefix, socket_type);
if(socket_type != SOCK_STREAM)
syslog(LOG_INFO, "%s-Socket is not SOCK_STREAM.\n", logPrefix);
}
if (listen(envSocket, 5) == -1) {
syslog(LOG_INFO, "%s-kwalletd: Couldn't listen in socket: %d-%s\n", logPrefix, errno, strerror(errno));
goto cleanup;
}
//finally close stderr
close(2);
// Fork twice to daemonize kwallet
setsid();
pid_t pid = fork();
if (pid != 0) {
if (pid == -1) {
exit(EXIT_FAILURE);
} else {
exit(0);
}
}
//TODO use a pam argument for full path kwalletd
char pipeInt[4];
sprintf(pipeInt, "%d", toWalletPipe[0]);
char sockIn[4];
sprintf(sockIn, "%d", envSocket);
char *args[] = {strdup(kwalletd), "--pam-login", pipeInt, sockIn, NULL, NULL};
execve(args[0], args, pam_getenvlist(pamh));
syslog(LOG_ERR, "%s: could not execute kwalletd from %s", logPrefix, kwalletd);
cleanup:
exit(EXIT_FAILURE);
}
And here's the log output:
Oct 12 19:31:28 fuji login[413]: pam_kwallet5-Socket type is 1
Oct 12 19:31:28 fuji login[413]: pam_kwallet5: final socket path: /run/user/1000/kwallet5.socket
Oct 12 19:31:28 fuji login[413]: pam_kwallet5-Socket type is 2
Oct 12 19:31:28 fuji login[413]: pam_kwallet5-Socket is not SOCK_STREAM.
Oct 12 19:31:28 fuji login[413]: pam_kwallet5-kwalletd: Couldn't listen in socket: 95-Operation not supported

Unable to use Azure Sphere device as TCP Client

The TCP Echo Server sample from the Azure Sphere Samples at github works great and I am able to use my PC or a raspberryPi as a TCP client and communicate to the TCP echo server at the sphere device. However, I have a requirement where I need my Azure Sphere device as a TCP client. I modified the private network services sample application to work as a client (commented out Echo TCP Server) but the connection to the TCP server running at PC fails. The 'connect' returns -1 with errno set to 115 (ERROR: Operation in progress). I have configured the network services at azure sphere device with static ip. I have also verified that the same TCP client and server application works fine when a raspberrypi is used as a client and PC as the server. This is the modified main file of the application:
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "applibs_versions.h"
#include <applibs/log.h>
#include <applibs/networking.h>
#include <hw/sample_hardware.h>
typedef enum {
ExitCode_Success = 0,
ExitCode_TermHandler_SigTerm = 1,
ExitCode_StoppedHandler_Stopped = 2,
ExitCode_CheckStatus_SetInterfaceState = 3,
ExitCode_CheckStatus_GetInterfaceCount = 4,
ExitCode_CheckStatus_GetInterfaceConnectionStatus = 5,
ExitCode_ConfigureStaticIp_IpConfigApply = 6,
ExitCode_StartSntpServer_StartSntp = 7,
ExitCode_StartDhcpServer_StartDhcp = 8,
ExitCode_TimerHandler_Consume = 9,
ExitCode_InitLaunch_Epoll = 10,
ExitCode_InitLaunch_Timer = 11,
ExitCode_Main_WaitCallFailure = 12,
ExitCode_EchoStart_Listen = 13,
ExitCode_OpenIpV4_Socket = 14,
ExitCode_OpenIpV4_SetSockOpt = 15,
ExitCode_OpenIpV4_Bind = 16
} ExitCode;
int tcp_client(void);
static bool isNetworkStackReady = false;
static volatile sig_atomic_t exitCode = ExitCode_Success;
static struct in_addr localServerIpAddress;
static struct in_addr subnetMask;
static struct in_addr gatewayIpAddress;
static const uint16_t LocalTcpServerPort = 11000;
static int serverBacklogSize = 3;
static const char NetworkInterface[] = "eth0";
int tcp_client(void) {
int sock = 0, valread;
struct sockaddr_in serv_addr;
char* hello = "Hello from client";
char buffer[1024] = { 0 };
if ((sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0)
{
Log_Debug("\n Socket creation error \n");
return -1;
}
Log_Debug("TCP_CLIENT: socket created\n");
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
if (inet_pton(AF_INET, "169.254.57.67", &serv_addr.sin_addr) <= 0)
{
Log_Debug("\nInvalid address/ Address not supported \n");
return -1;
}
Log_Debug("TCP_CLIENT: ip assigned\n");
int conn = connect(sock, (struct sockaddr*) & serv_addr, sizeof(serv_addr));
Log_Debug("TCP_CLIENT: conn = %d\n", conn);
Log_Debug("ERROR: %s\n", strerror(errno));
if (conn < 0)
{
Log_Debug("\nConnection Failed \n");
return -1;
}
send(sock, hello, strlen(hello), 0);
Log_Debug("Hello message sent\n");
valread = read(sock, buffer, 1024);
Log_Debug("%s\n", buffer);
return 0;
}
static ExitCode CheckNetworkStatus(void)
{
int result = Networking_SetInterfaceState(NetworkInterface, true);
if (result != 0) {
if (errno == EAGAIN) {
Log_Debug("INFO: The networking stack isn't ready yet, will try again later.\n");
return ExitCode_Success;
} else {
Log_Debug(
"ERROR: Networking_SetInterfaceState for interface '%s' failed: errno=%d (%s)\n",
NetworkInterface, errno, strerror(errno));
return ExitCode_CheckStatus_SetInterfaceState;
}
}
Log_Debug("INFO: CheckNetworkStatus: Network stack is ready\n");
isNetworkStackReady = true;
ssize_t count = Networking_GetInterfaceCount();
if (count == -1) {
Log_Debug("ERROR: Networking_GetInterfaceCount: errno=%d (%s)\n", errno, strerror(errno));
return ExitCode_CheckStatus_GetInterfaceCount;
}
Log_Debug("INFO:CheckNetworkStatus: Networking_GetInterfaceCount: count=%zd\n", count);
// Read current status of all interfaces.
size_t bytesRequired = ((size_t)count) * sizeof(Networking_NetworkInterface);
Networking_NetworkInterface *interfaces = malloc(bytesRequired);
if (!interfaces) {
abort();
}
ssize_t actualCount = Networking_GetInterfaces(interfaces, (size_t)count);
if (actualCount == -1) {
Log_Debug("ERROR: Networking_GetInterfaces: errno=%d (%s)\n", errno, strerror(errno));
}
Log_Debug("INFO: Networking_GetInterfaces: actualCount=%zd\n", actualCount);
for (ssize_t i = 0; i < actualCount; ++i) {
Log_Debug("INFO: interface #%zd\n", i);
Log_Debug("INFO: interfaceName=\"%s\"\n", interfaces[i].interfaceName);
Log_Debug("INFO: isEnabled=\"%d\"\n", interfaces[i].isEnabled);
Networking_IpType ipType = interfaces[i].ipConfigurationType;
const char *typeText;
switch (ipType) {
case Networking_IpType_DhcpNone:
typeText = "DhcpNone";
break;
case Networking_IpType_DhcpClient:
typeText = "DhcpClient";
break;
default:
typeText = "unknown-configuration-type";
break;
}
Log_Debug("INFO: ipConfigurationType=%d (%s)\n", ipType, typeText);
Networking_InterfaceMedium_Type mediumType = interfaces[i].interfaceMediumType;
const char *mediumText;
switch (mediumType) {
case Networking_InterfaceMedium_Unspecified:
mediumText = "unspecified";
break;
case Networking_InterfaceMedium_Wifi:
mediumText = "Wi-Fi";
break;
case Networking_InterfaceMedium_Ethernet:
mediumText = "Ethernet";
break;
default:
mediumText = "unknown-medium";
break;
}
Log_Debug("INFO: interfaceMediumType=%d (%s)\n", mediumType, mediumText);
Networking_InterfaceConnectionStatus status;
int result = Networking_GetInterfaceConnectionStatus(interfaces[i].interfaceName, &status);
if (result != 0) {
Log_Debug("ERROR: Networking_GetInterfaceConnectionStatus: errno=%d (%s)\n", errno,
strerror(errno));
return ExitCode_CheckStatus_GetInterfaceConnectionStatus;
}
Log_Debug("INFO: interfaceStatus=0x%02x\n", status);
}
free(interfaces);
return ExitCode_Success;
}
static ExitCode ConfigureNetworkInterfaceWithStaticIp(const char *interfaceName)
{
Networking_IpConfig ipConfig;
Log_Debug("INFO: ConfigureNetworkInterfaceWithStaticIp: %s.\n", interfaceName);
Networking_IpConfig_Init(&ipConfig);
inet_aton("169.254.57.70", &localServerIpAddress);
inet_aton("255.255.0.0", &subnetMask);
inet_aton("0.0.0.0", &gatewayIpAddress);
Networking_IpConfig_EnableStaticIp(&ipConfig, localServerIpAddress, subnetMask,
gatewayIpAddress);
int result = Networking_IpConfig_Apply(interfaceName, &ipConfig);
Networking_IpConfig_Destroy(&ipConfig);
if (result != 0) {
Log_Debug("ERROR: Networking_IpConfig_Apply: %d (%s)\n", errno, strerror(errno));
return ExitCode_ConfigureStaticIp_IpConfigApply;
}
Log_Debug("INFO: Set static IP address on network interface: %s.\n", interfaceName);
return ExitCode_Success;
}
static ExitCode init_nw_services(void)
{
// Check the network stack readiness and display available interfaces when it's ready.
Log_Debug("INFO: Inside init_nw_services\n");
ExitCode localExitCode = CheckNetworkStatus();
if (localExitCode != ExitCode_Success) {
return localExitCode;
}
if (isNetworkStackReady) {
Log_Debug("INFO: init_nw_services: The network stack is ready, so unregister the timer event handler and launch servers.\n");
localExitCode = ConfigureNetworkInterfaceWithStaticIp(NetworkInterface);
}
return ExitCode_Success;
}
static ExitCode initialize_nw(void)
{
while (!isNetworkStackReady) {
if (!isNetworkStackReady) {
Log_Debug("INFO: init_nw_services\n");
ExitCode localExitCode = init_nw_services();
if (localExitCode != ExitCode_Success) {
exitCode = localExitCode;
return;
}
}
else {
Log_Debug("INFO: Network stack is ready\n");
}
}
Log_Debug("INFO: Initialization and server launch complete\n");
return ExitCode_Success;
}
int main(int argc, char *argv[])
{
Log_Debug("INFO: Private Ethernet TCP server application starting.\n");
exitCode = initialize_nw();
if (isNetworkStackReady)
tcp_client();
Log_Debug("INFO: Application exiting.\n");
return exitCode;
}
Yes this is possible. Please ensure that the destination is in the app manifest under AllowedConnections

Communicating between C and NodeJS using node-ipc and unix sockets

This question follows on from the following:
Communicating between NodeJS and C using node-ipc and unix sockets
In regards to the accepted solution (https://stackoverflow.com/a/39848936/1834057), I was wondering if someone might be able to clarify exactly how to send data from C to Node.js. The solution demonstrates sending data from Node.js to C, but not in reverse. I have an application that requires two-way communications, so the missing component is critical for me.
My understanding of unix sockets that one of either write, send or sendmsg should be able to do the job, however, I am not having any luck. If this understanding is incorrect, please advise.
In order to get a trivial example running, lets say when a message is read in the C code, lets send back a message and try to trigger the ipc.of[socketId].on('message',...) event on the node server.
Which means I am trying to turn this:
while ( (rc=read(cl,buf,sizeof(buf))) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
}
Into this:
while ( (rc=read(cl,buf,sizeof(buf)) ) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
//Respond to the node server
int n;
char * msg = "{\"type\":\"message\",\"data\":\"hello response\"}\t";
if((n = write(fd,msg,sizeof(msg))) < 0){
printf("send failed\n");
}else{
printf("sent %d bytes\n", n);
}
}
This would mean that the complete server.c code now becomes:
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <string.h> //Missing from original server.c
char *socket_path = "/tmp/icp-test";
int main(int argc, char *argv[]) {
struct sockaddr_un addr;
char buf[100];
int fd,cl,rc;
if (argc > 1) socket_path=argv[1];
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
if (*socket_path == '\0') {
*addr.sun_path = '\0';
strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2);
} else {
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
unlink(socket_path);
}
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind error");
exit(-1);
}
if (listen(fd, 5) == -1) {
perror("listen error");
exit(-1);
}
while (1) {
if ( (cl = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
while ( (rc=read(cl,buf,sizeof(buf)) ) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
//Respond to the node server
int n;
char * msg = "{\"type\":\"message\",\"data\":\"hello response\"}\t";
if((n = write(fd,msg,sizeof(msg))) < 0){
printf("send failed\n");
}else{
printf("sent %d bytes\n", n);
}
}
if (rc == -1) {
perror("read");
exit(-1);
}
else if (rc == 0) {
printf("EOF\n");
close(cl);
}
}
return 0;
}
Now unfortunately, the write message for me returns code -1, and is not received by the node.js server.
The client.js code remains unchanged, and is as provided in the original question.
Can someone please clarify what I am doing wrong?
You have to change
char * msg = "{\"type\":\"message\",\"data\":\"hello response\"}\t";
if((n = write(fd,msg,sizeof(msg))) < 0){
printf("send failed\n");
}else{
printf("sent %d bytes\n", n);
}
to
char * msg = "{\"type\":\"message\",\"data\":\"hello response\"}\f";
if((n = write(cl,msg,strlen(msg))) < 0){
printf("send failed\n");
}else{
printf("sent %d bytes\n", n);
}
The library is waiting for \f at the end of the message :-)
hope the answer is not too late :-)

Why recv() from socket does not block

I create a TCP server socket that listens to connect() requests and accept() them. After accepting a client socket receives data from it. I want recv() function to block with a timeout, but it seems to be non-blocking.
I have achieved the client to be in blocking mode when receiving response from the server, but it does not seem to work the same with the server side.
Below I copy-paste the server side that I want to receive data in blocking mode:
int clientSocket = accept(_serverSocket, (struct sockaddr *)NULL, NULL);
if (clientSocket < 0)
return -1;
// set TIMEOUT option to server socket
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500 * 1000;
int sockOptRet = setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
struct linger sl;
sl.l_onoff = 1; /* non-zero value enables linger option in kernel */
sl.l_linger = 0; /* timeout interval in seconds */
setsockopt(clientSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));
char _rcvBuffer[sizeof(can_frame)];
int numBytesRcv = recv(clientSocket, _rcvBuffer, sizeof(can_frame), 0);
I have also tried it with MSG_WAITALL flag but it does not change anything...
Your code should clearly block, but your timeout of 0.5s doesn't do showing it justice.
If you increase the timeout to something more conspicuous such as 2s and graft the code onto, e.g., beej's tpc server template, you get something like:
/*
** server.c -- a stream socket server demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
void sigchld_handler(int s)
{
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo); // all done with this structure
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
// set TIMEOUT option to server socket
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0 * 500 * 1000;
int sockOptRet = setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
struct linger sl;
sl.l_onoff = 1; /* non-zero value enables linger option in kernel */
sl.l_linger = 0; /* timeout interval in seconds */
setsockopt(new_fd, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));
char can_frame[1024];
char _rcvBuffer[sizeof(can_frame)];
int numBytesRcv = recv(new_fd, _rcvBuffer, sizeof(can_frame), 0);
if (send(new_fd, "Hello, world!", 13, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}
Now if your run this and then do:
nc localhost 3490
without typing a line to send, there should be a distinct 2-second wait
before the server gives up on you, indicating that the recv is indeed blocking.

Resources