Let's take an example of my doubt with this server code:
/* some code */
void *filebuffer = NULL;
/* some other code */
for (size_to_send = fsize; size_to_send > 0; ){
rc = sendfile(f_sockd, fd, &offset, size_to_send);
if (rc <= 0){
perror("sendfile");
onexit(f_sockd, m_sockd, fd, 3);
}
size_to_send -= rc;
}
/* other code */
and this client code:
/* some code */
void *filebuffer;
/*some other code */
for(size_to_receive = fsize; size_to_receive > 0;){
nread = read(f_sockd, filebuffer, size_to_receive);
if(nread < 0){
perror("read error on retr");
onexit(f_sockd, 0, 0, 1);
}
if(write(fd, filebuffer, nread) != nread){
perror("write error on retr");
onexit(f_sockd, 0, 0, 1);
}
size_to_receive -= nread;
}
/* other code */
My question is: if the server is on a x86 machine (little endian) and the client is on a x64 machine (little endian) could the pointer different size (4-8 byte) lead to a problem?
If yes, how can i solve?
No it won't be a problem, as you actually don't send pointers over the socket, just a stream of bytes. The only problem I see is if the file is a binary data and have 64-bit integers in it, and the receiving platform is 32 bit without support for 64 bit integers (e.g. long long), however, that is very unlikely unless you are receiving on an embedded system.
PS.
In your receiving loop you check for read errors, but not for the socket to be closed by the other end (when read returns 0.)
Related
Extremely new to socket programming and C in general. I am trying to write a basic program to send and receive data between two machines. I understand that recv will not get all your data at once -- you essentially have to loop it until it has read the whole message.
In lieu of just setting a limit on both machines, I have created a simple Message struct on the client side:
struct Message {
size_t length;
char contents[1024 - sizeof(size_t)];
} message;
message.length = sizeof(struct Message);
message.contents = information_i_want_to_send;
When it arrives at the server, I have recv read into a buffer: received = recv(ioSock, &buffer, 1024, 0) (Which coincidentally is the same size as my Message struct -- but assuming it wasn't...).
I then extract Message.length from the buffer like this:
size_t messagelength;
messagelength = *((size_t *) &buffer);
Then I loop recv into the buffer while received < messagelength.
This works, but I can't help feeling it's really ugly and it feels hacky. (Especially if the first recv call reads less than sizeof(size_t) or the machines are different bit architectures, in which case the size_t cast won't work..). Is there a better way to do this?
You have a fixed-size message, so you can use something like this:
#include <errno.h>
#include <limits.h>
// Returns the number of bytes read.
// EOF was reached if the number of bytes read is less than requested.
// On error, returns -1 and sets errno.
ssize_t recv_fixed_amount(int sockfd, char *buf, size_t size) {
if (size > SSIZE_MAX) {
errno = EINVAL;
return -1;
}
ssize_t bytes_read = 0;
while (size > 0) {
ssize_t rv = recv(sockfd, buf, size, 0);
if (rv < 0)
return -1;
if (rv == 0)
return bytes_read;
size -= rv;
bytes_read += rv;
buf += rv;
}
return bytes_read;
}
It would be used something like this:
typedef struct {
uint32_t length;
char contents[1020];
} Message;
Message message;
ssize_t bytes_read = recv_fixed_amount(sockfd, &(message.length), sizeof(message.length));
if (bytes_read == 0) {
printf("EOF reached\n");
exit(EXIT_SUCCESS);
}
if (bytes_read < 0) {
perror("recv");
exit(EXIT_FAILURE);
}
if (bytes_read != sizeof(message.length)) {
fprintf(stderr, "recv: Premature EOF.\n");
exit(EXIT_FAILURE);
}
bytes_read = recv_fixed_amount(sockfd, &(message.content), sizeof(message.content));
if (bytes_read < 0) {
perror("recv");
exit(EXIT_FAILURE);
}
if (bytes_read != msg_size) {
fprintf(stderr, "recv: Premature EOF.\n");
exit(EXIT_FAILURE);
}
Notes:
size_t is not going to be the same everywhere, so I switched to a uint32_t.
I read the fields independently because the padding within the struct can vary between implementations. They would need to be sent that way as well.
The receiver is populating message.length with the information from the stream, but doesn't actually use it.
A malicious or buggy sender could provide a value for message.length that's too large and crash the receiver (or worse) if it doesn't validate it. Same goes for contents. It might not be NUL-terminated if that's expected.
But what if the length wasn't fixed? Then the sender would need to somehow communicate how much the reader needs to read. A common approach is a length prefix.
typedef struct {
uint32_t length;
char contents[];
} Message;
uint32_t contents_size;
ssize_t bytes_read = recv_fixed_amount(sockfd, &contents_size, sizeof(contents_size));
if (bytes_read == 0) {
printf("EOF reached\n");
exit(EXIT_SUCCESS);
}
if (bytes_read < 0) {
perror("recv");
exit(EXIT_FAILURE);
}
if (bytes_read != sizeof(contents_size)) {
fprintf(stderr, "recv: Premature EOF.\n");
exit(EXIT_FAILURE);
}
Message *message = malloc(sizeof(Message)+contents_size);
if (!message) {
perror("malloc");
exit(EXIT_FAILURE);
}
message->length = contents_size;
bytes_read = recv_fixed_amount(sockfd, &(message->contents), contents_size);
if (bytes_read < 0) {
perror("recv");
exit(EXIT_FAILURE);
}
if (bytes_read != contents_size) {
fprintf(stderr, "recv: Premature EOF.\n");
exit(EXIT_FAILURE);
}
Notes:
message->length contains the size of message->contents instead of the size of the structure. This is far more useful.
Another approach is to use a sentinel value. This is a value that tells the reader the message is over. This is what the NUL that terminates C strings is. This is more complicated because you don't know how much to read in advance. Reading byte-by-byte is too expensive, so one normally uses a buffer.
while (1) {
extend_buffer_if_necessary();
recv_into_buffer();
while (buffer_contains_a_sentinel()) {
// This also shifts the remainder of the buffer's contents.
extract_contents_of_buffer_up_to_sentinel();
process_extracted_message();
}
}
The advantage of using a sentinel value is that one doesn't need to know the length of the message in advance (so the sender can start sending it before it's fully created.)
The disadvantage is the same as for C strings: The message can't contain the sentinel value unless some form of escaping mechanism is used. Between this and the complexity of the reader, you can see why a length prefix is usually preferred over a sentinel value. :)
Finally, there's a better solution than sentinel values for large messages that you want to start sending before they are fully created: A sequence of length-prefixed chunks. One keeps reading chunks until a chunk of size 0 is encountered, signaling the end.
HTTP supports both length-prefixed messages (in the form of Content-Length: <length> header) and this approach (in the form of the Transfer-Encoding: chunked header).
There are Two ways to do that...
1.)
Use Binary Synchronous protocol. (Use of STX - Start of Text and ETX - End of Text ) for identification of the Text start and end.
2.)
Attach the number of bytes of data being sent at the start of Data. The socket will read those number of bytes and will get the number of bytes to be received from the socket. Then read all data and get the amount of data required.
Hmm... Seems tough...?? Let me give you an example.
Actual Data need to be sent: ABCDEFGHIJ
New Data format : 0010ABCDEFGHIJ
Data required in server side: ABCDE
recv function will read the first 4 bytes to get the number of bytes of actual data(In loop untill it gets 4 bytes):
int received1= recv(ioSock, recvbuf, 4, 0);
As per the above case, 'recvbuf' will be 0010 converted to an integer will give value as '10' which can be stored in some integer variable. So we have :
int toReadVal = 10
Now all we need is to read these 10 digits in next recv call :
int received= recv(ioSock, recvbuf1, toReadVal, 0);
Finally, we get the value of recvbuf1 as ABCDEFGHIG. Now you can truncate the value as per your requirement.
I slightly modified the demo taken from ALSA Project website in order to test it on my laptop's sound card (Intel PCH ALC3227 Analog, Ubuntu 18.04), which requires 2 channels and 16 bit integers. I also doubled the latency (1 s), switched off resampling and made the demo lasts longer. This is the code (runtime error checking not pasted for sake of synthesis)
#include <alsa/asoundlib.h>
#include <stdlib.h>
static char *device = "hw:1,0"; /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[16*1024]; /* some random data */
int main(void) {
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = (unsigned char) (rand() & 0xff);
snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)
snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 48000, 0, 1E6);
// Print actual buffer size
snd_pcm_hw_params_t *hw_params;
snd_pcm_hw_params_malloc(&hw_params);
snd_pcm_hw_params_current(handle, hw_params);
snd_pcm_uframes_t bufferSize;
snd_pcm_hw_params_get_buffer_size(hw_params, &bufferSize);
printf("ALSA buffer size = %li\n", bufferSize);
// playback
for (i = 0; i < 256; ++i) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer) / 4);
if (frames < 0)
frames = snd_pcm_recover(handle, (int) frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror((int) frames));
break;
}
if (frames > 0 && frames < (long) sizeof(buffer) / 4)
printf("Short write (expected %li, wrote %li)\n",
(long) sizeof(buffer) / 4, frames);
}
snd_pcm_hw_params_free(hw_params);
snd_pcm_close(handle);
return (0);
}
Audio works but could someone explain me why I sometimes get output like the following
ALSA buffer size = 16384
Short write (expected 4096, wrote 9)
Short write (expected 4096, wrote 4080)
indicating that less frames than expected have been written by snd_pcm_writei? According to the ALSA docs, I understand that a signal has to be occurred, but I don't get the reason and which signal is.
I also tried to halve the buffer's size, but the result is pretty the same.
A short read is reported when an error happens, but some frames were already written successfully.
You are supposed to call the same function again, with the remaining buffer; if the error was not transient, it will be reported then.
(This example code is wrong; it just ignores that the remaining part of the buffer was not written.)
I'm trying to implement a working HTTP Client-Server application just to make practice with network programming.
The 2 programs have to follow this basic algorithm:
CLIENT - send a GET request
SERVER - send "+OK\r\n"
SERVER - send file size in bytes
SERVER - send file
CLIENT - send ACK
I'm having a lot of troubles in the reading part, probably because i perform some dirty read on the stream.
These are the 2 reading function that i'm using:
/* Reads a line from stream socket s to buffer ptr
The line is stored in ptr including the final '\n'
At most maxlen chasracters are read*/
int readline (SOCKET s, char *ptr, size_t maxlen)
{
size_t n;
ssize_t nread;
char c;
for (n=1; n<maxlen; n++)
{
nread=recv(s, &c, 1, 0);
if (nread == 1)
{
*ptr++ = c;
if (c == '\n')
break;
}
else if (nread == 0) /* connection closed by party */
{
*ptr = 0;
return (n-1);
}
else /* error */
return (-1);
}
*ptr = 0;
return (n);
}
and:
int readNumber(SOCKET s, long *num, int maxRead)
{
size_t n;
ssize_t nread;
int totRead;
long number=0;
for (n=1; n<maxRead+1; n++)
{
nread=recv(s, &number, sizeof(number), 0);
if (nread == sizeof(number))
{
totRead+=nread;
*num = number;
}
else if (nread == 0) /* connection closed by party */
{
*num = 0;
return (n-1);
}
else /* error */
{
printf("nread = %d\n", nread);
return (-1);
}
}
return (totRead);
}
this is the snippet of the main where i receive the +OK message and then the file size:
memset(rbuf,0,sizeof(rbuf)); //rbuf is the buffer where is store the read
printf("waiting for response...\n");
result = readline(s, rbuf, sizeof(rbuf)); //reading function is above
printf("Byte read(okMsg) = %d\n", result);
if (result <= 0)
//ERROR MANAGEMENT
{
printf("Read error/Connection closed\n");
closesocket(s);
SockCleanup();
exit(1);
}
else
{
long fileLength=0;
unsigned char *fBuf;
//RECEIVE OK
if(!strcmp(rbuf,"+OK\r\n"))
{
puts("+OK\n");
//RECEIVE FILE LEN
int nw = readNumber(s, &fileLength, 1); //reading function is above
printf("Byte read(fDim) = %d\n", nw);
printf("File is %ld bytes long\n", fileLength);
if(nw >0)
{
// RECEIVE FILE
}
}
}
When i send the "+OK\r\n" string the server tells me that it sends 8 bytes, but when i read i find the '\0' char only after 6 bytes.
By the way it reads correctly the message, but when i try to read the file size (that is a long) it gives me back a wrong number.
My opinion is that the stream buffer is dirty, and that i'm reading 2 bytes that are not part of the file size, but i'm not understanding why this happens.
Please ask me more info if i'm not clear enough.
SOLVED:
Thank you all for your answers!!!
You put me in the right mindset to understand what was wrong.
Look like the problem was this declaration in the server:
char *okMsg = "+OK\r\n";
instead of
char okMsg[] = "+OK\r\n";
that lead me to an undefined behavior.
long number=0;
for (n=1; n<maxRead+1; n++)
{
nread=recv(s, &number, sizeof(number), 0);
You forgot to design and implement a protocol to carry the data between your server and your client. Because TCP provides a stream of bytes, your protocol should be defined as a stream of bytes.
How many bytes convey this number? Is "however many bytes a 'long' happens to occupy on my platform" a good answer? What's the semantic meaning of the first byte? Is "whatever the first byte of a 'long' happens to mean on my platform" a good answer?
A good answer would be, "The size shall be conveyed as a 4-byte unsigned integer in little-endian byte order". Then make absolutely sure your code sends and receives in that format.
I'm writing a small and simple server (in C language for Linux stations).
A client requests a file to my server, my server asks this file to another server which sends it to my server.
My server should NOT receive ALL the file before sending it to the client BUT must send the bytes of the file so as they arrive.
This is an exercise in school so I can not dissociate myself from this requirement.
I have implemented the function explained below. The problem is that the client receives a non-deterministic number of bytes and NEVER the entire file.
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
size_t n;
ssize_t nread;
ssize_t nwritten;
char c;
for (n=1; n<file_size; n++)
{
nread=recv(socketa, &c, 1, 0);
if (nread == 1)
{
nwritten = send(socketb,&c,1,0);
}
else if (nread == 0)
{
*buffer = 0;
return (-1); /* Errore */
}
else
return (-1); /* Errore */
}
}
*buffer = 0;
return (n);
}
Someone could kindly tell me where I'm wrong?
Is it an stupid idea to change the values SO_SNDBUF and SO_RCVBUF on both the server and the client?
Assuming the file_size is the total number of bytes you want to send, then your for loop will only send file_size - 1 bytes. In other words, you are off by one. Start from 0 instead to fix this:
for (n=0; n<file_size; n++)
{ //..
You capture the return value of send(), but you do not check to see if it was successful or not.
You are treating a 0 return value from recv() the same as an error. Since you do not show what you do after returning -1 from your function, I don't know if this may be contributing to your problem or not.
Certain errors on send() and recv() are "soft", in that you are allowed to retry the operation for those particular errors. One such error is EINTR, but check the documentation on your system to see if there are others.
In order to optimize performance and simplify your code, you can use splice()+pipes. Sendfile enables you to "forward" data between file descriptors, without the copy to user space.
Are you sure you have copied the correct code? That part as it is would not compile, there is a } in the last else which don't match with a corresponding {.
Also, how you get to know the file size? if it's send thru the socket as an integer, bear in mind the possible byte order of the source and destination machines.
Anyway, you are reading one byte at a time, you should improve it this way:
EDIT: use buffer and not the extra buff[2048];
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
ssize_t nread;
ssize_t nwritten;
ssize_t bLeft=file_size;
while (bLeft > 0)
{
nread=recv(socketa, buffer, bleft, 0);
if (nread > 0)
{
nwritten = send(socketb, buffer, nread, 0);
bLeft -= nread;
buffer+=nread;
}
else if (nread == 0)
{
// I think this could raise a memory exception, read below
*buffer = 0;
return (-1); /* Errore */
}
else
{
return (-1); /* Errore */
}
}
// If buffer is allocated with file_size bytes this one will raise a memory exception
// *buffer = 0;
return (file_size-bLeft);
}
This may be a straightforward solution but basically I am sending a binary file piece-by-piece to another program using TCP and checking to make sure the checksum matches for validity. The problem is the checksum received is never the checksum sent apart from the last part submitted (the remainder).
The code fragment for the sender is:
void* buffer = (void *)malloc(BLOCKSIZE+1);
if (!buffer)
error("Error: malloc error for buffer.\n");
fread(buffer, BLOCKSIZE, 1, fp_read); //read from binary file for the current block.
int checksum = checksum(buffer,BLOCKSIZE);
n = write(sockfd,buffer,BLOCKSIZE);
if (n < 0)
error("ERROR writing to socket");
For the receiver is is:
void* buffer = (void *)malloc(BLOCKSIZE+1);
if (!buffer)
error("Error: malloc error for buffer.\n");
n = read(sockfd,buffer,BLOCKSIZE);
if (n < 0)
error("Error: reading from socket.");
int checksum = checksumv(buffer,BLOCKSIZE);
Anyone see anything wrong with it? The only part having checksums match is the final piece which doesn't completely fill the buffer.
Thanks.
The whole code for the sender is:
FILE* fp_read = fopen("file.jpg", "rb");
if (fp_read == NULL)
error("Cannot open the file for peer piece download.");
fseek(fp_read, 0, SEEK_END);
unsigned long fileLen = ftell(fp_read);
fseek(fp_read, 0, SEEK_SET);
int checksum, loops = fileLen / BLOCKSIZE;
int remainder = fileLen % BLOCKSIZE;
int segment_num = loops+1;
void* buffer4 = (void *)malloc(BLOCKSIZE+1);
if (!buffer4)
error("Error: malloc error for buffer.\n");
int i, sent = 0;
for (i=1; i<=loops; i++)
{
fread(buffer4, BLOCKSIZE, 1, fp_read);
checksum = checksumv(buffer4,BLOCKSIZE);
n = write(sock,buffer4,BLOCKSIZE);
if (n < 0)
error("ERROR writing to socket");
}
if (remainder > 0)
{
//Allocate memory
void* buffer5 = (void *)malloc(remainder+1);
if (!buffer5)
error("Error: malloc error for buffer2.\n");
fread(buffer5, remainder, 1, fp_read);
checksum = checksumv(buffer5,remainder);
n = write(sock,buffer5,remainder);
if (n < 0)
error("ERROR writing to socket");
}
When you are reading data:
n = read(sockfd,buffer,BLOCKSIZE);
if (n < 0)
error("Error: reading from socket.");
int checksum = checksumv(buffer,BLOCKSIZE);
you need to respect the number of bytes that read() says it placed into buffer - it may read fewer than BLOCKSIZE number of bytes before returning.
Also, I don't see where the checksum is sent (or received) - I only see the file data being sent. How are you comparing checksums?
Finally, since TCP is a streaming protocol, you'll need to have some way to indicate to receiver when the file data is finished such as by sending the size ahead of the file data, or having some 'out of band' indication.
You're not accumulating your checksum correctly, but you don't need all this complexity just to copy a file:
byte buffer[8192];
while ((count = fread(buffer, sizeof buffer, 1, fp)) > 0)
{
n = write(sock,buffer4,count);
if (n < 0)
error("ERROR writing to socket");
checksum += checksumv(buffer, count); // or possibly ^=, it depends how your checksum is supposed to accumulate
}