I have a weird problem when writing a simple socket server and client in C.
The logic is very simple, the client sends 2 messages every time:
The data size (like a header indicating the data size it will send).
the data itself.
The server can successfully receive the first couple of messages, but after a while the data size message becomes some weird value.
This is the code and output.
server.c
uint32_t toUInt32(uint8_t *buf) {
return ((uint32_t) buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
int recvall(int fd, uint8_t *data, int size) {
int BUFF_SIZE = 1024;
int cur_size = 0;
while(cur_size < size) {
int size_to_recv = BUFF_SIZE;
if (size - cur_size < BUFF_SIZE) {
size_to_recv = size - cur_size;
}
char *temp = malloc(size_to_recv);
int recv_size = recv(fd, temp, size_to_recv, 0);
memcpy(data + cur_size, temp, size_to_recv);
cur_size += size_to_recv;
free(temp);
}
return cur_size;
}
while(1)
{
uint8_t size_message[4];
memset(size_message, 0 , 4);
read_size = recv(client_sock , size_message, 4 , 0);
if (read_size == 0) {
return 0;
}
printf("bytes are %d %d %d %d\n", size_message[0], size_message[1], size_message[2], size_message[3]);
uint32_t size = toUInt32(size_message);
printf("size is %d\n", size);
uint8_t *data = malloc(size);
read_size = recvall(client_sock , data , size);
free(data);
}
server output:
bytes are 0 1 83 91
size is 86875
bytes are 0 1 130 117
size is 98933
bytes are 0 1 208 101
size is 118885
bytes are 0 2 141 253
size is 167421
bytes are 43 244 25 88
size is 737417560
client.c
static inline void
buffer_write32be(uint8_t *buf, uint32_t value) {
buf[0] = value >> 24;
buf[1] = value >> 16;
buf[2] = value >> 8;
buf[3] = value;
}
while(1) {
uint32_t size = (uint32_t)packet->size;
uint8_t *buf = malloc(sizeof(uint32_t));
buffer_write32be(buf, size);
if (send(sock , buf , sizeof(uint32_t) , 0 ) < 0)
{
return false;
}
if (send(sock , packet->data , packet->size, 0 ) < 0)
{
return false;
}
free(buf);
sleep(1);
}
client output:
packet size is 86875, header size 4
packet size is 98933, header size 4
packet size is 118885, header size 4
packet size is 167421, header size 4
packet size is 167847, header size 4
packet size is 169004, header size 4
packet size is 169811, header size 4
You can see that the data size that the server receives suddenly becomes a weird large value, but the client sent out the right value.
What is going on here?
Related
I am trying to use Alsa library to reproduce the audio I get from my CAN FD communication, into my headphones. I don't quite understand how to properly configure Alsa's parameters, in order to be able to listen to the sound I get from the CAN FD.
static char *device = "plughw:0,0"; /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
static unsigned int rate = 16000; /* stream rate */
static unsigned int channels = 1; /* count of channels */
static unsigned int buffer_time = 40000; /* ring buffer length in us */
static unsigned int period_time = 120000; /* period time in us */
static int resample = 1; /* enable alsa-lib resampling */
static int period_event = 0; /* produce poll event after each period */
int size;
while (1) {
do {
nbytes = read(s, &frame, sizeof(struct canfd_frame));
} while (nbytes == 0);
for (x = 0; x < 64; x = x + 2) {
buffer[a] = ((uint32_t) frame.data[x] << 8)
| ((uint32_t) (frame.data[x + 1]));
a++;
}
//err=snd_pcm_writei(handle,buffer,32);
//printf("Datos = %d\n", err);
memcpy(total1 + i * 32, buffer, 32 * sizeof(uint32_t));
i++;
a = 0;
if (i == 500) {
buffer_length=16000;
ptr = total1;
while(buffer_length > 0){
err = snd_pcm_writei(handle, ptr, 16000);
printf("Datos = %d\n", err);
snd_pcm_avail_delay(handle, &availp, &delayp);
//printf("available frames =%ld delay = %ld z = %d\n", availp, delayp, z);
if (err == -EAGAIN)
continue;
if(err < 0){
err=snd_pcm_recover(handle, err, 1);
}
else{
ptr += err * channels;
buffer_length -= err;
z++;
}
if(err<0){
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
}
i = 0;
}
This is a part of my code, I don't thinks posting the whole code is worth. I don't understand which values should I give to buffer_time, period_time and how to be able to listen to what a I get through the CAN FD in real time. I am using snd_pcm_writei, inserting a buffer I fill with some samples I get from the CAN FD. I don't know which size should I give to the buffer and to the "frames" variable, another one that I don't quite understand, eventhough I have read some about it.
Any idea how should I configure my system? (buffer_time, period_time, buffer_size, frame,...)
I have tried using different buffer and frame sizes, but I don't think I understand how it works properly. How can I calculate the size of the frame and buffer of the snd_pcm_writei(), in order to listen in Real Time to the audio?
Should I use two differente threads? One to create the buffer with the CAN FD information and the other one to handle the buffer and the audio output?
Thanks in advance,
Ander.
I have finally managed to hear my self through the headphones. I have changed my configuration posted on my previous in order to sincronize it with the data I get from the CAN FD. I will post part of my code down here in case somebody needs an example. The most important part having to handle buffers like these is to handle the time to fill and the time to communicate it. Handling the time and configuring the Alsa parameters accordingly makes easier to handle the buffers.
static char *device = "plughw:0,0"; /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
static unsigned int rate = 22000; /* stream rate */
static unsigned int channels = 1; /* count of channels */
static unsigned int buffer_time = 1000; /* ring buffer length in us */
static unsigned int period_time = 10000; /* period time in us */
static int resample = 1; /* enable alsa-lib resampling */
static int period_event = 0; /* produce poll event after each period */
int size;
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
static snd_output_t *output = NULL;
snd_pcm_sframes_t delayp;
snd_pcm_sframes_t availp;
snd_pcm_uframes_t frames;
static void write_loop(snd_pcm_t *handle) {
uint32_t *buffer = malloc(16000 * sizeof(uint32_t));
uint32_t *total1 = malloc(16000 * sizeof(uint32_t)); // array to hold the result
while (1) {
do {
nbytes = read(s, &frame, sizeof(struct canfd_frame));
} while (nbytes == 0);
for (x = 0; x < 64;x = x + 2) {
buffer[a] = ((uint32_t) frame.data[x] << 8)
| ((uint32_t) (frame.data[x + 1]));
//buffer[a]=frame.data[x];
a++;
}
i++;
if (i == 250) {
memcpy(total1, buffer, 16000 * sizeof(uint32_t));
//printf("Address = %lu \n",(unsigned long)total1);
flag = 1;
buffer_length = 16000;
i = 0;
a = 0;
}
if (flag == 1) {
while(buffer_length > 0) {
snd_pcm_prepare(handle);
err = snd_pcm_writei(handle, total1, buffer_length);
//printf("Datos = %d\n", err);
snd_pcm_avail_delay(handle, &availp, &delayp);
//printf("available frames =%ld delay = %ld\n",availp,delayp);
if (err == -EAGAIN)
continue;
if (err < 0) {
err = snd_pcm_recover(handle, err, 1);
} else {
ptr += err * channels;
buffer_length -= err;
z++;
}
if (err < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
}
flag = 0;
}
}
}
I'm trying to do a client/server program using sockets, with protobuf messages, for school.
However one of the commands is(put) isn't working properly. It works with size, and returns 0, as it should but when I try put, it doesn't seem to work properly. I wrote some printf's in the code to try and check what's happening, and read_all doesn't seem right.
Here's the code.
int read_all (int socket, char *buf, int len){
int left;
int used;
char *prov;
prov = buf;
left = len;
printf("read all len: %d\n",len);
while (left > 0) {
printf("left : %d\n",left);
used = read(socket, prov, left);
printf("used: %d\n",used);
if ( used < 0) {
perror("readall err\n");
printf("errno here %d \n",errno);
return -1;
} else if (used == 0) break;
left -= used;
printf("left 2 : %d\n",left);
prov += used;
}
printf("read_all %d\n",len-left);
return (len - left);
}
When I use put , on the server terminal it shows :
read all len: 6
left: 6
and then it stops, that makes me think that read_all isn't working well, but I don't understand why. While on the client terminal it just shows that put wasn't successful.
Is read_all right or is something wrong.
Edit, read calls.
read_all for the size of msg buff:
read_all(client_socket, &rec, sizeof(rec) )
read_all for msg buff:
read_all(client_socket,(char *) rbuf, len )
rec is an int
len is an unsigned = ntohl(rec)
rbuf is a uint8_t = malloc(len)
edit 3:
write_all code:
int write_all(int socket, char *buf, int len){
int bufsize = len;
char *prov = buf;
while(bufsize > 0){
int res = write(socket, prov, bufsize);
if(res < 0){
perror("writeall err");
return -1;
}
prov += res;
bufsize -= res;
}
return len;
}
write calls:
sends size:
write_all(descr, &sen , sizeof(len))) != len)
send msg buf:
write_all(descritor,(char *) buf, len)) != len)
variable:
unsigned len = message_t__get_packed_size(msg)
uint8_t *buf = malloc(len)
unsigned sen = htoml(len)
I am trying to send a char array of 100.000.000 bytes (maybe more) from a server to a client through a TCP socket in C.
I am doing this like that:
char *array; // global array malloc'd (SIZE)
//#######################
// server code
//#######################
int i;
int SIZE = 100000000
for (i = 0; i < SIZE; i = i + 4){
write(id, &array[i], 4); // write 4 bytes every time
}
//#######################
// client code
//#######################
int i;
int SIZE = 100.000.000
for (i = 0; i < SIZE; i = i + 4)
read(id, array + i, 4); // read 4 bytes
Problems:
1) When I try to send more bytes something goes wrong with the transfer. For example if I change 4 to 100, it says "broken pipe". Why does that happen?
2) I know this is not a "safe" way for reading/writing since I am not checking read() and write() return values. How can I do that?
3) Do I have to use htonl() and ntohl() functions?
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
//in #param
//#param fd the socket file descriptor
//#param array an array of data source to write to send to the connected client
//#param SIZE the size of data source to send to the client
//#param sz_emit the size of data to send in one loop step
//out #param
//total length of data emited to the client
int write_to_client(int fd, char* array, int SIZE, int sz_emit)
{
//#######################
// server code
//#######################
int i=0, sz=0;
for(i = 0; i < SIZE; i += sz_emit )
{
while(sz_emit-sz)
{
sz+=write(id, array+i+sz, sz_emit-sz);
}
sz = 0;
}
return i;
}
//#######################
// client code
//#######################
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//in #param
//#param fd is the file descriptor of the socket to read from
//#param SIZE the size of datas you want to read from the socket
//#param sz_received the size of byte to read in one loop step
//#param length, the length of data received
//#param read_err if 0 no error if -1 an error occurs use errno from #include <errno.h> to know more about that error
//out #param
//a pointer to an array of size SIZE containing the data readed
char* receive_from_server(int fd, int SIZE, int sz_received, int* length, int* read_err)
{
*read_err = 0;
int i = 0, sz = 0, rt = 0, count=0;
char *array = (char *)malloc(SIZE);
memset(array, 0, SIZE);
for (i = 0; i < SIZE; i += sz_received)
{
while(sz_received-sz)
{
rt = read(id, array + i + sz, sz_received-sz);
if(rt==-1)
{
*read_err=rt;
printf("an error occurs\n");
goto l;
}
if(!rt)goto l;
sz+=rt;
count += sz;
}
sz = 0;
}
l: *length = count;
return array;
}
usage:
//server side
int SIZE = 100000000;
char array_to_send[SIZE]={'r'};
int sz_data_emited = write_to_client(sock, array_to_send, SIZE, 4);
printf("how many byte data emited:%d\n", sz_data_emited);
//client side
int SIZE = 100000000, length = 0, read_err=0;
char*array_received = NULL;
array_received = receive_from_server(sock, SIZE, 4, &length, &read_err);
if(!read_err)printf("get some datas\n");
// free array_received when finished...free(array_received)
some notes:
you need to pay attention on endianess when you want to transfert a multi-byte entity for example a short, int, long, utf-16 etc but if your datas are utf-8 or ascii text you don't need it.
I think that Narcisse Doudieu Siewe answer has some errors. I suppose it fails when SIZE isn't multiplicity of sz_emit.
Example there is 20 bytes that we sent in 8 bytes chunkes than the last data chunk (or packet) will be 4 byte long. Than if we try to send the last 8 bytes chunk and there is only 4 bytes left while loop will be infinite as while(8 - 4), and it never reaches sz = 8, as the next send will just increment by 0. So I write modification like this (not tested, I will test soon and write also second method taking into account this boundary condition).
/**
* #param sock_fd - the file descriptor of the socket to write (send) data to
* #param packetLength - the size of data to send in one packet
* #param data - binary data to send (unsigned char array)
* #param dataLength - the size of all binary data to send
* #return - status code SUCCESS or FAILURE
*/
result_t send_binary(const sock_fd_t sock_fd, const size_t packetLength, const unsigned char *data, const size_t dataLength) {
ssize_t leftPacketLength = 0;
ssize_t offset = 0;
ssize_t sentPacketLength = 0;
// send each packet of data in the loop
for(int leftDataLength=dataLength; leftDataLength>0; leftDataLength -= packetLength) {
leftPacketLength = (leftDataLength > packetLength) ? packetLength : leftDataLength;
while(leftPacketLength > 0) {
sentPacketLength = send(sock_fd, data + offset, leftPacketLength, 0);
if(sentPacketLength < 0) {
fprintf(stderr, "%s: Error while sending data to the socket.\n", __func__);
perror(errno);
return FAILURE;
}
offset += sentPacketLength;
leftPacketLength -= sentPacketLength;
}
}
if(offset != dataLength)
return FAILURE;
return SUCCESS;
}
/**
* #param sock_fd - the file descriptor of the socket to read (recieve) data from
* #param packetLength - the size of data to recieve in one packet
* #param data - binary data received (unsigned char array) - previously allocated
* #param dataLength - the size of all binary data received - previously defined
* #return - status code SUCCESS or FAILURE
*/
result_t recv_binary(const sock_fd_t sock_fd, const size_t packetLength, unsigned char *data, const size_t dataLength) {
ssize_t leftPacketLength = 0;
ssize_t offset = 0;
ssize_t recvedPacketLength = 0;
for(int leftDataLength=dataLength; leftDataLength > 0; leftDataLength -= packetLength) {
leftPacketLength = (leftDataLength > packetLength) ? packetLength : leftDataLength;
while(leftPacketLength > 0) {
recvedPacketLength = recv(sock_fd, data + offset, leftPacketLength, 0);
if(recvedPacketLength < 0) {
fprintf(stderr, "%s: Error while receiving data from the socket.\n", __func__);
perror(errno);
return FAILURE;
}
offset += recvedPacketLength;
leftPacketLength -= recvedPacketLength;
}
}
if(offset != dataLength)
return FAILURE;
return SUCCESS;
}
There is also need to send size of binary data send/recv via socket before transfering actual binary data. It's needed to know how many bytes we need to read.
I wrote function for read and write for a tcp program . I output in server side but I can't get read on client side . my code
read function :
int read_data (int sd , char **data_buf)
{
int in_length,length,size,bytesread;
char *temp_buf;
size = read(sd,&in_length,sizeof(in_length));/*send entire length of data*/
if( 0 > size )
{
printf("Error on reading from socket\n");
exit(1);
}
length = ntohl(in_length);
printf("Total length coming : %d\n",length);
*data_buf =(char *)malloc((length+1)*sizeof(char));
temp_buf =(char *)malloc((length+1)*sizeof(char));
while(length> 0)
{
bytesread = read(sd,temp_buf,4);
strcat(*data_buf,temp_buf);
temp_buf = temp_buf + bytesread;
length = length - bytesread;
}
return 1;
}
and my write functions as :
int write_data (int sd , char *buffer)
{
int length,len_buff,bytesread,size;
len_buff = strlen(buffer);/*total length of string*/
printf("string == %s\n",buffer);
length = htonl(len_buff);/*convert to host to n/w*/
printf("Total length send =%d\n",len_buff);
size = write(sd,&length,sizeof(length));/*write total size to server */
if( 0 > size)
{
printf("error\n");
exit(0);
}
while(length > 0)
{
bytesread = write(sd,buffer,4);/*write 4 bytes to server*/
buffer = buffer + bytesread;
length = length - bytesread;
}
return 1;
}
client program :
///.............code for socket and connections.................//
ret = write_data(sd,user_string);/*write entire datas to server*/
value_from_server = read_data(sd,&data_buf);
server side program :
value_from_client = read_data(connfd,&data_buf);
printf("the value from client : %s\n",data_buf);
index = string_function(data_buf,&store_buf);
printf("after string process : %s\n",store_buf);
write_data(connfd,store_buf);
printf("i am waiting for next string\n");
connfd is the new socket for communication with client . reading and writing function work perfectly on server side . writing function work on client side . but reading from server not work in client program . ant mistake on my code ?
bytesread = read(sd,temp_buf,4);
Why read 4 bytes always inside the loop? You should be reading the remaining number of bytes to be read. The socket is blocking and hence will be stuck if the server is done sending but client still tries reading 4 bytes to arrive in the last iteration.
Have print statements inside the loop to know the bytes read in each iteration and see if client is blocked with read
Your code has several logic errors.
size = read(sd,&in_length,sizeof(in_length));/*send entire length of data*/
if( 0 > size )
{
printf("Error on reading from socket\n");
exit(1);
}
length = ntohl(in_length);
Here you are assuming you read four bytes, rather than fewer, or end of stream. You must check for end of stream (zero return value), and you must loop until you get the four bytes.
while(length> 0)
{
bytesread = read(sd,temp_buf,4);
strcat(*data_buf,temp_buf);
temp_buf = temp_buf + bytesread;
length = length - bytesread;
}
Here again you are ignoring the possibility of end of stream or an error. It should be:
while ((bytesread = read(sd,temp_buf, length)) > 0)
{
temp_buf += bytes_read;
length -= bytesread;
}
if (bytesread < 0)
{
perror("read 2");
}
else if (length > 0)
{
// end of stream before all expected bytes were received ...
}
else
{
// The OK case
}
Your sending code is suboptimal:
while(length > 0)
{
bytesread = write(sd,buffer,4);/*write 4 bytes to server*/
buffer = buffer + bytesread;
length = length - bytesread;
}
There's no point in chunking into 4-byte writes. It should be:
while (length > 0)
{
bytesread = write(sd, buffer, length);
buffer = buffer + bytesread;
length = length - bytesread;
}
and of course the misnamed bytesread variable should be called byteswritten. In fact you can rely on this loop only executing once. Again it should be followed by a test of byteswritten == -1 to check for errors.
Your functions have logic errors in them.
The reading loop is reading exactly 4 bytes on each iteration. If the length of the data being read is not an even multiple of 4, read() will block on the last iteration waiting for data that does not arrive. The reading loop is also assuming that read() returns a null-terminated buffer, but that is not the case, so strcat() will attempt to copy data from surrounding memory and will either copy garbage or crash with a segfault. Also, the reading function is not null-terminating the data buffer it returns to the caller, but the caller assumes it is null-terminated.
The writing loop is writing exactly 4 bytes on each iteration. If the length of the data is not an even multiple of 4, write() will attempt to write data from surrounding memory on the last iteration, and will either send garbage or crash with a segfault.
You are also not doing adequate error handling in either function.
Try something more like this instead:
void read_raw_bytes (int sd, void *data, int length)
{
int bytes_read;
char *data_ptr;
data_ptr = (char*) data;
while( length > 0 )
{
bytes_read = read(sd, data_ptr, length);
if( bytes_read < 0 )
{
printf("Error on reading from socket\n");
exit(1);
}
if( bytes_read == 0 )
{
printf("Disconnected while reading from socket\n");
exit(1);
}
data_ptr += bytes_read;
length -= bytes_read;
}
}
void write_raw_bytes (int sd, void *data, int length)
{
int bytes_sent;
char *data_ptr;
data_ptr = (char*) data;
while( length > 0 )
{
bytes_sent = write(sd, data_ptr, length);
if( bytes_sent < 0 )
{
printf("Error on writing to socket\n");
exit(0);
}
data_ptr += bytes_sent;
length -= bytes_sent;
}
}
int read_data (int sd, char **data_buf)
{
int length;
read_raw_bytes (sd, &length, sizeof(length)); /*send entire length of data*/
length = ntohl(length);
printf("Total length coming : %d\n", length);
*data_buf = (char *) malloc((length+1)*sizeof(char));
if (*data_buf == NULL)
{
printf("Error on allocating memory\n");
exit(1);
}
read_raw_bytes (sd, *data_buf, length);
(*data_buf)[length] = 0;
return 1;
}
int write_data (int sd, char *buffer)
{
int length, len_buff;
len_buff = strlen(buffer); /*total length of string*/
printf("string == %s\n", buffer);
printf("Total length send =%d\n", len_buff);
length = htonl(len_buff); /*convert to host to n/w*/
write_raw_bytes (sd, &length, sizeof(length)); /*write total size to server */
write_raw_bytes (sd, buffer, len_buff);
return 1;
}
I have made a C-program flash image to micro controller through uart.In this code,i am following a protocol,all things are going well.But when i load image into micro controller through uart,write is working fine but read blocked after reading 33 bytes.I am writing and reading byte by byte..declaration & definition of function to load image is written below:
Function declaration :
load_RAM_image(file_size, file_buff);
here file_size is unsigned int and its value is 40,980KB and file_buff is unsigned char pointer which points to image buffer.
Function declaration :
#define BYTE_WRITE 1
#define DELAY 10000
int load_RAM_image(unsigned int buff_size, unsigned char *buff)
{
unsigned int count, num_Wbytes, num_Rbytes, byte;
for (count = 0; count < buff_size;) /* increase count value to every time */
{
num_Wbytes = write( serial_fd, buff + count, BYTE_WRITE );
if (num_Wbytes < 0)
{
fputs("write failed!\n", stderr);
return -1;
}
tcdrain(serial_fd);
usleep(DELAY);
num_Rbytes = read( serial_fd, rbuff + count, BYTE_WRITE );
if (num_Rbytes < 0)
{
if (errno == EAGAIN)
{
printf("SERIAL EAGAIN ERROR\n");
return -EAGAIN;
}
else
{
printf("SERIAL read error %d %s\n", errno, strerror(errno));
}
}
printf("wbuf[ %d ] = %02x rbuff = %02x\n\n",
count /*+ byte*/, *(buff + count/* + byte*/), rbuff[count]);
/* Compare the received byte from BAM */
if (strncmp(buff + count, rbuff + count, BYTE_WRITE ) < 0)
{
printf("Error : RAM loading error(W != R)\n");
return -1;
}
count += BYTE_WRITE ;
printf("count = %d\n", count);
}
return 0;
}