So I programmed a multi threaded web server, here is one function from the program. This function takes output file descriptor (fd), content type, pointer to data to be served (*buf) and size of the data (numbytes). It always gets stuck at 5775 bytes! I've tried using write() instead of send(), but no avail! I tried to send whole buf at a time, and even tried to transfer it in chunks, but wget shows that it gets stck at 5775 bytes! Here is the code:
int return_result(int fd, char *content_type, char *buf, int numbytes)
{
char out_buf[BUF_SIZE], numb[6];
int buf_len, total = 0, buf_size;
long int i = 0;
sprintf(numb, "%d", numbytes);
strcpy(out_buf, "HTTP/1.1 200 OK \nContent-Type: ");
strcat(out_buf, content_type);
strcat(out_buf, "\nContent-Length: ");
strcat(out_buf, numb);
strcat(out_buf, "\nConnection: Close\n \n");
printf("\nSending HTTP Header\n %d bytes sent!",
send(fd, out_buf, strlen(out_buf), 0));
char *start = NULL, *str = NULL, *temp = NULL;
start = buf;
printf("\n Start Pointer Val = %ld", &start);
while (start != NULL) {
printf("\n While Loop");
if (i + 2048 * sizeof(char) < numbytes) {
printf("\n If 1");
str = (char *)malloc(sizeof(char) * 2048);
memcpy(str, start, sizeof(char) * 2048);
i = i + 2048 * sizeof(char);
buf_size = send(fd, str, 2048, 0);
free(str);
printf("\n Sent %d bytes total : %d", buf_size, total =
total + buf_size);
temp = start + sizeof(char) * 2048;
start = temp;
} else {
i = numbytes - i * sizeof(char);
if (i > 0) {
printf("\n If 2");
printf("\n Value of i %d", i);
str = (char *)malloc(sizeof(char) * i);
memcpy(str, start, sizeof(char) * i);
printf("Total bytes finally sent:%d", total =
total + send(fd, str, i, 0));
if (total == numbytes) {
printf("\nTransfer Complete!");
}
free(str);
}
start = NULL;
}
}
printf("out of loop!");
return 0;
}
I'd like to suggest replacing your code with the following writen() function from Advanced Programming in the Unix Environment, 2nd edition:
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
This code is already debugged and known working, and further allows write(2) to write PIPE_BUF bytes at a go for better speed when things are working well.
send(2) should block if it cannot send all the data you have requested, though. I think more interesting would be debugging the version with plain send(2) without any of the surrounding efforts to break things into blocks.
Better than both write(2) and send(2) would be sendfile(2) -- open the file, pass the descriptor and socket to sendfile(2), and let the kernel handle it all for you, using zero-copy mechanisms if possible.
One last point: HTTP uses CRLF, not plain carriage returns. Each \n should be replaced with \r\n.
Try something like this (printf() statements omitted for clarity):
int send_buf(in fd, void *buf, int numbytes)
{
char *start = (char*) buf;
while (numbytes > 0)
{
int sent = send(fd, start, numbytes, 0);
if (sent <= 0)
{
if ((sent == -1) && (errno == EAGAIN))
{
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
if (select(fd + 1, NULL, &wfds, NULL, NULL) == 1)
continue;
}
return -1;
}
start += sent;
numbytes -= sent;
}
return 0;
}
int return_result(int fd, char *content_type, void *buf, int numbytes)
{
char out_buf[BUF_SIZE],
int len = sprintf(out_buf,
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: Close\r\n"
"\r\n",
content_type,
numb);
if (send_buf(fd, out_buf, len) != 0)
return -1;
if (send_buf(fd, buf, numbytes) != 0)
return -1;
return 0;
}
Related
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 have an assignment in which a TCP client sends data to the TCP server in the form of:
IP_address\0port\0message\n
Now, the server (IP address 10.0.2.15) receives the packet fine when I send some data through a terminal like this:
printf "127.0.0.1\0004444\000Some message\n" | nc -N 10.0.2.15 3333
However, the second part of the assignment is to read a packet that comes in multiple segments:
(printf "127.0.0.1"; sleep 0.3; printf "\0004444\000"; sleep 0.3; \
printf "It works"; sleep 0.5; printf "\n") | nc -N 10.0.2.15 3333
How should I implement the read function on the server so that, if possible, all the segments are stored into a buffer?
The number of bytes recv() returns can be as few as 1 byte up to as many bytes as requested. TCP is a byte stream, it has no concept of messages, that has to be handled in the application code instead.
The receiver must know how many bytes to expect, and then keep reading in a loop until it has read that many bytes, however many reads it takes.
However, in this situation, the receiver does not know the exact length of the message, because the sender is not sending the message length before sending the message itself, so the only option available is for the receiver to read from the socket byte-by-byte until it encounters the terminating \n.
For example:
int readLine(int socket, char **line)
{
int r, len = 0, cap = 256;
char b;
*line = NULL;
char *outline = (char*) malloc(cap);
if (!outline) return -2;
do
{
r = recv(socket, &b, 1, 0);
if (r <= 0)
{
free(outline);
return r;
}
if (b == '\n')
break;
if (len == cap)
{
cap += 256;
char *newline = (char*) realloc(outline, cap);
if (!newline)
{
free(outline);
return -2;
}
outline = newline;
}
outline[len] = b;
++len;
}
while (true);
if ((len > 0) && (line[len-1] == '\r'))
--len;
if (len == cap)
{
char *newline = (char*) realloc(outline, cap + 1);
if (!newline)
{
free(outline);
return -2;
}
outline = newline;
}
outline[len] = '\0';
*line = outline;
return 1;
}
char *line;
int r;
do
{
r = readLine(cliSock, &line);
if (r <= 0)
{
if (r == 0)
printf("client disconnected\n");
else if (r == -2)
printf("memory error\n");
else
printf("read error\n");
break;
}
// process line as needed...
free(line);
}
while (true);
Alternatively, you can use an intermediate buffer to help you cache data between reads and get data out of the socket more efficiently:
char *buffer;
int buflen, bufcap;
int readLine(int socket, char **line)
{
char *ptr;
int r, idx = 0;
*line = NULL;
do
{
ptr = memchr(buffer + idx, '\n', buflen - idx);
if (ptr)
{
int total = ((ptr + 1) - buffer);
int len = (total - 1);
if ((len > 0) && (buffer[len-1] == '\r'))
--len;
*line = (char*) malloc(len + 1);
if (*line == NULL)
return -2;
memcpy(*line, buffer, len);
(*line)[len] = '\0';
if (total < buflen)
memmove(buffer, buffer + total, buflen - total);
buflen -= total;
break;
}
if (buflen == bufcap)
{
int newcap = bufcap + 256;
char *newbuffer = (char*) realloc(buffer, newcap);
if (!newbuffer)
return -2;
buffer = newbuffer;
bufcap = newcap;
}
r = recv(socket, buffer + buflen, bufcap - buflen, 0);
if (r <= 0)
return r;
buflen += r;
}
while (true);
return 1;
}
buflen = 0;
bufcap = 256;
buffer = (char*) malloc(bufcap);
if (buffer)
{
char *line;
int r;
do
{
r = readLine(cliSock, &line);
if (r <= 0)
{
if (r == 0)
printf("client disconnected\n");
else if (r == -2)
printf("memory error\n");
else
printf("read error\n");
break;
}
// process line as needed...
free(line);
}
while (true);
free(buffer);
}
I'm trying to get the source code of my website using c, I'm able to connect and everything but when I implement the recv() code, it only receives the last few bytes of the source code. I'd like to dynamically allocate space for the buffer to receive more using the C functions malloc and realloc.
This is the code I have so far:
char *buffer = NULL;
unsigned int i = 0;
unsigned long LEN = 200;
unsigned long cur_size = 0;
buffer = (char*)malloc(sizeof(char)*LEN);
do
{
if( status >= LEN )
{
cur_size += status;
buffer = (char*)realloc(buffer, cur_size);
}
status = recv(cSocket, buffer, LEN, 0);
if( status == 0 )
{
printf("Bye\n");
}
else if( status > 0 )
{
printf("%d\n", status);
}
else
{
printf("socket error=%d\n", WSAGetLastError());
break;
}
}while( status > 0 );
printf("%s\n", buffer);
It still doesn't print the whole source code. How should I go about this?
Pseudocode:
buffer = 'len chars';
loop:
if( status >= buffer ) buffer = 'resize to status chars';
status = recv(sock, buffer, len, 0);
end loop
As you resize the buffer in advance this needs to be reflected by its size. Which currently is not the case.
To fix this you could, for example, initialise cur_size with LEN by changing
unsigned long cur_size = 0;
to
unsigned long cur_size = LEN;
Assuming the fix above, you want to append to the buffer and not overwrite it with every call to recv().
To do so change this line
status = recv(cSocket, buffer, LEN, 0);
to be
status = recv(cSocket, buffer + cur_size - LEN, LEN, 0);
A more straight forward approach would be to not track the size of the buffer, but the number of bytes received and just always increase the buffer by a constant size.
Also the two calls to allocate memory can be replaced by one:
char *buffer = NULL;
unsigned long LEN = 200;
unsigned long bytes_received = 0;
unsigned long cur_size = 0;
int status = 0;
do
{
if (bytes_received >= cur_size)
{
char * tmp;
cur_size += LEN;
tmp = realloc(buffer, cur_size);
if (NULL == tmp)
{
fprintf(stderr, "realloc error=%d\n", WSAGetLastError());
break;
}
buffer = tmp;
}
status = recv(cSocket, buffer + bytes_received, LEN, 0);
if (status == 0)
{
printf("Bye\n");
}
else if (status > 0)
{
bytes_received += status;
printf("%d\n", status);
}
else /* < 0 */
{
fprintf(stderr, "socket error=%d\n", WSAGetLastError());
}
} while (status > 0);
printf("%s\n", buffer);
Well, after a bit of research, I came across this website and finally found what I was looking for.
Binary tides
Although it uses linux's fcntl, the windows equivalent is ioctlsocket which is used to set the socket's non-blocking mode.
To see the exact function, visit the website. I modified the version and set my socket to blocking mode.
int total_recv(SOCKET s)
{
int size_recv = 0, total_size = 0, block = 00;
char chunk[BUFLEN];
ioctlsocket(s, FIONBIO, (unsigned long*)&block); // set mode to block
// not necessary but clarification of function, mode is block by
// default
while( 1 )
{
memset(chunk, 0, BUFLEN);
if( ( size_recv = recv(s, chunk, BUFLEN, 0) ) == SOCKET_ERROR )
{
printf("Error receiving\n");
}
else if( size_recv == 0 )
{
break;
}
else
{
total_size += size_recv;
// i used file since console wouldn't show full source code
FILE *fp = NULL;
fp = fopen("source.txt", "a");
fprintf(fp, chunk);
fclose(fp);
}
}
return total_size;
}
I have a Server that contact another Server in order to obtain a file and then send it to the Client. I have write this piece of code (C - Linux) but only the first 4 bytes arrive to the Client.
Someone better than me can see the mistake?
Thank you very much
int Recv_file (int s, int f, char *ptr, size_t maxlen){
int const TIMEOUT = 60; /* 60 seconds */
size_t n;
ssize_t nread;
ssize_t nsend;
char c;
fd_set cset;
struct timeval tval;
int x;
for (n=1; n<maxlen; n++)
{
FD_ZERO(&cset);
FD_SET(s, &cset);
tval.tv_sec = TIMEOUT;
tval.tv_usec = 0;
x = select(FD_SETSIZE, &cset, NULL, NULL, &tval);
if (x==-1) {
perror("select() failed");
return -1; /* -1 = close connection with the client */
}
if (x>0) {
nread=recv(s, &c, 1, 0);
if (nread == 1)
{
*ptr++ = c;
nsend = Send(f,&c,1);
if (nsend != 1) {
return -1; /* close connection with the client */
}
}else if (nread == 0){
*ptr = 0;
return (-1); /* close connection with the client */
}
else
return (-1); /* close connection with the client */
}else{
printf("(It's been %d seconds and I have not received any response",TIMEOUT);
close(s);
return(-1); /* close connection with the client */
}
}
*ptr = 0;
return (n); /* n == maxlen */
}
with:
int Send(int s, char *ptr, size_t nbytes){
size_t nleft;
ssize_t nwritten;
for (nleft=nbytes; nleft > 0; )
{
nwritten = send(s, ptr, nleft, 0);
if (nwritten <=0)
return (nwritten);
else
{
nleft -= nwritten;
ptr += nwritten;
}
}
return (nbytes - nleft); /* number of bytes sent */
}
UPDATE:
While waiting for an answer I modified:
nread=recv(s, &c, 1, 0); in nread=recv(s, &c, sizeof(c), 0);
and
Send(f,&c,1); in Send(f,&c,sizeof(c));
But now I don't receive the last bytes of the file!
Thank you again!
You don't get the last byte because the for loop start at 1
for (n=1; n<maxlen; n++);
// it should be
for (n=0; n<maxlen; n++);
// or either
for (n=1; n<=maxlen; n++);
EDIT: Also, you could read as much bytes as are available not just one by one, in fact you are sending in the correct way,
For example:
int bLeft = maxlen;
while (bLeft > 0)
{
FD_ZERO(&cset);
FD_SET(s, &cset);
tval.tv_sec = TIMEOUT;
tval.tv_usec = 0;
x = select(FD_SETSIZE, &cset, NULL, NULL, &tval);
if (x > 0) {
nread=recv(s, ptr, bLeft , 0);
if (nread > 0)
{
nsend = Send(f, ptr, nread, 0);
if (nsend <= 0) {
return -1; /* close connection with the client */
}
ptr+ = nread;
bLeft -= nread;
}else if (nread == 0){
*ptr = 0;
return (-1); /* close connection with the client */
}
else
return (-1); /* close connection with the client */
}else{
printf("(It's been %d seconds and I have not received any response",TIMEOUT);
close(s);
return(-1); /* close connection with the client */
}
}
// I would not recommended this, you should adhere to a maxlen length
// assuming there is more space could lead to a memory Exception.
ptr++;
*ptr ='\0';
return (maxlen-bLeft);
My server code is as follows:
while(bytes_written < filesize){
//Send from send_ptr
bw = send(child_socket, send_ptr, newLength, 0);
printf("%d bytes written\n", (int)bw);
//Increment bytes written
bytes_written += bw;
//Move send pointer
send_ptr = send_ptr + bw;
}
And my client code is as follows:
while((num_bytes_recv = read(sd, jpgBufferPointer, BUFFER_LENGTH))>0){
total_bytes_recv += num_bytes_recv;
printf("Read %d bytes\n",num_bytes_recv);
//Check for error
if(jpgError == NULL)
jpgError = strstr(jpgBufferPointer, "404 Not Found");
if(jpgError != NULL){
//Forwarding error response
if(send(sd, jpgBuffer, num_bytes_recv, 0) == -1){
error("Failed to send response message to client");
}
}
else{
//Find content size
contentSizeBuffer = strstr(jpgBufferPointer,"Content-Length");
if(contentSizeBuffer != NULL){
contentSizeBuffer=contentSizeBuffer+16;
contentSize=atoi(contentSizeBuffer);
jpgBuffer=(char*)realloc(jpgBuffer,(contentSize+FILE_NAME_LENGTH*2)*sizeof(char));
jpgBufferPointer=jpgBuffer;
}
jpgBufferPointer+=num_bytes_recv;
}
}
The server is saying it has sent all 43000 bytes, but client says it has received only 32768 bytes.
Appreciate any help! Thanks
You have a bug in the sending part, you should update newLength, because if you have 1 byte left to send from the file, it will send more, going out of the memory area where the content you want to send is stored. You should fix in this way:
bw = send(child_socket, send_ptr, newLength<(filesize-bytes_written)?newLength:(filesize-bytes_written), 0);
In this way the last send will have the correct size.
Also, use write instead of send if you are not using any flags.
You need to have the similar loop as you have on the writing side (bytes_written < filesize) on the reading side (i.e., while you can read more bytes, you should read them and append them).
The network doesn't guarantee that one read() call will return all available data.
The best way of writing client-server socket programming is to have a header before your data. The header should state the amount of data that it is going to transfer.
For example, To send data "Hello World", then send it as "0011+HELLO WORLD"
Here 11 stands for the size of the data the sender is planning to send now. The receiver on reading the first 4 bytes can understand that he should be ready to read next 11 bytes of data from the sender.
So reader will do two read:
hRead = 5 /* With 5 you are saying it can read upto max of 9999 bytes from data".
read(sd, buff, hRead);
dRead = atoi(buff);
readn(sd, buff, dRead);
For Example : Server
size_t sendn(int fd, const void *vptr, size_t n) {
size_t nleft;
size_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = send(fd, vptr, nleft, 0)) <= 0) {
if (errno == EINTR)
nwritten = 0;
else {
fprintf(stderr, "send failed %d - %s\n", fd, strerror(errno));
return (-1);
}
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
To send message:
sprintf(buff, "%d + %d + %s\r\n", MSG_LOGIN, strlen("Hello World"), Hello World);
sendn(sd, buff, strlen(buff));
Client:
size_t readn(int fd, void *vptr, size_t n) {
size_t nleft;
size_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nread = recv(fd, ptr, nleft, 0)) < 0) {
if (errno == EINTR)
nread = 0;
else {
fprintf(stderr, "read failed %d - %s\n", fd, strerror(errno));
return (-1);
}
} else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return (n - nleft);
}