Transmitting audio through socket (UDP) but not playing back properly - c

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(&params);
/* 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;
}

Related

UDP sockets and full-duplex audio issues

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(&params);
// 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(&params2);
// 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

wav file over UDP not playing back properly in unix with Alsa-lib

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(&params);
// 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(&params);
// 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;
}

Sockets and threads in C

I am having a small problem when trying to implement a client-server program with multithreading on the side of the server. My idea is to have the server spin forever, accept a client when it can, and just send it to a client_handle() function using a thread.
Here is the problem: my server and client are using the code seen below. At the point of the initial response of the server, it fails in sending ALL_GOOD_CD. I'm not sure why this is happening, as I print out the socket fd of the client in a line before and it seems to match up with the file descriptor given to us at the time of acception.
One thought is that my socket id is not being passed to the thread correctly. My client never seems to receive the ALL_GOOD_CD (it is blocking on a recv() call after connecting with the server). I am new with threads and sockets, anything would help; thanks!
Here is the client code needed to run:
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <arpa/inet.h> /* for sockaddr_in and inet_addr() */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
void DieWithError(char *errorMessage); /* Error handling function */
int main(int argc, char *argv[])
{
int sock; /* Socket descriptor */
struct sockaddr_in server_addr; /* Server address */
unsigned short server_port; /* Server port */
char *server_ip; /* Server IP address (dotted quad) */
char server_response[300]; /* Buffer to hold response from the server */
char* username;
/* Test for correct number of arguments */
if (argc != 4) {
fprintf(stderr, "Usage: %s <server_ip> <server_port> <username>\n", argv[0]);
exit(1);
}
server_ip = argv[1]; /* Second arg: server IP address (dotted quad) */
server_port = atoi(argv[2]); /* Third arg: server port number */
username = argv[3]; /* Fourth arg: username */
/* Create a reliable, stream socket using TCP */
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
DieWithError("socket() failed");
/* Construct the server address structure */
memset(&server_addr, 0, sizeof(server_addr)); /* Zero out structure */
server_addr.sin_family = AF_INET; /* Internet address family */
server_addr.sin_addr.s_addr = inet_addr(server_ip); /* Server IP address */
server_addr.sin_port = htons(server_port); /* Server port */
/* Establish the connection to the server */
if (connect(sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0)
DieWithError("connect() failed, could not find server.");
printf("connected\n");
memset(&server_response, 0, 300);
if (recv(sock, server_response, 300, 0) < 0)
DieWithError("recv() for initial response failed");
printf("received initial reponse\n");
}
void DieWithError(char* errorMessage) {
fprintf(stderr, "Dying with error sadface: %s\n", errorMessage);
exit(1);
}
Here is the server code as minified as possible:
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <arpa/inet.h> /* for sockaddr_in and inet_addr() */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
#include <pthread.h> /* multithreading the clients! */
#define MAXMSGSIZE 150
#define MAXCLIENTS 5
#define TOO_MANY_CD 0
#define ALL_GOOD_CD 1
#define OTHER_BAD_CD 2
struct client {
char* username;
char** subs;
int socket;
char temp_msg[MAXMSGSIZE*2];
};
void DieWithError(char* errorMessage); /* Error handling function */
void handle_client(void* new_socket); /* Client handling function */
static struct client** clients;
static pthread_t* threads;
static pthread_mutex_t clients_mutex;
static pthread_mutex_t threads_mutex;
int main(int argc, char *argv[])
{
int server_sock; /* Server socket descriptor */
unsigned short server_port; /* Echo server port */
struct sockaddr_in server_addr; /* sockaddr_in struct to hold information about the server */
int server_addr_size; /* Size of server_addr struct in bytes */
int client_sock;
int empty_thread;
pthread_attr_t thread_attr;
if (argc != 2) { /* Test for correct number of arguments */
fprintf(stderr, "Usage: %s <server_port>\n", argv[0]);
exit(1);
}
clients = (struct client**) calloc(1, sizeof(struct client*) * MAXCLIENTS);
if (clients == NULL)
DieWithError("calloc() for clients failed");
threads = (pthread_t*) calloc(1, sizeof(pthread_t) * MAXCLIENTS);
if (clients == NULL)
DieWithError("calloc() for clients failed");
pthread_mutex_init(&clients_mutex, NULL);
pthread_mutex_init(&threads_mutex, NULL);
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
server_port = atoi(argv[1]);
/* Create a reliable, stream socket using TCP */
if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
DieWithError("socket() failed");
// Zero out server_addr var and fill with information
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(server_port);
// Bind server with sock, IP, and port so that the clients can connect to us
if (bind(server_sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0)
DieWithError("bind() failed");
// Allow this server to accept 5 clients at a time (queue has 0 capacity because we multithread)
if (listen(server_sock, 0) < 0)
DieWithError("listen() failed");
// Display some information so we can connect with client
printf("Using\n\tport: %d\n\tIP: %s\n", server_port, inet_ntoa(server_addr.sin_addr));
server_addr_size = sizeof(server_addr);
for (;;) {
int* new_socket = (int*)malloc(sizeof(int));
if ((*new_socket = accept(server_sock,
(struct sockaddr*) &server_addr, &server_addr_size)) < 0) {
printf("accept() failed");
continue;
}
int free_spot = -1;
for (int i = 0; i < MAXCLIENTS; i++)
if (!threads[i]) free_spot = i;
if (free_spot == -1) {
printf("no empty threads (max clients handled currently)");
send(*new_socket,(void*) OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
close(*new_socket);
continue;
}
if (pthread_create(&threads[free_spot], &thread_attr,
(void*) &handle_client, (void*) new_socket)) {
printf("pthread_create failed");
close(*new_socket);
continue;
}
printf("sent new client %d to handle_client()\n", *new_socket);
}
}
void handle_client(void* new_socket) {
int socket = *(int*)new_socket;
free(new_socket);
printf("handling new client %d\n", socket);
struct client* curr_cl;
pthread_mutex_lock(&clients_mutex);
printf("locked mutex?\n");
if (send(socket, (void*)ALL_GOOD_CD, sizeof(ALL_GOOD_CD), 0) < 0) {
printf("inital all good resp failed");
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
return;
}
printf("sent stuff\n");
int free_spot = -1;
for (int i = 0; i < MAXCLIENTS; i++)
if (!clients[i]) free_spot = i;
printf("filtered through clients and got free spot %d\n", free_spot);
if (free_spot == -1) {
printf("didn't find free spot :(\n");
send(socket, (void*)TOO_MANY_CD, sizeof(TOO_MANY_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
printf("found free spot %d for client %d", free_spot, socket);
clients[free_spot] = (struct client*) calloc(1, sizeof(struct client));
if (clients[free_spot] == NULL) {
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
curr_cl = clients[free_spot];
if (recv(socket, curr_cl->username, sizeof(curr_cl->username), 0) < 0) {
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
// Subscribe client to #ALL automatically
curr_cl->subs[0] = "#ALL";
if (send(socket, (void*)ALL_GOOD_CD, sizeof(ALL_GOOD_CD), 0) < 0) {
printf("send() for final all good failed\n");
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
printf("\taccepted new client %s and now listening\n", curr_cl->username);
pthread_mutex_unlock(&clients_mutex);
return;
}
void DieWithError(char* errorMessage) {
fprintf(stderr, "Dying with error sadface: %s\n", errorMessage);
exit(1);
}
Here is the Makefile
# the compiler: gcc for C
CC = gcc
# compiler flags
CFLAGS = -g
make: ttweetcl.c ttweetsrv.c
gcc -o ttweetcli ttweetcl.c && gcc -o ttweetsrv ttweetsrv.c -lpthread
.PHONY: clean
clean:
rm -f ./ttweetcli ./ttweetsrv
Solved! One comment (now removed) noticed that I was not ending my printf()'s with a \n and therefore was not flushing the buffer. Now that I have added all \n's then the code executes as it should.

Server and client program not sending or receiving data

I've been scratching my head with this one for quite a while now. I've got a simple client and server program and I want the server to echo what the client sends it. I can't figure out why the server isn't receiving any data.
Client code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAX_BUF 64
#define MAX_ARGS 8
void initClient(int*);
int main()
{
int socket;
/* initialize socket and connect to server */
initClient(&socket);
while(1){
char in[80];
char temp[80];
bzero(in, 80);
bzero(temp, 80);
printf("What's your message: ");
gets(in);
strcpy(temp, in);
send(socket, temp, strlen(temp), 0);
if(strcmp(temp, "exit") == 0)
break;
}
return 0;
}
void initClient(int *sock)
{
FILE *configFile;
char ip[MAX_BUF];
int port;
int i;
struct sockaddr_in addr;
/* get IP address and port number from config file */
if (!(configFile=fopen(".config","r"))) {
printf("cannot read config file...\n");
exit(1);
}
fscanf(configFile, "%s", ip);
fscanf(configFile, "%d", &port);
fclose(configFile);
/* create socket and connect to logger */
sock = (int *)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock < 0){
printf("Could not open socket\n");
exit(-1);
}
/* setup address */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons((unsigned short) port);
/* connect to server */
i = connect((int)sock,
(struct sockaddr *) &addr,
sizeof(addr));
if (i<0) {
printf("client could not connect!\n");
exit(-1);
}
}
Server code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAX_BUF 64
int main()
{
FILE *configFile;
char ip[MAX_BUF];
int port;
char str[MAX_BUF];
/* get IP address and port number from config file */
if (!(configFile=fopen(".config","r"))) {
printf("cannot read config file...\n");
exit(1);
}
fscanf(configFile, "%s", ip);
fscanf(configFile, "%d", &port);
fclose(configFile);
int myListenSocket, clientSocket;
struct sockaddr_in myAddr, clientAddr;
int i, addrSize, bytesRcv;
/* Create socket */
myListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(myListenSocket < 0) {
printf("Couldn't open socket\n");
exit(-1);
}
/* Set up server address */
memset(&myAddr, 0, sizeof(myAddr));
myAddr.sin_family = AF_INET;
myAddr.sin_addr.s_addr = htonl(INADDR_ANY);
myAddr.sin_port = htons((unsigned short) port);
/* Binding */
i = bind(myListenSocket, (struct sockaddr *) &myAddr, sizeof(myAddr));
if(i < 0){
printf("Couldn't bind socket\n");
exit(-1);
}
/* Listen */
i = listen(myListenSocket, 5);
if(i < 0){
printf("Couldn't listen\n");
exit(-1);
}
/* Wait for connection request */
addrSize = sizeof(clientAddr);
clientSocket = accept(myListenSocket,
(struct sockaddr *) &clientAddr,
&addrSize);
if(clientSocket < 0){
printf("Couldn't accept the connection\n");
exit(-1);
}
/* Read message from client and do something with it */
char buffer[100];
while(1){
bzero(buffer, 100);
bytesRcv = read(clientSocket, buffer, sizeof(buffer));
buffer[bytesRcv] = 0;
printf("this is what the client sent: %s\n", buffer);
if(bytesRcv == 0){
break;
}
}
close(myListenSocket);
close(clientSocket);
return 0;
}
When you pass a pointer you should reference it as *sock to get its value, otherwise if you reference it as sock you are, in fact, getting the address and not the value of the variable.
Here is your initClient function corrected:
void initClient(int *sock)
{
FILE *configFile;
char ip[128];
int port;
int i;
struct sockaddr_in addr;
/* get IP address and port number from config file */
if (!(configFile=fopen(".config","r"))) {
printf("cannot read config file...\n");
exit(1);
}
fscanf(configFile, "%s", ip);
fscanf(configFile, "%d", &port);
fclose(configFile);
/* create socket and connect to logger */
*sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(*sock < 0){
printf("Could not open socket\n");
exit(-1);
}
/* setup address */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons((unsigned short) port);
/* connect to server */
i = connect(*sock, (struct sockaddr *) &addr, sizeof(addr));
if (i<0) {
printf("client could not connect!\n");
exit(-1);
}
}

UDP in c: Extra characters getting added to file when saving received data in chunks

I'm trying to get a simple send and receive UDP program working, but I'm having a bit of trouble with saving the received data. As far as I can tell the data is being sent and received properly, as I've printed it out on both ends. Before I write the data to the file (if I just print out the received chunk) it doesn't have any extra characters, so I'm a bit lost as to where they are coming from.
When I append each chunk of received data to the file it adds a "^P^B^GÐ^?" after every chunk written. for example one of the chunks ended with "We, therefore^P^B^GÐ^?," instead of "We, therefore,".
Any help is appreciated, thanks in advance.
UPDATE:
I've seemed to have gotten things working semi-better, I'm now having an issue with it replacing the first character of every chunk with a null character, for example:
"^#N CONGRESS, July 4, 1776." instead of "IN CONGRESS, July 4, 1776."
It's doing this for the first char of every chunk received, I've tried multiple debug print statements but can't seem to figure out what the issue is.
Here is my Receive and Send functions:
void receiveFile() {
int addr_len, bytesRead;
char recvData[BUFSIZE]; // Buffer to store received data
struct sockaddr_in server_addr, client_addr;
// Set up struct to receive data from our port and address
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero), 8);
addr_len = sizeof (struct sockaddr);
printf("\nWaiting for data on port %d\n", port);
//Keep reading data from the socket
while (1) {
FILE *fp;
fp=fopen("dummyfile.txt", "ab");
memset(recvData, 0, BUFSIZE);
bytesRead = recvfrom(sock, recvData, BUFSIZE, 0,
(struct sockaddr *) &client_addr, &addr_len);
int x;
for(x = 0; x < bytesRead; x++) {
fputc(recvData[x], fp);
}
// Print out who we're receiving from and what we're recieving
printf("Receiving data from %s : %d\n", inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
fclose(fp);
}}
Here is the Send Function:
void sendFile() {
// Announce who we're sending data to
if(DEBUG) { printf("\nSending %s to %s:%d\n", filename, address, port); }
// Open file
FILE * file = fopen(filename, "rb");
if (file == NULL) {
perror("Invalid File\n");
exit(1);
}
// Get size of the file
fseek(file, 0, SEEK_END);
int filesize = ftell(file);
rewind(file);
int curPos = 0;
int dataSize = 0;
while(curPos < filesize) {
struct sockaddr_in server_addr;
struct hostent *recvr;
char sendData[BUFSIZE]; // stores message to be sent
memset(sendData, 0, BUFSIZE);
int byte, i;
for(i = 0; i < BUFSIZE; i++){
if((filesize - curPos) > 0) {
byte = fgetc(file);
sendData[i] = byte;
curPos++;
dataSize++;
}
else { break; }
}
recvr = gethostbyname(address);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *) recvr->h_addr);
bzero(&(server_addr.sin_zero), 8);
if(DEBUG) {
char tempData[1201];
strncpy(tempData, sendData, 1200);
tempData[1201] ='\0';
printf("%s\n\n\n\n\n", tempData);
}
sendto(sock, sendData, dataSize, 0,
(struct sockaddr *) &server_addr, sizeof (struct sockaddr));
dataSize = 0;
}
fclose(file);}
What happens when you change the printing to:
fprintf(fp, "%.*s", bytesRead, recvData);
There is a guarantee that recvfrom() will not null terminate your messages; you would have to transmit the null terminator yourself.
I can't tell what your residual problem is. I have the following to complete programs working back to back. I've scrutinized the saved file for NULs with no problem.
I ran them as:
./recv & sleep 1; ./send; kill %1
recv.c
#include "posixver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in */
#include <arpa/inet.h> /* inet_ntoa() */
#include "stderr.h"
static void receiveFile(int sock, int port, char *filename)
{
//Keep reading data from the socket
FILE *fp = fopen(filename, "ab");
if (fp == 0)
err_syserr("failed to open file %s", filename);
printf("\nWaiting for data on port %d\n", port);
while (1)
{
char recvData[BUFSIZ]; // Buffer to store received data
struct sockaddr_storage addr;
struct sockaddr_in *client_addr = (struct sockaddr_in *)&addr;
memset(recvData, 0, sizeof(recvData));
socklen_t addr_len = sizeof (struct sockaddr_storage);
int bytesRead = recvfrom(sock, recvData, sizeof(recvData), 0,
(struct sockaddr *) &client_addr, &addr_len);
if (bytesRead < 0)
err_syserr("Failed to read from socket");
err_remark("Read %d bytes\n", bytesRead);
for (int x = 0; x < bytesRead; x++)
{
fputc(recvData[x], fp);
}
fflush(fp);
// Print out who we're receiving from and what we're receiving
//char *rem_host = inet_ntoa(client_addr->sin_addr);
//int rem_port = ntohs(client_addr->sin_port);
//printf("Receiving %d bytes from %s:%d\n", bytesRead, rem_host ? rem_host : "<unknown>", rem_port);
}
fclose(fp);
}
int main(int argc, char **argv)
{
int fd;
struct sockaddr_storage addr;
struct sockaddr_in *server_addr = (struct sockaddr_in *)&addr;
memset(&addr, 0, sizeof(addr));
server_addr->sin_family = AF_INET;
server_addr->sin_addr.s_addr = htonl(INADDR_ANY);
server_addr->sin_port = htons(5190);
err_setarg0(argv[0]);
if (argc > 1)
err_usage("");
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
err_syserr("Failed to open DGRAM socket");
if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0)
err_syserr("Failed to bind DGRAM socket");
receiveFile(fd, 5190, "dummy.text");
return(0);
}
send.c
#include "posixver.h"
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "stderr.h"
#define bzero(b,len) (memset((b), '\0', (len)), (void)0)
enum { DEBUG = 1 };
static void sendFile(int sock, const char *filename, char *address, int port)
{
// Announce who we're sending data to
if (DEBUG)
printf("\nSending %s to %s:%d\n", filename, address, port);
// Open file
FILE * file = fopen(filename, "rb");
if (file == 0)
err_syserr("Failed to open file %s", filename);
// Get size of the file
fseek(file, 0, SEEK_END);
int filesize = ftell(file);
rewind(file);
int curPos = 0;
int dataSize = 0;
while (curPos < filesize)
{
struct sockaddr_in server_addr;
struct hostent *recvr;
char sendData[BUFSIZ]; // stores message to be sent
memset(sendData, 0, BUFSIZ);
int byte, i;
for (i = 0; i < BUFSIZ; i++){
if ((filesize - curPos) > 0) {
byte = fgetc(file);
sendData[i] = byte;
curPos++;
dataSize++;
}
else
break;
}
recvr = gethostbyname(address);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *) recvr->h_addr_list[0]);
bzero(&(server_addr.sin_zero), 8);
if(DEBUG) {
char tempData[1201];
strncpy(tempData, sendData, 1200);
tempData[1201] ='\0';
printf("SEND:\n%s\n\n\n", tempData);
}
if (sendto(sock, sendData, dataSize, 0,
(struct sockaddr *) &server_addr, sizeof (struct sockaddr)) < 0)
err_syserr("Failed to send %d bytes\n", dataSize);
dataSize = 0;
}
fclose(file);
}
int main(int argc, char **argv)
{
int fd;
err_setarg0(argv[0]);
if (argc > 1)
err_usage("");
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
err_syserr("Failed to open DGRAM socket");
sendFile(fd, "/etc/passwd", "localhost", 5190);
return(0);
}
posixver.h
#ifndef JLSS_ID_POSIXVER_H
#define JLSS_ID_POSIXVER_H
/*
** Include this file before including system headers. By default, with
** C99 support from the compiler, it requests POSIX 2001 support. With
** C89 support only, it requests POSIX 1997 support. Override the
** default behaviour by setting either _XOPEN_SOURCE or _POSIX_C_SOURCE.
*/
/* _XOPEN_SOURCE 700 is loosely equivalent to _POSIX_C_SOURCE 200809L */
/* _XOPEN_SOURCE 600 is loosely equivalent to _POSIX_C_SOURCE 200112L */
/* _XOPEN_SOURCE 500 is loosely equivalent to _POSIX_C_SOURCE 199506L */
#if !defined(_XOPEN_SOURCE) && !defined(_POSIX_C_SOURCE)
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600 /* SUS v3, POSIX 1003.1 2004 (POSIX 2001 + Corrigenda) */
#else
#define _XOPEN_SOURCE 500 /* SUS v2, POSIX 1003.1 1997 */
#endif /* __STDC_VERSION__ */
#endif /* !_XOPEN_SOURCE && !_POSIX_C_SOURCE */
#endif /* JLSS_ID_POSIXVER_H */
stderr.c and stderr.h
Actually, not standard at all, except in my code. The functions used have the declarations:
extern void err_setarg0(const char *argv0);
extern void err_error(const char *format, ...) PRINTFLIKE(1,2) NORETURN();
extern void err_remark(const char *format, ...) PRINTFLIKE(1,2);
extern void err_syserr(const char *format, ...) PRINTFLIKE(1,2) NORETURN();
extern void err_usage(const char *usestr) NORETURN();
The first records the program name. The second reports an error message and exits; the third reports a message and returns; the fourth reports an error message and adds error information from 'errno' and 'strerror()' if there is any to use; the last reports on how to use the program - in this case, the programs accept no arguments. The full source code (quite large) is available from the IIUG Software site as part of the SQLCMD package available there, and various other programs that I've also submitted there.

Resources