The following code is part of a C-RAN program where the server code is part of a larger module code that serves as an interface to communicate with the Remote Radio Head.
To simplify it, let us treat it as a simple client-server service. The server will send an audio file through a specific UDP socket (port1) to the client and this one will play it back. Then, the client sends an ACK to the server through another socket (port2). Afterwards, the client starts recording audio and sends the datagrams via socket (port3). FInally, the client sends again a last ack through socket (port4).
Why so many different sockets? It is mandatory, full-duplex is a must, let's leave it here.
If I "comment" the code where the client records audio, communications between client-server are fine, audio is played back properly, no choppy playback. As soon as I activate the client code for recording, the playback starts to be choppy and the recording is a mess.
I've tried all sort of tests, like reducing the number of sockets, increasing the buffer sizes, removing one or all the acks, etc..
My network programming skills are so so, thus I don't know what else I can do. One thing is for sure, either there's a datagram flow issue or the ALSA lib drives can not cope, the latter I doubt it.
The way the code is in here, audio playback will be fine, all code parts for recording are commented in the client code.
Client:
//COMPILE: gcc -o client client.c -lasound
//EXECUTE (only playback): ./client
//EXECUTE (playback&recording): ./client > recordedFilename.raw
/*
-------- Remote Radio Head - client side
Remote Radio Head recieves audio from a dedicated socket from BBU, may process it or not
and sends audio through a different socket to the BBU.
*/
/* DEPENDENCIES */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <alsa/asoundlib.h>
#include <dirent.h>
/* CONSTANTS */
#define PORT1 8070
#define PORT2 8080
#define PORT3 8090
#define PORT4 8100
#define MAXLINE 1024
#define FRAME_SIZE 512
#define PAYLOAD_SIZE (FRAME_SIZE * 4)
/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
// START
int main(int argc, char *argv[])
{
// Sockets variables
int sockfd1, sockfd2, sockfd3, sockfd4;
int len, n;
int i = 1;
const char* ACK_play = "playback_ack";
const char* ACK_rec = "recording_ack";
const char* START_BROADCAST = "CLIENT: Connected !!!";
const char* MSG = "\nCLIENT: sending recording datagrams\n";
char buffer[MAXLINE];
struct sockaddr_in servaddr1, servaddr2, servaddr3, servaddr4;
// ALSA playback variables
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
// ALSA recording variables
long loops;
int rc2;
int size2;
snd_pcm_t *handle2;
snd_pcm_hw_params_t *params2;
unsigned int val2;
int dir2;
snd_pcm_uframes_t frames2;
char *buffer2;
/*********************************************
* ALSA PLAYBACK DRIVERS SETUP *
*********************************************/
// Open PCM device for playback.
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
// Allocate a hardware parameters object.
snd_pcm_hw_params_alloca(¶ms);
// Fill it in with default values.
snd_pcm_hw_params_any(handle, params);
// Set the desired hardware parameters.
// Interleaved mode
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
// Signed 16-bit little-endian format
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
// Two channels (stereo)
snd_pcm_hw_params_set_channels(handle, params, 2);
// playback sampling rate
val = 48000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
// Set period size to 32 frames.
frames = FRAME_SIZE;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
// Write the parameters to the driver
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}
/*********************************************
* ALSA RECORDING DRIVERS SETUP *
*********************************************/
// Open PCM device for recording (capture).
rc2 = snd_pcm_open(&handle2, "default", SND_PCM_STREAM_CAPTURE, 0);
if (rc2 < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc2));
exit(1);
}
// Allocate a hardware parameters object.
snd_pcm_hw_params_alloca(¶ms2);
// Fill it in with default values.
snd_pcm_hw_params_any(handle2, params2);
// Set the desired hardware parameters.
// Interleaved mode
snd_pcm_hw_params_set_access(handle2, params2, SND_PCM_ACCESS_RW_INTERLEAVED);
// Signed 16-bit little-endian format
snd_pcm_hw_params_set_format(handle2, params2, SND_PCM_FORMAT_S16_LE);
// Two channels (stereo)
snd_pcm_hw_params_set_channels(handle2, params2, 2);
// recording sampling rate
val2 = 48000;
snd_pcm_hw_params_set_rate_near(handle2, params2, &val, &dir2);
// Set period size to 32 frames.
frames2 = FRAME_SIZE;
snd_pcm_hw_params_set_period_size_near(handle2, params2, &frames, &dir2);
// Write the parameters to the driver
rc2 = snd_pcm_hw_params(handle2, params2);
if (rc2 < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc2));
exit(1);
}
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
// --------------- SOCKET 1
if ((sockfd1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket 1 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr1, 0, sizeof(servaddr1));
// Filling server 1 information
servaddr1.sin_family = AF_INET; // ipv4
servaddr1.sin_addr.s_addr = INADDR_ANY; // Any server
servaddr1.sin_port = htons(PORT1);
// --------------- SOCKET 2
if ((sockfd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket 2 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr2, 0, sizeof(servaddr2));
// Filling server 2 information
servaddr2.sin_family = AF_INET; // ipv4
servaddr2.sin_addr.s_addr = INADDR_ANY; // Any server
servaddr2.sin_port = htons(PORT2);
// --------------- SOCKET 3
if ((sockfd3 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket 3 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr3, 0, sizeof(servaddr3));
// Filling server 2 information
servaddr3.sin_family = AF_INET; // ipv4
servaddr3.sin_addr.s_addr = INADDR_ANY; // Any server
servaddr3.sin_port = htons(PORT3);
// --------------- SOCKET 4
if ((sockfd4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket 4 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr4, 0, sizeof(servaddr4));
// Filling server 2 information
servaddr4.sin_family = AF_INET; // ipv4
servaddr4.sin_addr.s_addr = INADDR_ANY; // Any server
servaddr4.sin_port = htons(PORT4);
/********************************************
* SEND MSG TO SERVER *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
sendto(sockfd1, START_BROADCAST, strlen(START_BROADCAST), 0, (const struct sockaddr *)&servaddr1, sizeof(servaddr1));
printf("CLIENT: Waiting for audio!!!\n");
/********************************************
* RECIEVE MSG FROM SERVER *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
/* We allocate memory to store the payload of the incoming datagram. */
char *play_load = (char *)malloc(PAYLOAD_SIZE * sizeof(char));
char *rec_load = (char *)malloc(PAYLOAD_SIZE * sizeof(char));
///////////////////////////////////////////////////////////////////////////////////////
/* HERE CLIENT (RRH) RECIEVES AND SENDS DATA TO SERVER (BBU) NEEDS TO BE FULL DUPLEX */
///////////////////////////////////////////////////////////////////////////////////////
while (1)
{
//----------------------------------------- RECIEVE AUDIO DATAGRAMS
len = PAYLOAD_SIZE;
int playback_bytes = recvfrom(sockfd1, (void *)play_load, PAYLOAD_SIZE, MSG_WAITALL, (struct sockaddr *)&servaddr1, &len);
//----------------------------------------- ALSA PLAYBACK DATAGRAMS
rc = snd_pcm_writei(handle, play_load, frames);
// if true, audio is playing
if (rc>0 && i>0){
printf("CLIENT: Playing audio!!!\n");
i--;
}
if (rc == -EPIPE) {
// EPIPE means underrun
fprintf(stderr, "playback underrun occurred\n");
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr, "playback error from writei: %s\n", snd_strerror(rc));
} else if (rc != (int)frames) {
fprintf(stderr, "playback short write, write %d frames\n", rc);
}
//------------------------------------------- SEND PLAYBACK ACK
sendto(sockfd2, ACK_play, strlen(ACK_play), 0, (const struct sockaddr *)&servaddr2, len);
printf("1 -sending audio playback ACK\n");
//------------------------------------------- ALSA RECORDING DATAGRAMS
/*//¡¡¡uncomment to record audio and send it to server!!!
rc2 = snd_pcm_readi(handle2, rec_load, frames2);
if (rc2 == -EPIPE) {
// EPIPE means overrun
fprintf(stderr, "recording overrun occurred\n");
snd_pcm_prepare(handle2);
} else if (rc2 < 0) {
fprintf(stderr, "recording error from read: %s\n", snd_strerror(rc2));
} else if (rc2 != (int)frames2) {
fprintf(stderr, "recording short read, read %d frames\n", rc2);
}
//rc2 = write(1, buffer2, size2); //Only to see recorded data// Only uncomment to save recorded audio into a raw file
if (rc2 != size2)
fprintf(stderr, "recording short write: wrote %d bytes\n", rc2);
*/
//------------------------------------------- SEND RECORDING DATAGRAMS
sendto(sockfd3, MSG, strlen(MSG), 0, (const struct sockaddr *)&servaddr3, len);
//sendto(sockfd3, rec_load, PAYLOAD_SIZE, 0, (const struct sockaddr *)&servaddr3, len);//uncomment to send audio recorded datagrams
printf("2 -sending audio recording datagrams\n");
//------------------------------------------- SEND RECORDING ACK
sendto(sockfd4, ACK_rec, strlen(ACK_rec), 0, (const struct sockaddr *)&servaddr4, len);
printf("3 -sending audio recording ACK\n\n");
//------------------------------------------- PLAYBACK FINISH
if(playback_bytes<2048){
printf("END PLAYBACK DATAGRAMS %d\n",playback_bytes);
break;
}
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
close(sockfd1); //close file descriptor
close(sockfd2);
close(sockfd3);
close(sockfd4);
return 0;
}
//END
server:
//COMPILE: gcc -o server server.c
//EXECUTE: ./server < filename.wav
/*
-----Implementation Base Band Unit server side
BBU sends audio through a dedicated sockets to RRH
and recieves it throught a different socket from it
through a dedicated socket to process it afterwards
in other modules.
*/
/* DEPENDENCIES */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/* CONSTANTS */
#define PORT1 8070
#define PORT2 8080
#define PORT3 8090
#define PORT4 8100
#define MAXLINE 1024
#define PAYLOAD_SIZE 2048
// START
int main(int argc, char *argv[])
{
int sockfd1, sockfd2, sockfd3, sockfd4;
int len, len2, len3, n, ad;
char buffer1[MAXLINE];
char buffer2[MAXLINE];
const char* msg = "\nSERVER: Sending audio complete";
const char* ACK = "ack";
struct sockaddr_in servaddr1, cliaddr1;
struct sockaddr_in servaddr2, cliaddr2;
struct sockaddr_in servaddr3, cliaddr3;
struct sockaddr_in servaddr4, cliaddr4;
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
// SOCKET 1
if ((sockfd1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
/*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket 1 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr1, 0, sizeof(servaddr1)); // Allocate memory for structure
memset(&cliaddr1, 0, sizeof(cliaddr1));
// Filling server information
servaddr1.sin_family = AF_INET; // IPv4
servaddr1.sin_addr.s_addr = INADDR_ANY; // Any client
servaddr1.sin_port = htons(PORT1);
// SOCKET 2
if ((sockfd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
/*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket 2 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr2, 0, sizeof(servaddr2)); // Allocate memory for structure
memset(&cliaddr2, 0, sizeof(cliaddr2));
// Filling server information
servaddr2.sin_family = AF_INET; // IPv4
servaddr2.sin_addr.s_addr = INADDR_ANY; // Any client
servaddr2.sin_port = htons(PORT2);
// SOCKET 3
if ((sockfd3 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
/*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket 3 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr3, 0, sizeof(servaddr3)); // Allocate memory for structure
memset(&cliaddr3, 0, sizeof(cliaddr3));
// Filling server information
servaddr3.sin_family = AF_INET; // IPv4
servaddr3.sin_addr.s_addr = INADDR_ANY; // Any client
servaddr3.sin_port = htons(PORT3);
// SOCKET 4
if ((sockfd4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
/*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket 4 creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr4, 0, sizeof(servaddr4)); // Allocate memory for structure
memset(&cliaddr4, 0, sizeof(cliaddr4));
// Filling server information
servaddr4.sin_family = AF_INET; // IPv4
servaddr4.sin_addr.s_addr = INADDR_ANY; // Any client
servaddr4.sin_port = htons(PORT4);
/********************************************
* BIND THE SOCKET WITH THE SERVER ADDRESS *
********************************************/
//sockfd: File descriptor of socket to be binded
//addr: Structure in which address to be binded to is specified
//addrlen: Size of addr structure
// BIND SOCKET 1
if (bind(sockfd1, (const struct sockaddr *)&servaddr1, sizeof(servaddr1)) < 0) {
perror("bind socket 1 failed");
exit(EXIT_FAILURE);
}
// BIND SOCKET 2
if (bind(sockfd2, (const struct sockaddr *)&servaddr2, sizeof(servaddr2)) < 0) {
perror("bind socket 2 failed");
exit(EXIT_FAILURE);
}
// BIND SOCKET 3
if (bind(sockfd3, (const struct sockaddr *)&servaddr3, sizeof(servaddr3)) < 0) {
perror("bind socket 3 failed");
exit(EXIT_FAILURE);
}
// BIND SOCKET 4
if (bind(sockfd4, (const struct sockaddr *)&servaddr4, sizeof(servaddr4)) < 0) {
perror("bind socket 4 failed");
exit(EXIT_FAILURE);
}
/********************************************
* RECEIVE MSG FROM CLIENT *
/*******************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
len = PAYLOAD_SIZE; // len is value/result
printf("\nSERVER: Waiting for client connection...\n");
n = recvfrom(sockfd1, (char *)buffer1, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr1, &len);
buffer1[n] = '\0';
printf("%s\n", buffer1);
/********************************************
* SEND MSG TO CLIENT *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
// We allocate memory for the datagrams payload
char* payload = (char*)malloc(PAYLOAD_SIZE * sizeof(char));
printf("SERVER: Sending audio...\n");
//////////////////////////////////////////////////////////////////////////
/* HERE SERVER (BBU) SENDS DATA TO CLIENT (RRH) NEEDS TO BE FULL DUPLEX */
//////////////////////////////////////////////////////////////////////////
while (1)
{
//---------------------------------------- READING AUDIO FILE
int playback = read(0, (void*)payload, PAYLOAD_SIZE);
if (playback <= 0){// If true, audio send end
printf("%s\n\n", msg);
break;
}
//--------------------------------------- SEND AUDIO PLAYBACK DATAGRAMS
sendto(sockfd1, (void*)payload, playback, 0, (struct sockaddr *)&cliaddr1, len);
printf(" 1- Sending audio playback datagrams\n");
//--------------------------------------- RECIEVE AUDIO PLAYBACK ACK
len2 = sizeof(cliaddr2);
n = recvfrom(sockfd2, (char *)buffer1, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr2, &len);
buffer1[n] = '\0';
//printf("CLIENT: %s\n", buffer1);// to check incoming ack
printf(" 2- Receiving audio playback ACK\n");
//--------------------------------------- RECIEVE AUDIO RECORDING DATAGRAMS
ad = recvfrom(sockfd3, (char *)buffer2, PAYLOAD_SIZE, MSG_WAITALL, (struct sockaddr *)&cliaddr3, &len);
printf(" 3- Receiving audio recording datagrams\n");
buffer2[ad] = '\0';
printf("%s\n", buffer2);
//--------------------------------------- RECEIVE RECORDING ACK
n = recvfrom(sockfd4, (char *)buffer1, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr4, &len);
buffer1[n] = '\0';
//printf("CLIENT: %s\n", buffer1);//to check incoming ack
printf(" 4- Receiving audio recording ACK\n");
}
return 0;
}
//END
Related
I'm trying to send a .wav file via UDP sockets from the server to the client and playing it back from the client side.
The file is 48kHz and 16 bits and has duration of 25 sec.
Since the server.c is a small code section inside a larger C-RAN code module I'm checking it by passing a wav file from the stdin:
./SERVER < filename.wav
and then as soon as I execute the client.c, the server starts sending the datagrams and the client.c starts playing them back. Everything is fine bu the audio is played back at a much faster speed, its like playing it back at twice the correct speed. Sometimes it starts playing back correctlyI but after a few seconds it speeds up.
I've passed a large .txt file instead of the .wav file and the client recieves all the .txt file, from start to end.
Is there anything that I'm missing that I should take into account in order to playback the audio wav file correctly?
client.c code:
// Remote Radio Head - client side
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <alsa/asoundlib.h>
#include <dirent.h>
/* CONSTANTS */
#define PORT 8080
#define MAXLINE 1024
#define PAYLOAD_SIZE 128
/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
/*************************************************
* DRIVER CODE *
*************************************************/
int main(int argc, char *argv[]) {
// Sockets variables
int sockfd;
char buffer1[MAXLINE];
char *data = "Hello from client, waiting for audio to playback";
struct sockaddr_in servaddr;
// ALSA playback variables
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer2;
/*********************************************
* ALSA DRIVERS SETUP *
*********************************************/
// Open PCM device for playback.
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
// Allocate a hardware parameters object.
snd_pcm_hw_params_alloca(¶ms);
// Fill it in with default values.
snd_pcm_hw_params_any(handle, params);
// Set the desired hardware parameters.
// Interleaved mode
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
// Signed 16-bit little-endian format
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
// Two channels (stereo)
snd_pcm_hw_params_set_channels(handle, params, 2);
// bits/second sampling rate
val = 48000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
// Set period size to 32 frames.
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
// Write the parameters to the driver
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}
// Use a buffer large enough to hold one period
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
size = frames * 4; // 2 bytes/sample, 2 channels
buffer2 = (char *) malloc(size);
// We want to loop for 25 seconds
snd_pcm_hw_params_get_period_time(params, &val, &dir);
// 25 seconds in microseconds divided by period time
loops = 25000000/val;
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET; //ipv4
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY; //Any server
/********************************************
* SEND MSG TO SERVER *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
sendto(sockfd, (const char *)data, strlen(data), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
printf("Waiting for audio!!!\n");
/********************************************
* RECIEVE MSG FROM SERVER *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
int n, len;
/* We allocate memory to store the payload of the incoming datagram. */
char *payload = (char *)malloc(PAYLOAD_SIZE*sizeof(char));
while(1){
//We receive datagram
int bytes_read = recvfrom(sockfd, (void *)payload, PAYLOAD_SIZE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
fprintf(stderr,"r");
fflush(stderr);
// We write the datagram to stdout.
//write(1, (void *)payload, bytes_read);
//fprintf(stderr,"w");
//fflush(stderr);
while (loops > 0) {
loops--;
rc = read(sockfd, buffer2, size); //Read audio file
if (rc == 0)
{
fprintf(stderr, "end of file on input\n");
break;
}
else if (rc != size)
{
fprintf(stderr, "short read: read %d bytes\n", rc);
}
rc = snd_pcm_writei(handle, buffer2, frames);
if (rc == -EPIPE)
{
// EPIPE means underrun
fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(handle);
}
else if (rc < 0)
{
fprintf(stderr, "error from writei: %s\n",
snd_strerror(rc));
}
else if (rc != (int)frames)
{
fprintf(stderr, "short write, write %d frames\n", rc);
}
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer2);
buffer1[n] = '\0';
printf("Server : %s\n", buffer1);
}
//n = recvfrom(sockfd, (char *)buffer1, MAXLINE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
close(sockfd); //close file descriptor
return 0;
}
server.c
// Server side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
//CONSTANTS
#define PORT 8080
#define MAXLINE 1024
#define PAYLOAD_SIZE 128
// Driver code
int main(int argc, char *argv[]) {
int sockfd;
char buffer[MAXLINE];
char *msg = "Hello from server, ready to send audio";
struct sockaddr_in servaddr, cliaddr;
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { /*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr)); //Allocate memory for structure
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;//Any client
servaddr.sin_port = htons(PORT);
/********************************************
* BIND THE SOCKET WITH THE SERVER ADDRESS *
********************************************/
//sockfd: File descriptor of socket to be binded
//addr: Structure in which address to be binded to is specified
//addrlen: Size of addr structure
if ( bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 )
{
perror("bind failed");
exit(EXIT_FAILURE);
}
/********************************************
* RECIEVE MSG FROM CLIENT *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
int len, n;
len = sizeof(cliaddr); //len is value/resuslt
n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, ( struct sockaddr *) &cliaddr, &len);
buffer[n] = '\0';
printf("Client : %s\n", buffer);
/********************************************
* SEND MSG TO CLIENT *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
// We allocate memory for the datagrams payload
char *payload = (char *)malloc(PAYLOAD_SIZE*sizeof(char));
printf("Sending audio in 3, 2, 1.....\n");
while(1){
// Reading from the std in
int bytes_read = read(0, (void *)payload, PAYLOAD_SIZE);
fprintf(stderr, "r");
fflush(stderr);
if(bytes_read < 1) break;
// We write the datagram to stdout.
write(1, (void *)payload, bytes_read);
fprintf(stderr, "w");
fflush(stderr);
//Sending datagram
sendto(sockfd, (void *)payload, bytes_read, 0, (struct sockaddr *) &cliaddr, len);
fprintf(stderr, "s");
fflush(stderr);
}
//sendto(sockfd, (const char *)msg, strlen(msg), MSG_CONFIRM, (const struct sockaddr *) &cliaddr, len);
return 0;
}
The problem in your implementation is that the transfer of the music data is one way from the server to the client. When the client sends the first request, the server starts broadcasting the audio stream as fast as it can. As a result the client will loose some packets. If only one in two packets gets actually written to the audio device, it looks like that the music is played twice as fast. You can easily see that you are loosing packets if you sum all the bytes_read in the client. It will be much smaller than the actual file size.
Also it's not clear why in the client you first recvfrom socket into payload and then read into buffer2. In theory only the first operation is required and then you write from payload to the audio device.
If you want to implement the streaming in the proper way you need to implement a proper buffering solution in the client and also some speed throttling on the server, to avoid sending data at a speed much higher than needed.
If you want to fix your code in an easy way, one possibility would be to add an ACK that the client sends to the server after receiving one packet. The server will wait for the client ACK before sending the next packet. This will more or less make the UDP protocol a TCP protocol.
I have modified your code a little bit to show you what I mwan. With this code you are able to playback correctly the wav file. It's not perfect, but at least it should give you an idea of what is the problem with your code
server.c
// gcc -o server server.c
// Server side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
// CONSTANTS
#define PORT 8080
#define MAXLINE 1024
#define PAYLOAD_SIZE 2048
int main(int argc, char *argv[])
{
int sockfd;
char buffer[MAXLINE];
const char* msg = "SERVER: Sending audio complete";
struct sockaddr_in servaddr, cliaddr;
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
/*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr)); // Allocate memory for structure
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY; // Any client
servaddr.sin_port = htons(PORT);
/********************************************
* BIND THE SOCKET WITH THE SERVER ADDRESS *
********************************************/
//sockfd: File descriptor of socket to be binded
//addr: Structure in which address to be binded to is specified
//addrlen: Size of addr structure
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
/********************************************
* RECIEVE MSG FROM CLIENT *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
int len, n;
len = sizeof(cliaddr); // len is value/result
printf("Waiting for client connection...\n");
n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);
buffer[n] = '\0';
printf("%s\n", buffer);
/********************************************
* SEND MSG TO CLIENT *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
// We allocate memory for the datagrams payload
char* payload = (char*)malloc(PAYLOAD_SIZE * sizeof(char));
printf("Sending audio...\n");
while (1) {
// Reading from the stdin
int bytes_read = read(0, (void*)payload, PAYLOAD_SIZE);
if (bytes_read <= 0)
break;
// Sending datagram
sendto(sockfd, (void*)payload, bytes_read, 0, (struct sockaddr *)&cliaddr, len);
// Waiting for ACK
n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);
}
return 0;
}
client.c
// gcc -o client client.c -lasound
// Remote Radio Head - client side
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <alsa/asoundlib.h>
#include <dirent.h>
/* CONSTANTS */
#define PORT 8080
#define MAXLINE 1024
#define FRAME_SIZE 512
#define PAYLOAD_SIZE (FRAME_SIZE * 4)
/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
int main(int argc, char *argv[])
{
// Sockets variables
int sockfd;
const char* ACK = "ack";
const char* START_BROADCAST = "CLIENT: waiting for audio to playback";
struct sockaddr_in servaddr;
// ALSA playback variables
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
/*********************************************
* ALSA DRIVERS SETUP *
*********************************************/
// Open PCM device for playback.
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
// Allocate a hardware parameters object.
snd_pcm_hw_params_alloca(¶ms);
// Fill it in with default values.
snd_pcm_hw_params_any(handle, params);
// Set the desired hardware parameters.
// Interleaved mode
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
// Signed 16-bit little-endian format
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
// Two channels (stereo)
snd_pcm_hw_params_set_channels(handle, params, 2);
// bits/second sampling rate
val = 48000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
// Set period size to 32 frames.
frames = FRAME_SIZE;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
// Write the parameters to the driver
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET; // ipv4
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY; // Any server
/********************************************
* SEND MSG TO SERVER *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
sendto(sockfd, START_BROADCAST, strlen(START_BROADCAST), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr));
printf("Waiting for audio!!!\n");
/********************************************
* RECIEVE MSG FROM SERVER *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
int n, len;
/* We allocate memory to store the payload of the incoming datagram. */
char *payload = (char *)malloc(PAYLOAD_SIZE * sizeof(char));
while (1)
{
len = PAYLOAD_SIZE;
int bytes_read = recvfrom(sockfd, (void *)payload, PAYLOAD_SIZE, MSG_WAITALL, (struct sockaddr *)&servaddr, &len);
rc = snd_pcm_writei(handle, payload, frames);
if (rc == -EPIPE) {
// EPIPE means underrun
fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr, "error from writei: %s\n",
snd_strerror(rc));
} else if (rc != (int)frames) {
fprintf(stderr, "short write, write %d frames\n", rc);
}
// Send ACK
sendto(sockfd, ACK, strlen(ACK), 0, (const struct sockaddr *)&servaddr, len);
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
close(sockfd); //close file descriptor
return 0;
}
I'm trying to transmit from the client side a wav file through socket (UDP) and have the server playing the wav file by means of the ALSA library.
Since my coding level in C is very low I've first learnt how to set up a communication between client and server via sockets and interchange text messages.
Afterwards I've learnt how to play a wav file with ALSA by redirecting the stdin when running the playback app.
I've tried to joint both code (UDPServer.c + playback.c) files and when I pass the stdin to the UDPClient.c file it sends it to the UDPServer.c but it's just a pure tone instead of the actual music file that I'm trying to pass.
I don't know how exactly to pass the file into the UDPClient.c in order for it to send it to the socket to the server.
UDPServer.c
// Server side implementation of UDP client-server model
// Libraries
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <alsa/asoundlib.h>
// CONSTANTS
#define PORT 8080
#define MAXLINE 2048
/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
// FUNCTION DECLARATION
int audio_play (char signal);
// Main code
int main() {
int sockfd;
char buffer[MAXLINE];
char *hello = "AUDIO PLAYBACK FINISH";
struct sockaddr_in servaddr, cliaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
// Bind the socket with the server address
if ( bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) {
perror("bind failed");
exit(EXIT_FAILURE);
}
int len, n;
len = sizeof(cliaddr); //len is value/result
// Receive a mesage from the socket
n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, ( struct sockaddr *) &cliaddr, &len);
buffer[n] = '\0';
//printf("Client : %s\n", buffer);
audio_play(buffer);
// Send a message on the socket
sendto(sockfd, (const char *)hello, strlen(hello), MSG_CONFIRM, (const struct sockaddr *) &cliaddr, len);
printf("PLAYING AUDIO???.\n");
return 0;
}
// FUNCTIONS
int audio_play (char s)
{
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer;
/* Open PCM device for playback. */
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(¶ms);
/* Fill it in with default values. */
snd_pcm_hw_params_any(handle, params);
/* Set the desired hardware parameters. */
/* Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(handle, params, 2);
/* 48000 bits/second sampling rate */
val = 48000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
/* Set period size to 32 frames. */
frames = 32;
snd_ pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}
/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
size = frames * 4; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);
/* We want to loop for 5 seconds */
snd_pcm_hw_params_get_period_time(params, &val, &dir);
/* 5 seconds in microseconds divided by
* period time */
loops = 5000000 / val;
while (loops > 0) {
loops--;
// rc = read(0, buffer, size);
rc = s;
if (rc == 0) {
fprintf(stderr, "end of file on input\n");
break;
} else if (rc != size) {
fprintf(stderr, "short read: read %d bytes\n", rc);
}
rc = snd_pcm_writei(handle, buffer, frames);
if (rc == -EPIPE) {
/* EPIPE means underrun */
fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr, "error from writei: %s\n", snd_strerror(rc));
} else if (rc != (int)frames) {
fprintf(stderr, "short write, write %d frames\n", rc);
}
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
}
UDPClient.c
// Client side implementation of UDP client-server model
// Libraries
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
// CONSTANTS
#define PORT 8080
#define MAXLINE 2048
// Main code
int main(int argc, char **argv) {
int sockfd;
char buffer[MAXLINE];
char *data = "Here is where the file of audio should be?";
struct sockaddr_in servaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
int n, len;
// Send a message on the socket
sendto(sockfd, (const char *)data, strlen(data), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
printf("AUDIO SENT.\n");
// Receive a message from the socket
n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
buffer[n] = '\0';
printf("Server : %s\n", buffer);
close(sockfd);
return 0;
}
End goal:
For the parent process of the server to know what clients join or leave the multicast group. So far I've only tried to check on clients joining, for I assume checking on those leaving is a similar matter.
My method(s) so far:
Checking for changes in the socket of the multicast group through select().
After joining, the client executes a sendto() directed to the (parent process of) the server program. The select() in the server is meant to recognize any changes, but apparently doesn't, and thus retval != 0 is never true.
Results so far
I Have tried with many different IP addresses and constants such as INADDR_ANY, but I've only managed as far as sending a message back to the client through the multicast, and this one interpret it as if the server-program had sent it. The most common result is the server-program not receiving any message at all.
Here is my client code:
/* Receiver/client multicast Datagram*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_MSG 100
struct sockaddr_in localSock, servSock;
struct ip_mreq group;
int sd, n;
int datalen, mcastport;
char msg[MAX_MSG];
int main(int argc, char *argv[])
{
if(argc!=3) {
printf("usage : %s <address> <port>\n",argv[0]);
exit(0);
}
mcastport = atoi(argv[2]);
/* Create a datagram socket on which to receive. */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening datagram socket....OK.\n");
/* Enable SO_REUSEADDR to allow multiple instances of this */
/* application to receive copies of the multicast datagrams. */
{
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Setting SO_REUSEADDR error");
close(sd);
exit(1);
}
else
printf("Setting SO_REUSEADDR...OK.\n");
}
/* Bind to the proper port number with the IP address */
/* specified as INADDR_ANY. */
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(mcastport);
localSock.sin_addr.s_addr = INADDR_ANY;
if(bind(sd, (struct sockaddr*)&localSock, sizeof(localSock)))
{
perror("Binding datagram socket error");
close(sd);
exit(1);
}
else
printf("Binding datagram socket...OK.\n");
printf("Enter the group's name you want to join:\n");
scanf("%s", msg);
/* Join the multicast group 226.1.1.1 on the local IP address */
/* interface. Note that this IP_ADD_MEMBERSHIP option must be */
/* called for each local interface over which the multicast */
/* datagrams are to be received. */
group.imr_multiaddr.s_addr = inet_addr(argv[1]);
group.imr_interface.s_addr = inet_addr("127.0.0.1");
if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
perror("Adding multicast group error");
close(sd);
exit(1);
}
else
printf("Adding multicast group...OK.\n");
/* Initialize the group sockaddr structure with a */
/* group address of 226.1.1.1 and port given by user. */
memset((char *) &servSock, 0, sizeof(servSock));
servSock.sin_family = AF_INET;
servSock.sin_addr.s_addr = inet_addr(argv[1]);
servSock.sin_port = htons(mcastport);
if(sendto(sd, "", 1, 0, (struct sockaddr*) &servSock, sizeof(servSock)) < 0)
{perror("Sending datagram message error");}
else
printf("Sending datagram message...OK\n");
/* Read from the socket. */
if((n=read(sd, msg, MAX_MSG)) < 0)
{
perror("Reading datagram message error");
close(sd);
exit(1);
}
else
{
printf("Reading datagram message...OK.\n");
printf("The message from multicast server is: \"%s\"\n", msg);
}
msg[n] = '\0';
return 0;
}
This is my server program code:
/* Send Multicast Datagram code*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h> /* for strncpy, memset */
#define MAX_MSG 100
struct in_addr localInterface;
struct sockaddr_in groupSock, cliAddr;
int sd, mcastport, maxJoin, maxJoined = 0, pipefd[2], cliLen, cpid;
char msg[MAX_MSG], groupName[MAX_MSG];
int main (int argc, char *argv[ ])
{
/* check command line args */
if(argc < 2) {
printf("usage : %s <port> \n", argv[0]);
exit(1);
}
mcastport = atoi(argv[1]);
/* Create a datagram socket on which to send. */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening the datagram socket...OK\n");
/* Initialize the group sockaddr structure with a */
/* group address of 225.1.1.1 and port given by user. */
memset((char *) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
groupSock.sin_port = htons(mcastport);
printf("Create a group: ");
scanf("%s", groupName);
printf("Maximum number of clients that can join the group? ");
scanf("%d", &maxJoin);
/* Disable loopback so you do not receive your own datagrams.
{
char loopch = 0;
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0)
{
perror("Setting IP_MULTICAST_LOOP error");
close(sd);
exit(1);
}
else
printf("Disabling the loopback...OK.\n");
}
*/
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local, */
/* multicast capable interface. */
printf("Setting the local interface...");
localInterface.s_addr = inet_addr("127.0.0.1");
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
{
perror("error");
exit(1);
}
else
printf("OK\n");
if((cpid = fork()) == 0) //child process --sends messages
{
/* Send a message to the multicast group specified by the*/
/* groupSock sockaddr structure. */
printf("Enter a message to send: \n");
scanf("%s", msg);
if(sendto(sd, msg, strlen(msg)+1, 0, (struct sockaddr*)&groupSock, sizeof(groupSock)) < 0)
{perror("Sending datagram message error");}
else
printf("Sending datagram message...OK\n");
/* Try the re-read from the socket if the loopback is not disable
if(read(sd, databuf, datalen) < 0)
{
perror("Reading datagram message error\n");
close(sd);
exit(1);
}
else
{
printf("Reading datagram message from client...OK\n");
printf("The message is: %s\n", databuf);
}
*/
exit(EXIT_SUCCESS);
}
else //parent process --checks for JOINs and QUITs
{
fd_set rfds;
struct timeval tv;
int retval, status;
while (waitpid(cpid, &status, WNOHANG) != cpid)
{
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(sd, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(sd+1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval != 0)
{
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
cliLen = sizeof(cliAddr);
int n;
if((n = recvfrom(sd, msg, MAX_MSG, 0, (struct sockaddr *) &cliAddr,&cliLen)) == -1)
perror("Some bullshit happened");
msg[n] = '\0';
printf("Client IP:port is: %s:%d", inet_ntoa(cliAddr.sin_addr), (int) ntohs(cliAddr.sin_port));
} else
{printf("no data.\n");}
}
exit(0);
}
return 0;
}
Possible solutions I haven't tried:
Maybe I shouldn't use the multicast group to send information client->server, but rather another type of connection? I'm just guessing here. I know you guys don't like doing anyone's work.
I have been at this "simple" issue for several hours and tried reading everywhere I could, including this question which seems very similar, but I haven't managed to solve it in any way. I'm completely striking out here.
If the client sends to the multicast group and the server expects to read that, the server needs to join the multicast group as well.
It isn't a great solution, as all the other client members will also receive that multicast.
It would make more sense for the client to first receive a multicast from the server, and then to respond to the server's address, which is provided via the result arguments of recvfrom().
Two points that may help you:
There is no need to use multicasting if your communication is only local (you use 127.0.0.1 in your code)
Multicast groups are joined by network interfaces, not by processes. Once a network interface has joined a multicast interface, a second joining of the same network interface to the same multicast group will change nothing. Even when it's another process on the same host that requests the second
joining.
have a stand alone PC running VS6 on WinXP - yes ancient technology.
I am porting a C code app from Linux.
Stuck on multicast problem
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h> /* for printf(), fprintf() */
#include <conio.h>
#include <winsock.h> /* for socket(),... */
#include <stdlib.h> /* for exit() */
#define MAXRECVSTRING 255 /* Longest string to receive */
int main(int argc, char* argv[])
{
char msg[100];
char loopchar = 0;
int iOptVal = 0;
char iOptVal2 = 1;
int iLenOptVal = sizeof(int);
int result = -1;
int retval = -1;
int set_option_on = 1;
int sock; /* Socket */
struct sockaddr_in multicastAddr; /* Multicast Address */
char *multicastIP; /* IP Multicast Address */
unsigned short multicastPort; /* Port */
char recvString[MAXRECVSTRING+1]; /* Buffer for received string */
unsigned int recvStringLen; /* Length of received string */
struct ip_mreq multicastRequest; /* Multicast address join structure */
WSADATA wsaData; /* Structure for WinSock setup communication */
if (argc != 3) /* Test for correct number of arguments */
{
fprintf(stderr,"Usage: %s <Multicast IP> <Multicast Port>\n", argv[0]);
exit(1);
}
multicastIP = argv[1]; /* First arg: Multicast IP address (dotted quad) */
//WW Silversilsied //WW SilversilsiedmulticastIP = inet_addr("224.000.010.101"); /* First arg: Multicast IP address (dotted quad) */
//multicastIP = inet_addr("224.000.010.101"); /* First arg: Multicast IP address (dotted quad) */
multicastPort = atoi(argv[2]);/* Second arg: Multicast port */
//multicastPort = 6600);/* Second arg: Multicast port */
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) /* Load Winsock 2.0 DLL */
{
fprintf(stderr, "WSAStartup() failed");
exit(1);
}
/* Create a best-effort datagram socket using UDP */
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
printf("\nsocket() failed - error = %d\n", sock);
}
/* Construct bind structure */
memset(&multicastAddr, 0, sizeof(multicastAddr)); /* Zero out structure */
multicastAddr.sin_family = AF_INET; /* Internet address family */
multicastAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
multicastAddr.sin_port = htons(multicastPort); /* Multicast port */
/* Bind to the multicast port */
retval = bind(sock, (struct sockaddr *) &multicastAddr, sizeof(multicastAddr));
if (retval < 0)
{
printf("\nbind() failed - error = %d\n", retval);
}
#if 0
/* Specify the multicast group */
multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastIP);
/* Accept multicast from any interface */
multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
/* Join the multicast address */
//if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&set_option_on, sizeof(set_option_on));
if (result < 0)
{
printf("\n setsockopt() failed");
perror(" setsockopt ");
}
/* Receive a single datagram from the server */
while(1)
{
if ((recvStringLen = recvfrom(sock, recvString, MAXRECVSTRING, 0, NULL, 0)) < 0)
{
printf("\nrecvfrom() failed");
}
Sleep(1000);
}
#else
strcpy(msg,"default test message");
struct sockaddr_in address1;
int len;
int bytes_sent = -1;
memset(&address1, 0, sizeof(address1));
address1.sin_family = AF_INET;
address1.sin_port = multicastPort;
address1.sin_addr.s_addr = inet_addr(multicastIP);
//msg = "abcdefghijklmnopqrstuvwxyz";
//socklen_t len;
//size = strlen(msg);
if ((recvStringLen = recvfrom(sock, recvString, 1024, 0, (struct sockaddr*)&address1, &len)) < 0)
{
printf("\nrecvfrom() failed ");
perror(" recvfrom ");
}else{
recvString[recvStringLen] = '\0';
printf("Received: %d bytes %s\n", recvStringLen,recvString); /* Print the received string */
perror(" received from ");
}
bytes_sent = sendto(sock,
msg,
sizeof(msg),
0,
(struct sockaddr*)&address1,
sizeof(address1));
printf("bytes sent = %d \n", bytes_sent);
printf("size of msg = %d \n ", sizeof(msg));
perror( "sendto ");
#endif
getch();
closesocket(sock);
WSACleanup(); /* Cleanup Winsock */
exit(0);
return 0;
}
When I single step through, I get a successful socket creation and valid socket descriptor.
I get a successful bind, at which point the port shows up as udp when I do a cmd line netstat -p UDP -a
setsockopt also completes without error.
When I step through the recvfrom it receives 2^24 bytes all of which are the same -52
The machine is stand alone, not on a network.
recvString is 256 bytes long, but your recvFrom() is asking for up to 1024 bytes.
I'm trying write a C program that sends an UDP packet to a given IP adress and waits for an ICMP response of a router telling that the time to live expired. It's kept very simple because I just want to understand the mechanism at first. What I need is someone checking my code giving feedback on what's wrong and what's missing. I'm very unexperienced at C-programming but I have to do it as an assignment - giving my best to understand it...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip.h>
// The packet length
#define PCKT_LEN 8192
// Source IP, source port, target IP, target port from the command line arguments
int main(int argc, char *argv[])
{
int send, recv, resp;
int ttl = 1; // Time to live
char buffer[PCKT_LEN];
// Destination address
struct sockaddr_in dst;
// ICMP Response
struct msghdr icmp;
memset(buffer, 0, PCKT_LEN);
// Create a raw socket with UDP protocol
if ((send = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
printf("Could not process socket() [send].\n");
return EXIT_FAILURE;
}
// ICMP Socket
if ((recv = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
{
printf("Could not process socket() [recv].\n");
return EXIT_FAILURE;
}
dst.sin_family = AF_INET;
dst.sin_addr.s_addr = inet_addr("74.125.39.106");
dst.sin_port = htons(60001);
// Define time to life
if(setsockopt(send, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0)
{
printf("Could not process setsockopt().\n");
return EXIT_FAILURE;
}
if(sendto(send, buffer, sizeof(buffer), 0, (struct sockaddr *) &dst, sizeof(dst)) < 0)
{
printf("Could not process sendto().\n");
return EXIT_FAILURE;
}
if((resp = recvmsg(recv, (struct msghdr *) &icmp, IP_RECVERR|MSG_ERRQUEUE)) < 0 )
{
printf("Could not process recvmsg().\n");
return EXIT_FAILURE;
}
close(send);
close(recv);
return 0;
}
I keep receiving "Could not process recvmsg()." and I have no clue anymore what else to try. I'd like to receive an ICMP answer and read its senders IP-Address.
Looking forward to helpfull hints.
Best regards!
I normally use
recvfrom(serversock, buf, 100, 0, (struct sockaddr *)&rcv,&size);
printf("Received packet from %s:%d\nData: %s\n\n", inet_ntoa(rcv.sin_addr), ntohs(rcv.sin_port), buf);