Two processes share UDP socket, one stops receiving messages after being restarted - linux

On a Linux platform I want to use socket sharing between 2 processes. One of the processes sends data on the socket, the other one receives data. I read on this site (here) that this is done by setting an option SO_REUSEADDR and/or SO_REUSEPORT.
So I set a test scenario with 3 processes:
1) An echo server bound to localhost that listens for messages on 127.0.0.1:44000. When it receives a message it replies to the sender immediately;
2) A sender that binds to 127.0.01:44001 and issues periodic messages to the echo server;
3) A receiver that binds to 127.0.01:44001 and listens for messages from the echo server;
The problem: Receiver stops receiving replies from the echo server. This depends on the socket option used:
With SO_REUSEADDR:
If sender(2) is started after the receiver(3) the latter does not receive anything. If the receiver is started last, but the sender is restarted, again the receiver stops receiving.
With SO_REUSEPORT (or together with SO_REUSEADDR):
The situation is just the opposite - receiver has to be started first for things to work, as sender is started last you can restart sender as many times as you want, everything will work well. But if you restart the sender (or just start it last) it will not get any message.
This is the code I use:
#define CC_LISTEN_PORT 44000
#define DRN_LISTEN_PORT 44001
static void runCC_EchoMode(struct sockaddr_in* ccaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in remaddr;
int recvlen, sentlen;
// bind
if(bind(s, (struct sockaddr *)ccaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
/* now loop, receiving data and printing what we received */
unsigned int addrlen = sizeof(remaddr);
int count = 0;
for (;;) {
debug("waiting on port %d\n", ntohs(ccaddr->sin_port));
recvlen = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&remaddr, &addrlen);
debug("received %d bytes\n", recvlen);
if (recvlen > 0) {
buf[recvlen] = 0;
printf("received message: \"%s\"\n", buf);
// send echo back
sprintf(buf, "Echo #%d", count++);
sentlen = sendto(s, buf, strlen(buf), 0, (struct sockaddr *)&remaddr, sizeof(remaddr));
debug("sent %d bytes to %s:%d\n", sentlen,
inet_ntoa(remaddr.sin_addr), ntohs(remaddr.sin_port));
}
}
close(s);
}
static void runDrn_SendMode(struct sockaddr_in* ccaddr, struct sockaddr_in* drnaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
int sentlen;
int one = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEADDR) failed\n");
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEPORT) failed\n");
}
// bind
if(bind(s, (struct sockaddr *)drnaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
int count = 0;
for (;;) {
sleep(2);
sprintf(buf, "Hello #%d", count++);
debug("sending \"%s\" to server...\n", buf);
sentlen = sendto(s, buf, strlen(buf), 0, (struct sockaddr *)ccaddr, sizeof(struct sockaddr_in));
debug("sent %d bytes\n", sentlen);
}
close(s);
}
static void runDrn_RcvMode(struct sockaddr_in* ccaddr, struct sockaddr_in* drnaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in remaddr;
int recvlen;
int one = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEADDR) failed\n");
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEPORT) failed\n");
}
// bind
if(bind(s, (struct sockaddr *)drnaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
/* now loop, receiving data and printing what we received */
unsigned int addrlen = sizeof(remaddr);
for (;;) {
debug("waiting on port %d\n", ntohs(drnaddr->sin_port));
recvlen = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&remaddr, &addrlen);
debug("received %d bytes\n", recvlen);
if (recvlen > 0) {
buf[recvlen] = 0;
printf("received message: \"%s\"\n", buf);
}
}
close(s);
}
int main(int argc, char *argv[])
{
int mode;
if (argc < 3) {
fprintf(stderr, "Usage: %s <host> <mode>\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Starting process with PID: %d\n", getpid());
// this is a simple wrapper of getaddrinfo()
AddressResolver resv1(argv[1]);
resv1.print();
struct sockaddr_in ccaddr, drnaddr;
ccaddr = *(resv1.getAddress(0));
ccaddr.sin_port = htons(CC_LISTEN_PORT);
drnaddr = *(resv1.getAddress(0));
drnaddr.sin_port = htons(DRN_LISTEN_PORT);
mode = atoi(argv[2]);
switch(mode) {
case 0: // cc
runCC_EchoMode(&ccaddr);
break;
case 1: // drone sender
runDrn_SendMode(&ccaddr, &drnaddr);
break;
case 2: // drone receiver
runDrn_RcvMode(&ccaddr, &drnaddr);
break;
default:
debug("Mode is not available\n");
break;
}
return 0;
}
And this is how I start the 3 processes:
./testUDP localhost 0
./testUDP localhost 1
./testUDP localhost 2
This is a the output of a test run:
./testUDP localhost 0
Starting process with PID: 10651
IP: 127.0.0.1
waiting on port 44000
received 8 bytes
received message: "Hello #0"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
received 8 bytes
received message: "Hello #1"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
received 8 bytes
received message: "Hello #2"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
^C
...
./testUDP localhost 1
Starting process with PID: 10655
IP: 127.0.0.1
sending "Hello #0" to server...
sent 8 bytes
sending "Hello #1" to server...
sent 8 bytes
sending "Hello #2" to server...
sent 8 bytes
^C
...
./testUDP localhost 2
Starting process with PID: 10652
IP: 127.0.0.1
waiting on port 44001
received 7 bytes
received message: "Echo #0"
waiting on port 44001
received 7 bytes
received message: "Echo #1"
waiting on port 44001
received 7 bytes
received message: "Echo #2"
waiting on port 44001
^C

The behavior of two different processes listening on the same interface and port is non-deterministic: it will vary by operating system, kernel version, and other factors.
In general, a port is intended to be associated with a single process and socket. SO_REUSE is intended for UDP multicast receive or for TCP to bypass the WAIT state after a connection drop. While some systems will let you bind a single port to multiple sockets, threads, or processes for other purposes, the behavior is too varied to be useful.
What you are probably looking for is some kind of packet duplication or round-robin distribution. SO_REUSE does not guarantee that.

Related

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

Handsfree device not able to receive any audio from sco socket

I am developing a handsfree module, in which after completing service level connection I am setting up sco connection with phone's audio gateway and receive audio data as below...
void audio_connection_setup(char *bluetooth_addr)
{
struct sockaddr_sco addr;
pthread_t tid;
int scoSock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
addr.sco_bdaddr = *BDADDR_ANY;
if (bind(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "Can't bind socket: %s (%d)",strerror(errno), errno);
}
hci_read_voice_setting(scoSock, &voice, 5000);
hci_write_voice_setting(scoSock, BT_VOICE_CVSD_16BIT, 5000);
str2ba(bluetooth_addr, &addr.sco_bdaddr);
if (connect(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "\nCan't connect: %s (%d)", strerror(errno), errno);
}
pthread_create(&tid, NULL, &read_data, &scoSock);
}
Here is the read_data thread
gBuff[16284];
void* read_data(int *scoSock)
{
int fd = *scoSock;
int len = -1;
char buff[48];
int numBytesRead;
while (1)
{
numBytesRead = 0;
while(numBytesRead < 16284)
{
memset(buff, 0x0, 48);
len = recv(fd, buff, 48, MSG_DONTWAIT);
usleep(10);
memcpy(gBuff + numBytesRead, buff + 2, len - 2);
numBytesRead = numBytesRead + len - 2;
}
printf("Number of bytes received = %d errno = %d\n", numBytesRead, errno);
memset(gBuff, 0x0, numBytesRead);
}
}
This code is working fine if I run it on linux PC, But when i run on arm board the recv system call returns errno EAGAIN in continuous loop and never comes out. On PC the recv system call returns number of bytes properly. What may be possible cause to this issue?
I think the reason should be when your code runs on the pc, you may get the audio data from HCI link, i.e. the HF sco over HCI and then you transfer it to PC driver. However when you switch to board, I wonder whether the board's hardware Bluetooth interface lets say UART, whether it has high throughput to transfer the sco data, and most importantly, you need check whether the sco data was routed to PCM interface i.e. does not sending to HCI.

Understandng the reason for recv blocking forever

I run a Linux program written in C that would periodically receive data by parsing an HTTP response, crunch some numbers and then report the result by HTTP GET of another web page.
My problem is that sometimes, one of the instances would "freeze".
Looking at top I can see that it is in sk_wait_data state and attaching a debugger reveals that it is blocked by a recv call.
Here is a minimal version of the code that does the TCP connection (it was adapted from http://www.linuxhowtos.org/C_C++/socket.htm):
int connectTCP(const char* host, const char* page, int portno) {
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
// Create socket //
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("ERROR opening socket");
// Get ip from hostname //
server = gethostbyname(host);
if (server == NULL)
error("ERROR, can not find host\n");
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy((char *)&serv_addr.sin_addr.s_addr, // Destination
(char *)server->h_addr, // Source
server->h_length); // Size
serv_addr.sin_port = htons(portno);
// Conect to socket //
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");
return sockfd;
}
char* httpGet(const char* host, const char* page, int portno) {
int sockfd, n;
sockfd = connectTCP(host, page, portno);
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", page, host);
n = send(sockfd,buffer,strlen(buffer), 0);
if (n < 0)
error("ERROR writing to socket");
int count = 0;
do {
n = recv(sockfd, buffer + count, BUFFER_MAX_SIZE - count, 0);
if (n < 0) {
error("ERROR reading from socket");
}
count += n;
} while(n != 0);
close(sockfd);
return buffer;
}
Bugs in your code:
If recv() returns zero you whould close the socket and stop reading.
If recv() returns -1 you should report the error, close the socket, and stop reading, unless you had set a read timeout and errno was EAGAIN/EWOULDBLOCK, in which case you should handle the timeout however is appropriate for your application.

why my TCP server code send a SYN/ACK on only first packet or only on the first connection?

SOCKET sock;
SOCKET fd;
uint16 port = 18001;
void CreateSocket()
{
struct sockaddr_in server, client; // creating a socket address structure: structure contains ip address and port number
WORD wVersionRequested;
WSADATA wsaData;
int len;
printf("Initializing Winsock\n");
wVersionRequested = MAKEWORD (2, 2);
iResult = WSAStartup (wVersionRequested, &wsaData);
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
// create socket
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
printf("Could not Create Socket\n");
//return 0;
}
printf("Socket Created\n");
// create socket address of the server
memset( &server, 0, sizeof(server));
// IPv4 - connection
server.sin_family = AF_INET;
// accept connections from any ip adress
server.sin_addr.s_addr = htonl(INADDR_ANY);
// set port
server.sin_port = htons(18001);
//Binding between the socket and ip address
if(bind (sock, (struct sockaddr *) &server, sizeof(server)) < 0)
{
printf("Bind failed with error code: %d", WSAGetLastError());
}
//Listen to incoming connections
if(listen(sock,3) == -1){
printf("Listen failed with error code: %d", WSAGetLastError());
}
printf("Server has been successfully set up - Waiting for incoming connections");
for(;;){
len = sizeof(client);
fd = accept(sock, (struct sockaddr*) &client, &len);
if (fd < 0){
printf("Accept failed");
close(sock);
}
//echo(fd);
printf("\n Process incoming connection from (%s , %d)", inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//closesocket(fd);
}
}
The server code is accepting a connection from the client via the ip address and the port number. It is sending SYN/ACK to the client only during the first connection and It is sending like below for the second time: RST / ACK (it is resetting during the second time).
Could anyone tell me what is the error in the above code ??
Look at Accept multiple subsequent connections to socket
Here is a quote: "To service multiple clients, you need to avoid blocking I/O -- i.e., you can't just read from the socket and block until data comes in."

"resource temporarily unavailable" in recv in socket programming

I want to read and write over Wanpipe driver (a network device driver for Sangoma cards) via socket programming but i get this message error: "resource temporarily unavailable". The card is working and i see it send and receive packets in ifconfig. I have included my code and would be very pleased if somebody help me in this.
A related question: I set the socket to blocking mode but the recv message does not block? how could i block the recv?
int main(void)
{
int sd;
int buflen=WP_HEADER + MAX_PACKET;
char buf[buflen];
struct wan_sockaddr_ll sa;
sd = socket(AF_WANPIPE, SOCK_RAW,0);
if (sd < 0) /* if socket failed to initialize, exit */
{
perror("Error Creating Socket");
exit(1);
}
printf("Socket Descriptor:%d\n",sd);
memset(&sa,0,sizeof(struct wan_sockaddr_ll));
strncpy((char*)sa.sll_card,"wanpipe1",sizeof(sa.sl l_card));
strncpy((char*)sa.sll_device,"w1g1",sizeof(sa.sll_ device));
sa.sll_protocol = htons(PVC_PROT);
sa.sll_family = AF_WANPIPE;
if(bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
{
perror("error bind failed");
close(sd);
exit(1);
}
int data=0;
int ret=ioctl(sd,FIONBIO,&data);
if (ret < 0)
{
perror("ioctl error!");
close(sd);
return 1;
}
fd_set read_fds;
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&read_fds);
FD_SET(sd,&read_fds);
if(select(sd+1, &read_fds, NULL, NULL, &timeout) < 0)
{
perror("select() error!");
exit(1);
}
if (FD_ISSET(sd,&read_fds))
printf("There is data for reading\n");
else
printf("There is no data for reading\n");*/
// MSG_WAITALL | MSG_PEEK | MSG_OOB
int r=recv(sd,buf,buflen,0);
if (r < 0)
{
perror("Wanpipe raw socket reading");
close(sd);
exit(1);
}
printf("\nNumber of bytes read into the buffer: %d",r);
printf("\nThe read buffer: ");
puts(buf);
close(sd);
}
thank you in advance.

Resources