I just implemented a HTTP/1.1 client to parse the chunked transferring coding. However, it works for some websites but fails for others. I assume I need to read chunkSize + 2 bytes including \r\n for each chunk data, am I right?
Here is my code:
while(chunked)//if detecting chunked in the header before, this is true
{
//getLine is a function can read a line separated by \r\n
//sockfd is a socket created before and file position is at the start of HTTP body (after that blank line between header and body)
line = getLine(sockfd);
printf("%s", line);//print the chunk size line in hex
int chunkSize = strtol(line, NULL, 16);
if(chunkSize == 0)
{
printf("##### Read chunk size of 0, reading until we hit end of stream.\n");
break;
}
printf("##### Chunk size (in hex above) is %d in decimal and is printed here:\n", chunkSize);
char* chunkBuf = (char *)malloc(chunkSize + 2 + 1);//2 for \r\n, 1 for \0
bzero(chunkBuf, chunkSize + 3);
if(read(sockfd, chunkBuf, chunkSize + 2) == 0)//sockfd is a socket created before
{
perror("Read Error: ");
exit(EXIT_FAILURE);
}
printf("%s", chunkBuf);//print the chunk content
free(chunkBuf);
}
Actually I can print out the whole content without parsing, i.e. print line by line, so I think I may make some mistakes in the code above, could anyone give me some hint?
Below is the whole code for your reference:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#define HTTP_VERSION "HTTP/1.1"
#define PAGE "/"
int createSokect()
{
int socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(socketfd < 0)
{
perror("Cannot create socket\n");
exit(EXIT_FAILURE);
}
return socketfd;
}
char* getIP(char* host)
{
struct hostent* hent;
int len = 15;//xxx.xxx.xxx.xxx
char *ipaddr = (char *)malloc(len + 1);//one more \0
bzero(ipaddr, len + 1);
if((hent = gethostbyname(host)) == NULL)
{
printf("Cannot get IP for this host: %s\n", host);
exit(EXIT_FAILURE);
}
if(inet_ntop(AF_INET, (void*)hent->h_addr_list[0], ipaddr, len) == NULL)
{
printf("Cannot resolve IP for this host: %s\n", host);
exit(EXIT_FAILURE);
}
return ipaddr;
}
char* createQuery(char* host, char* page)
{
char* msg = "GET %s %s\r\nHost: %s\r\nConnection: close\r\n\r\n";
char* query = (char *)malloc(strlen(host) + strlen(page) + strlen(msg) + strlen(HTTP_VERSION) - 6 + 1);//-6: %s %s %s
sprintf(query, msg, page, HTTP_VERSION, host);
return query;
}
char* getLine(int fd)
{
char c = 0, pre = 0;
char* line = 0;
int size = 1;
int pos = 0;
while(read(fd, &c, 1)!=0)
{
if(pos + 1 == size)
{
size *= 2;
line = realloc(line, size);
}
line[pos++] = c;
//printf("%c", c);
if(pre == '\r' && c == '\n')//this is a new line
{
break;
}
pre = c;
}
if(line)
{
line[pos++] = 0;
}
return line;
}
int main(int argc, char** argv)
{
if(argc < 3)
{
perror("Need more arguments");
exit(EXIT_FAILURE);
}
int sockfd = createSokect();
char* ip = getIP(argv[1]);
printf("Host: %s\n", argv[1]);
printf("IP: %s\n", ip);
struct sockaddr_in server;
server.sin_family = AF_INET;
int err = inet_pton(server.sin_family, ip, (void *)(&(server.sin_addr.s_addr)));
if(err != 1)
{
perror("Cannot convert IP to binary address\n");
exit(EXIT_FAILURE);
}
server.sin_port = htons(atoi(argv[2]));
printf("port: %d\n", server.sin_port);
//connect to the server
if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("Cannot connect: %d\n", err);
exit(EXIT_FAILURE);
}
char* query = createQuery(argv[1], PAGE);
printf("##### CLIENT IS SENDING THE FOLLOWING TO SERVER:\n");
printf("%s", query);
int offset = 0;
//send query to the server
err = send(sockfd, query + offset, strlen(query) - offset, 0);
if(err < 0)
{
perror("Cannot send query");
exit(EXIT_FAILURE);
}
printf("##### CLIENT RECEIVED THE FOLLOWING FROM SERVER:\n");
//receive message line by line
bool chunked = false;
char* line;
while((line = getLine(sockfd)) != NULL)
{
printf("%s", line);
if(!strcasecmp(line, "transfer-encoding: chunked\r\n"))
{
chunked = true;
//printf("Chunked here\n");
}
if(!strcmp(line, "\r\n"))
{
printf("##### Just read blank line, now reading body.\n");
if(chunked)//chunked, we print those in another way, otherwise line by line
{
free(line);
break;
}
}
free(line);
}
while(chunked)
{
line = getLine(sockfd);
printf("%s", line);
int chunkSize = strtol(line, NULL, 16);
if(chunkSize == 0)
{
printf("##### Read chunk size of 0, reading until we hit end of stream.\n");
break;
}
printf("##### Chunk size (in hex above) is %d in decimal and is printed here:\n", chunkSize);
char* chunkBuf = (char *)malloc(chunkSize + 2 + 1);//2 for \r\n, 1 for \0
bzero(chunkBuf, chunkSize + 3);
if(read(sockfd, chunkBuf, chunkSize + 2) == 0)
{
perror("Read Error: ");
exit(EXIT_FAILURE);
}
printf("%s", chunkBuf);
free(chunkBuf);
}
//receive message from the server
/*
char buf[2048];
bzero(buf, sizeof(buf));
err = recv(sockfd, buf, sizeof(buf), 0);
if(err < 0)
{
perror("Receive error");
exit(EXIT_FAILURE);
}
char *content = buf;
fprintf(stdout, content);*/
free(query);
free(ip);
close(sockfd);
printf("##### Connection closed by server.\n");
exit(EXIT_SUCCESS);
}
The line:
if(read(sockfd, chunkBuf, chunkSize + 2) == 0) ...
will read up to chunkSize+2, i.e. it can read less. See the manual page of read. Your code shall look something like:
int n = 0;
while (n<chunkSize) {
r = read(sockfd, chunkBuf+n, chunkSize - n);
if (r <= 0) { error or closed conection ... }
n += r;
}
Since I know the chunk size, so I read character one by one counting up to the chunk size. This way can work. But I still don't understand why I failed when trying to use read or recv by the whole chunk size at one time.
Related
Im trying to make a distributed client / server application for file sharing. I firstly made a LIST_FILES function which reads and prints all the files of your directory, but Im having trouble deleting and renaming .txt files. If I manually use the name of the .txt in the functions, it works for both, but I dont know how to properly read from the server which file I want to use. Im using cygwin, C code and Windows.
Example: ./server.exe --> ./client.exe 127.0.0.1 DELETE_FILE test.txt or MOVE_FILE test.txt newtest.txt
SERVER
#include <stdio.h>
#include <string.h> //strlen
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <unistd.h> // Close sockets
#include <dirent.h>
#define MAXDATASIZE 5000
#define _BUFFER_SIZE 2000
#define ECHO_PORT 5675
#define FAIL "SEND_FAILED"
long fileSize(const char fileNom[]);
int getFiles(char *cadena, int longitud);
int main(int argc, char *argv[])
{
char *namefile;
int i, del, ren, leng, length2, length3;
int socket_desc, accepted_socket, connfd = 0;
struct sockaddr_in server_addr; // Direccion del servidor
char down[_BUFFER_SIZE];
char response[_BUFFER_SIZE];
char reply[_BUFFER_SIZE] = "SUCCESS";
char reply2[_BUFFER_SIZE] = "ERROR";
char buffer[_BUFFER_SIZE];
char buffer2[_BUFFER_SIZE];
char buffer3[_BUFFER_SIZE];
char ack[_BUFFER_SIZE] = "ACK";
printf("Initializing echo server\n");
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0); // Sock stream --> SOCKET TCP
if (socket_desc < 0)
{
printf("Could not create socket");
return 0;
}
printf("Socket created.\n");
char *address = "0.0.0.0"; // Accept connections from the whole Internet
if (argc > 1)
address = argv[1];
server_addr.sin_addr.s_addr = inet_addr(address);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(ECHO_PORT);
bind(socket_desc, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(socket_desc, 10);
int exit = 0;
while (!exit)
{
printf("Echo server. Waiting for incoming connections from clients.\n");
accepted_socket = accept(socket_desc, (struct sockaddr *)NULL, NULL);
printf("Accepted a connection from client\n");
// First wait for the command of the client
ssize_t len = recv(accepted_socket, buffer, _BUFFER_SIZE, 0);
// If the client sends two or more arguments, the second is the file's name
if (len < 0)
{
printf("recv failed\n");
}
else
{
char fileName[_BUFFER_SIZE];
char command[_BUFFER_SIZE];
char command2[_BUFFER_SIZE];
printf("Received command from client: %s\n", buffer); // TODO: If the message is too long --> may print garbage
/*
We check the length of buffer2 and buffer3, if the length is
>= 1, it means that the client sent those
*/
length2 = strlen(buffer2);
if (length2 >= 1)
printf("Received file's name from client: %s\n", buffer2);
length3 = strlen(buffer3);
if (length3 >= 1)
printf("Received new file's name from client: %s\n", buffer3);
if (strcmp(buffer, "LIST_FILES") == 0)
{
printf("Received LIST_FILES command --> stopping server\n");
getFiles(buffer, _BUFFER_SIZE);
printf("Directory list: \n %s", buffer);
len = strlen(buffer);
if (send(accepted_socket, buffer, len, 0) < 0)
{
printf("Send failed\n");
return 1;
}
}
else if (strcmp(buffer, "DOWNLOAD_FILE") == 0)
{
i = 10;
long length = 0;
length = fileSize(buffer2);
printf("Received DOWNLOAD_FILE command %s, size: %ld \n", buffer2, length);
if (length >= 0)
{
//We enter in the If command just in case the size of the file is positive
sprintf(down, "%ld", length);
send(accepted_socket, down, _BUFFER_SIZE, 0);
recv(accepted_socket, ack, _BUFFER_SIZE, 0);
if (strcmp(ack, "ACK") == 0) //Checking the ACK message from the client
{
printf("ACK RECEIVED \n");
/* Open the file that we wish to transfer */
FILE *fp = fopen(buffer2, "rb");
if (fp == NULL)
{
printf("Error opening the file");
return 1;
}
else
{
while (1)
{
//Reading and sending the file the client wants to download
unsigned char buff[_BUFFER_SIZE] = {0};
int nread = fread(buff, 1, _BUFFER_SIZE, fp);
printf("Bytes read %d \n", nread);
if (nread > 0)
{
printf("Sending \n");
write(accepted_socket, buff, nread);
}
if (nread < _BUFFER_SIZE)
{
if (feof(fp))
printf("\nEnd of file\n");
if (ferror(fp))
printf("\nError reading\n");
break;
}
}
}
}
}
else
{
strcpy(buffer, reply2); //If ack doesn't is received, we send ERROR
printf("Error: unable to download the file\n");
send(accepted_socket, reply2, len, 0);
}
}
else if (strcmp(buffer, "DELETE_FILE") == 0)
{
del = remove(buffer2);
if (del == 0)
{
strcpy(buffer, reply);
printf("File deleted sucessfully\n");
send(accepted_socket, reply, len, 0);
}
else
{
strcpy(buffer, reply2);
printf("Error: unable to delete the file\n");
send(accepted_socket, reply2, len, 0);
}
}
else if (strcmp(buffer, "MOVE_FILE") == 0)
{
ren = rename("prueba.txt", "nuevaprueba.txt");
if (ren == 0)
{
strcpy(buffer, reply);
printf("File renamed successfully\n");
send(accepted_socket, reply, len, 0);
}
else
{
strcpy(buffer, reply2);
printf("Error: unable to rename the file\n");
send(accepted_socket, reply2, len, 0);
}
}
close(accepted_socket);
printf("Accepted connection closed.\n");
sleep(1);
}
}
printf("Closing binded socket\n");
close(socket_desc);
return 0;
}
int getFiles(char *cadena, int longitud)
{
int cont = 0;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
while ((dir = readdir(d)) != NULL)
{
cont += sprintf(cadena + cont, "%s\n", dir->d_name); // ¿Cuántos caracteres escribe la función? Contador, salir
}
closedir(d);
}
else
{
return -1;
}
return cont;
}
long fileSize(const char fileNom[])
{
long size = -1;
FILE *fich;
fich = fopen(fileNom, "rb");
if (fich != NULL)
{
fseek(fich, 0L, SEEK_END);
size = ftell(fich);
fclose(fich);
}
return size;
}
CLIENT
#include <stdio.h>
#include <string.h> //strlen
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <unistd.h>
#include <stdlib.h>
#define ECHO_PORT 5675
#define _BUFFER_SIZE 20000
int main(int argc, char *argv[])
{
int len = 0, len2 = 0, bytesReceived = 0;
int socket_desc;
struct sockaddr_in server;
char received[_BUFFER_SIZE];
char *message, server_reply[_BUFFER_SIZE], server_status[_BUFFER_SIZE], file_size[_BUFFER_SIZE];
char *newNameOfFile = "";
char *nameOfFile = "";
char *comp;
char *ack = "ACK";
printf("Initializing socket\n");
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
char *address = "127.0.0.1";
if (argc > 1)
address = argv[1]; // Si existe, el primer argumento es la IP
server.sin_addr.s_addr = inet_addr(address);
server.sin_family = AF_INET;
server.sin_port = htons(ECHO_PORT);
printf("Trying to connect to address: %s\n", address);
//Connect to remote server
if (connect(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("Connect error\n");
return 1;
}
printf("Connected Successfully\n");
//Send some data
message = "ECHO";
if (argc > 2)
message = argv[2]; // If exists, the second argument is the message
if (send(socket_desc, message, strlen(message) + 1, 0) < 0) // Importante el +1 para enviar el finalizador de cadena!!
{
printf("Send failed\n");
return 1;
}
printf("Message sent: %s\n", message);
//Send the file we want to
if (argc > 3)
nameOfFile = argv[3]; // If exists, the third argument is the file name
if (send(socket_desc, nameOfFile, strlen(nameOfFile) + 1, 0) < 0)
{
printf("Send failed\n");
return 1;
}
//We check if NameOfFile's size is >= 1
len = strlen(nameOfFile);
if (len >= 1)
printf("File name sent: %s\n", nameOfFile);
//Send the new file's name
if (argc > 4)
newNameOfFile = argv[4]; // If exists, the fourth argument is the new file's name
//Sending data to the server
if (send(socket_desc, newNameOfFile, strlen(newNameOfFile) + 1, 0) < 0)
{
printf("Send failed\n");
return 1;
}
//We check if newNameOfFile's size is >= 1
len2 = strlen(newNameOfFile);
if (len2 >= 1)
printf("New file's name sent: %s\n", newNameOfFile);
//Receive a reply from the server
if (recv(socket_desc, server_status, _BUFFER_SIZE, 0) >= 1)
{
printf("%s\n", server_status);
}
//We change the type of the data
long size = strtol(server_status, &comp, 10);
if (size >= 1)
{
send(socket_desc, ack, _BUFFER_SIZE, 0);
}
/*If our order is DOWNLOAD_FILE, its create the file
where we are storing the data*/
if ((strcmp(message, "DOWNLOAD_FILE") == 0) && size >= 1)
{
FILE *fp;
fp = fopen(nameOfFile, "wb");
if (NULL == fp)
{
printf("Error opening file");
return 1;
}
/* Receive data in chunks of 2000 bytes */
while ((bytesReceived = read(socket_desc, received, _BUFFER_SIZE)) > 0)
{
printf("Bytes received %d\n", bytesReceived);
fwrite(received, 1, bytesReceived, fp);
}
if (bytesReceived < 0)
{
printf("\n Read Error \n");
}
else
{
printf("SUCCESS");
}
}
//Help Command, general and particullary for each command
if (strcmp(message, "HELP") == 0)
{
printf("List of available commands: \n-LIST_FILES\n-DOWNLOAD_FILE <filename>\n-DELETE_FILE <filename>\n-MOVE_FILE <old_filename> <new_filename>\n \n \n For more help, type 'HELP_command'\n");
}
else if (strcmp(message, "HELP_LIST_FILES") == 0)
{
printf("Command: LIST_FILES \n");
printf("-Shows every existing file in the folder. \n");
}
else if (strcmp(message, "HELP_DOWNLOAD_FILE") == 0)
{
printf("Command: DOWNLOAD_FILES <filename> \n");
printf("-Downloads the requested file. \n");
}
else if (strcmp(message, "HELP_DELETE_FILE") == 0)
{
printf("Command: DELETE_FILE <filename> \n");
printf("-Deletes the requested file. \n");
}
else if (strcmp(message, "HELP_MOVE_FILE") == 0)
{
printf("Command: MOVE_FILE <old_filename> <new_filename> \n");
printf("-Renames the old_filename for the new_filename. \n");
}
close(socket_desc);
return 0;
}
I have been trying to get this FTP client/ server to work together but its currently not playing friendly
Bits of code that I have tested and that I couldn't find to work have been commented but also some of these commented recv and write lines work but to make the over all problem simpler i have commented them out for the time being. The problem seems to be in the ConnectionHandler function under the else if(strstr(client_request, "Put")) line of code. i have currently got successful FTP from the server to the client but the client to server part I'm struggling to get to work any advice would be grateful.
I feel as though this line is the problem file_desc = open(file_name, O_CREAT | O_EXCL | O_WRONLY, 0777); but i need to create the file to run to but file_desc returns -1 even though the file is created and I'm simply unable to determine why this is the case. Ultimately i may be overlooking something else.
Server
/*
* Compile & link : gcc server.c -lpthread -o server
* Execute : ./server
*/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#define SERVER_PORT 8080
void* ConnectionHandler(void* socket_desc);
char* GetFilenameFromRequest(char* request);
bool SendFileOverSocket(int socket_desc, char* file_name);
int readn(int fd, char *buf, int bufsize)
{
short data_size; // sizeof (short) must be 2
int n, nr, len;
// check buffer size len
if (bufsize < BUFSIZ)
return (-3); // buffer too small
// get the size of data sent to me
if (read(fd, (char *) &data_size, 1) != 1)
return (-1);
if (read(fd, (char *) (&data_size)+1, 1) != 1)
return (-1);
len = (int) ntohs(data_size); // convert to host byte order
// read len number of bytes to buf
for (n=0; n < len; n += nr)
{
if ((nr = read(fd, buf+n, len-n)) <= 0)
return (nr); // error in reading
}
return (len);
}
int main(int argc, char **argv)
{
int socket_desc,
socket_client,
*new_sock,
c = sizeof(struct sockaddr_in);
struct sockaddr_in server,
client;
// Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
perror("Could not create socket");
return 1;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(SERVER_PORT);
if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("Bind failed");
return 1;
}
listen(socket_desc , 3);
while (socket_client = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c))
{
pthread_t sniffer_thread;
new_sock = malloc(1);
*new_sock = socket_client;
pthread_create(&sniffer_thread, NULL, ConnectionHandler, (void*) new_sock);
pthread_join(sniffer_thread, NULL);
}
if (socket_client<0)
{
perror("Accept failed");
return 1;
}
return 0;
}
void *ConnectionHandler(void *socket_desc)
{
int socket = *(int*)socket_desc;
char server_response[BUFSIZ],
client_request[BUFSIZ],
file_name[BUFSIZ];
recv(socket, client_request, BUFSIZ, 0); //Wait at this point to recieve a message from the client
if(strstr(client_request, "Get"))
{
perror("1");
strcpy(file_name, GetFilenameFromRequest(client_request));
// If requested file exists, notify the client and send it
if (access(file_name, F_OK) != -1){
perror("2");
strcpy(server_response, "OK");
write(socket, server_response, strlen(server_response));
perror("3");
SendFileOverSocket(socket, file_name);
perror("4");
}
else {
// Requested file does not exist, notify the client
strcpy(server_response, "NO");
write(socket, server_response, strlen(server_response));
}
}
else if(strstr(client_request, "Put"))
{
char *data, reply_msg[BUFSIZ];
char buff[BUFSIZ];
int file_size, file_desc;
perror("0.5");
strcpy(file_name, GetFilenameFromRequest(client_request));
perror("0.75");
//recv(socket, reply_msg, 2, 0); //Receive reply message from client if file exists
//fprintf(stderr, "File was found = %s", reply_msg);
//Create File
perror("1");
recv(socket, &file_size, sizeof(int), 0);
perror("1.25");
file_desc = open(file_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
perror("1.5");
int incomingSize;
while((incomingSize = readn(socket, buff, sizeof(buff))) > 0)
{
perror("2 ");
for(int i = 0; i <= sizeof(buff); i++)
{
buff[incomingSize] = '\0';
write(file_desc, buff, sizeof(buff));
}
}
perror("3");
if(incomingSize < 0)
{
perror("Something went wrong connection broken");
return; // connection broken down
}
else
{
fprintf(stderr, "Connection terminated");
}
perror("4");
close(socket);
/*
perror("1");
data = malloc(BUFSIZ);
file_desc = open(file_name, O_CREAT | O_EXCL | O_WRONLY, 0777);
perror("1.2");
fprintf(stderr, "fileDesc = %d", file_desc);
perror("1.5");
strcpy(server_response, "OK");
write(socket, server_response, strlen(server_response));
perror("2");
//recv(socket, &file_size, sizeof(int), 0);
perror("3");
//recv(socket, &file_size, sizeof(int), 0);
file_size = BUFSIZ;
perror("4");
fprintf(stderr, "%d", file_size);
recv(socket, data, file_size, 0);
perror("5");
write(file_desc, data, file_size);
perror("6");
close(file_desc);
*/
/*// Start receiving file
if (strcmp(reply_msg, "OK") == 0) //Check that response is "OK"
{
}
else
{
fprintf(stderr, "Bad request\n");
}
*/
}
free(socket_desc);
return 0;
}
char* GetFilenameFromRequest(char* request)
{
char *file_name = strchr(request, ' ');
return file_name + 1;
}
bool SendFileOverSocket(int socket_desc, char* file_name)
{
struct stat obj;
int file_desc,
file_size;
stat(file_name, &obj);
file_desc = open(file_name, O_RDONLY);
file_size = obj.st_size;
send(socket_desc, &file_size, sizeof(int), 0);
sendfile(socket_desc, file_desc, NULL, file_size);
return true;
}
Client
/*
* Compile & link : gcc client.c -o client
* Execute : ./client
*
*/
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define FILENAME "a.txt" //Possibly worth changing to make it more dynamic
//Should consider overriding this with command line arguments if passed
#define SERVER_IP "127.0.0.1" //Address of local host
#define SERVER_PORT 8080
bool SendFileOverSocket(int socket_desc, char* file_name);
int writen(int fd, char *buf, int nbytes)
{
short data_size = nbytes; // short must be two bytes long
int n, nw;
if (nbytes > BUFSIZ)
return (-3); // too many bytes to send in one go
// send the data size
data_size = htons(data_size);
if (write(fd, (char *) &data_size, 1) != 1)
return (-1);
if (write(fd, (char *) (&data_size)+1, 1) != 1)
return (-1);
// send nbytes
for (n=0; n<nbytes; n += nw)
{
if ((nw = write(fd, buf+n, nbytes-n)) <= 0)
return (nw); // write error
}
return (n);
}
int main(int argc , char **argv)
{
int socket_desc;
// Variables for the file being received
int file_size, file_desc;
struct sockaddr_in server;
char request_msg[BUFSIZ],
reply_msg[BUFSIZ],
userCommand[BUFSIZ],
userFileName[BUFSIZ],
*data;
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
perror("Could not create socket");
return 1;
}
server.sin_addr.s_addr = inet_addr(SERVER_IP);
server.sin_family = AF_INET;
server.sin_port = htons(SERVER_PORT);
// Connect to server
if (connect(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("Connection failed");
return 1;
}
printf("------------Enter Command-----------\n");
printf("Get, Put, Pwd, lpwd, dir, ldir, cd, lcd, quit\n");
printf("------------------------------------\n");
while(userCommand != "quit")
{
printf(">");
fgets(userCommand, BUFSIZ, stdin);
printf("User Command Variable = %s", userCommand);
if(strstr(userCommand, "Get"))
{
char userFileName[BUFSIZ];
// Get a file from server
strcpy(request_msg, "Get "); //Copy message "Get " into variable request_msg
printf("Enter the file name you would like to send.");
scanf("%s", userFileName);
strcat(request_msg, userFileName); //Add the file name to the end of the varaible that is now "Get "
printf("1");
write(socket_desc, request_msg, strlen(request_msg)); //Changing this to fwrite produces same results with more warnings
printf("2");
recv(socket_desc, reply_msg, 2, 0); //Wait here for response
printf("3");
// Start receiving file
if (strcmp(reply_msg, "OK") == 0) //Check that response is "OK"
{
printf("4");
recv(socket_desc, &file_size, sizeof(int), 0);
printf("5");
data = malloc(file_size);
file_desc = open(userFileName, O_CREAT | O_EXCL | O_WRONLY, 0666);
recv(socket_desc, data, file_size, 0);
printf("6");
write(file_desc, data, file_size);
printf("7");
close(file_desc);
}
else
{
fprintf(stderr, "Bad request\n");
}
}
else if(strstr(userCommand, "Put"))
{
char server_response[BUFSIZ], buff[BUFSIZ];
struct stat obj;
fprintf(stderr,"What is the file name you would like to send?\n");
scanf("%s", userFileName);
fprintf(stderr,"File name entered was: %s", userFileName);
strcpy(request_msg, "Put "); //Copy message "Get " into variable request_msg
strcat(request_msg, userFileName); //Add the file name to the end of the varaible that is now "Get "
perror("1");
write(socket_desc, request_msg, strlen(request_msg)); //Changing this to fwrite produces same results with more warnings
perror("2");
stat(userFileName, &obj);
file_desc = open(userFileName, O_RDONLY);
file_size = obj.st_size;
perror("2.1");
strcpy(reply_msg, "OK");
perror("2.2");
//write(socket_desc, reply_msg, strlen(reply_msg));
perror("2.3");
perror("2.4");
perror("2.45");
writen(socket_desc, buff, sizeof(buff));
//SendFileOverSocket(socket_desc, userFileName);
perror("3");
close(socket_desc);
/*if (access(userFileName + 1, F_OK) != -1)
{
}
else {
printf("File doesnt exist with access code %d\n", access(userFileName, F_OK));
strcpy(reply_msg, "OK");
write(socket, reply_msg, strlen(reply_msg));
}*/
}
}
return 0;
}
/*bool SendFileOverSocket(int socket_desc, char* file_name)
{
struct stat obj;
int file_desc,
file_size;
stat(file_name, &obj);
file_desc = open(file_name, O_RDONLY);
file_size = obj.st_size;
printf("2.5");
printf("%s", file_name);
//send(socket_desc, &file_size, sizeof(int), 0);
printf("2.6");
perror("Pre");
sendfile(socket_desc, file_desc, NULL, file_size);
perror("sendfile");
printf("2.7");
return true;
}*/
I have a client and server, the server is setup this way:
int listenS = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
bind(listenS, (struct sockaddr*)&s, sizeof(s));
listen(listenS, QUEUE_LEN);
struct sockaddr_in clientIn;
int clientInSize = sizeof clientIn;
while (1)
{
int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
//......
(There are tests I just removed them to make the code more readable)
The client is just:
int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
{ //......
I get a connection, and everything is working great, the server recv a message the first time I send it from the client, but when I try to send another message to the server the server wont block the recv call and get nothing (returning the buffer size, not 0)
Here is the client code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#define PORT 0x0da2
#define IP_ADDR 0x7f000001
#define MAX_BUFFER_SIZE 1024
int send_all(int socket, void* buffer, size_t length)
{
char *ptr = (char*)buffer;
while (length > 0)
{
int i = send(socket, ptr, length, 0);
if (i < 1) return -1;
ptr += i;
length -= i;
}
return 0;
}
int main(int argc, char** argv)
{
if (argc > 1)
{
if ((strcmp(argv[1], "list-files") != 0) &&
(strcmp(argv[1], "upload-file") != 0) &&
(strcmp(argv[1], "download-file") != 0) &&
(strcmp(argv[1], "search") != 0))
{
perror("The arguments are incorrect.");
}
int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
{
perror("connect");
return 1;
}
printf("Successfully connected.\n");
char sendBuffer[MAX_BUFFER_SIZE];
int lenOfArgv = strlen(argv[1]);
int sendBufferIndex = 0;
for (int i = 0;
i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
i++, sendBufferIndex++)
{
sendBuffer[sendBufferIndex] = argv[1][i];
}
if (argc == 3)
{
sendBuffer[sendBufferIndex++] = ' ';
int lenOfArgv = strlen(argv[2]);
for (int i = 0;
i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
i++, sendBufferIndex++)
{
sendBuffer[sendBufferIndex] = argv[2][i];
}
}
sendBuffer[sendBufferIndex] = 0;
// + 1 for terminating null
if (send_all(sock, sendBuffer, strlen(sendBuffer) + 1) < 0)
{
perror("send buffer to server failed");
return 1;
}
if(strcmp(argv[1], "download-file") == 0)
{
char sizeBuffer[256];
recv(sock, sizeBuffer, 256, 0);
int fileSize = atoi(sizeBuffer);
if(fileSize > 0)
{
FILE* recievedFile = fopen(argv[2], "w");
if(recievedFile != NULL)
{
int remainData = fileSize;
size_t len;
char fileBuffer[MAX_BUFFER_SIZE];
while(((len = recv(sock, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
{
fwrite(fileBuffer, sizeof(char), len, recievedFile);
remainData -= len;
printf("Received %d bytes, %d is left..\n", len, remainData);
}
fclose(recievedFile);
printf("File downloaded!\n");
}
else
{
perror("Failed to download file\n");
}
}
}
else if(strcmp(argv[1], "upload-file") == 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "%s", argv[2]);
int fd = open(filePath, O_RDONLY);
int downloadFailed = 0;
if (fd != -1)
{
struct stat file_stat;
if(fstat(fd, &file_stat) >= 0)
{
char fileSize[256];
sprintf(fileSize, "%d", (int)file_stat.st_size);
int len = send(sock, fileSize, sizeof(fileSize), 0);
if(len >= 0)
{
int remainData = file_stat.st_size;
off_t offset = 0;
int sent_bytes = 0;
while(((sent_bytes = sendfile(sock, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
{
remainData -= sent_bytes;
printf("sent %d bytes, %d is left...\n", sent_bytes, remainData);
}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
if(downloadFailed == 1)
{
perror("Failed to download file!\n");
}
}
else
{
char someBuffer[MAX_BUFFER_SIZE];
// nrecv is the number of bytes that we recieved
if ((nrecv = recv(sock, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
{
perror("recv");
return 1;
}
printf("%s\n", someBuffer);
}
close(sock);
return 0;
}
else
{
perror("The arguments are incorrect.");
}
}
here is the server code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <math.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#define PORT 0x0da2 // 3490
#define IP_ADDR 0x7f000001 // 127.0.0.1
#define QUEUE_LEN 20
#define MAX_BUFFER_SIZE 1024
int send_all(int socket, void* buffer, int length)
{
char *ptr = (char*)buffer;
while (length > 0)
{
int i = send(socket, ptr, length, 0);
if (i < 1) return -1;
ptr += i;
length -= i;
}
return 0;
}
void list_dir()
{
DIR * directory;
struct dirent* dir;
directory = opendir(".");
if (directory)
{
while ((dir = readdir(directory)) != NULL)
{
printf("%s\n", dir->d_name); // /home/text.txt, text.txt
// get filesize (in bytes0 with dir->d_name
}
}
}
void list_files(char* buffer, int withBytes = 0)
{
DIR* d;
struct dirent* dir;
d = opendir("data");
int bufferIndex = 0;
while((dir = readdir(d)) != NULL)
{
char tempFilename[256] = "data/";
int tempIndex = 5;
char* scan = dir->d_name;
while(*scan)
{
tempFilename[tempIndex++] = *scan;
buffer[bufferIndex++] = *scan++;
}
tempFilename[tempIndex] = 0;
struct stat st = {0};
stat(tempFilename, &st);
int fileSize = st.st_size;
if(withBytes == 1)
{
// Adding file size to the buffer
bufferIndex += sprintf(&buffer[bufferIndex], " %d bytes", fileSize);
}
buffer[bufferIndex++] = '\n';
}
buffer[bufferIndex] = 0;
closedir(d);
}
int main(void)
{
int listenS = socket(AF_INET, SOCK_STREAM, 0);
if (listenS < 0)
{
perror("socket");
return 1;
}
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (bind(listenS, (struct sockaddr*)&s, sizeof(s)) < 0)
{
perror("bind");
return 1;
}
if (listen(listenS, QUEUE_LEN) < 0)
{
perror("listen");
return 1;
}
struct sockaddr_in clientIn;
int clientInSize = sizeof clientIn;
struct stat st = {0};
if(stat("data", &st) == -1)
{
mkdir("data", 0700);
}
while (1)
{
int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
if (newfd < 0)
{
perror("accept");
return 1;
}
int pid = fork(); // creating new thread
if (pid == 0)
{
close(listenS); // duplicate=> thats why we need to close the socket
char someBuffer[MAX_BUFFER_SIZE];
int nrecv;
if ((nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
{
perror("recv");
return 1;
}
printf("Message recieved: %s\n", someBuffer);
// Here we read the command the argument and split them
// into seperate variables
char command[256];
char argument[256];
int commandHasBeenSet = 0;
char* token = strtok(someBuffer, " ");
while(token != NULL)
{
if(commandHasBeenSet == 0)
{
strcpy(command, token);
commandHasBeenSet = 1;
}
else
{
strcpy(argument, token);
}
token = strtok(NULL, " ");
}
if (strcmp(command, "list-files") == 0)
{
char buffer[MAX_BUFFER_SIZE];
list_files(buffer, 1);
if (send_all(newfd, buffer, strlen(buffer) + 1) < 0)
{
perror("send buffer to client failed");
return 1;
}
printf("Sent a message to a client!\n");
}
else if (strcmp(command, "upload-file") == 0)
{
printf("Uploading file %s\n", argument);
char sizeBuffer[256];
recv(newfd, sizeBuffer, 256, 0);
int fileSize = atoi(sizeBuffer);
if(fileSize > 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "data/%s", argument);
printf("Downloading to %s", filePath);
FILE* recievedFile = fopen(filePath, "w");
if(recievedFile != NULL)
{
int remainData = fileSize;
size_t len;
char fileBuffer[MAX_BUFFER_SIZE];
while(((len = recv(newfd, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
{
fwrite(fileBuffer, sizeof(char), len, recievedFile);
remainData -= len;
printf("Received %d bytes, %d is left..\n", len, remainData);
}
fclose(recievedFile);
printf("File downloaded!\n");
}
else
{
perror("Failed to download file\n");
}
}else
{
perror("Failed to get file size for download\n");
}
}
else if (strcmp(command, "download-file") == 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "data/%s", argument);
int fd = open(filePath, O_RDONLY);
int downloadFailed = 0;
if (fd != -1)
{
struct stat file_stat;
if(fstat(fd, &file_stat) >= 0)
{
char fileSize[256];
sprintf(fileSize, "%d", (int)file_stat.st_size);
int len = send(newfd, fileSize, sizeof(fileSize), 0);
if(len >= 0)
{
int remainData = file_stat.st_size;
off_t offset = 0;
int sent_bytes = 0;
while(((sent_bytes = sendfile(newfd, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
{
remainData -= sent_bytes;
printf("Server sent %d bytes, %d is left...\n", sent_bytes, remainData);
}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
if(downloadFailed == 1)
{
perror("Failed to download file!\n");
}
}
else if (strcmp(command, "search") == 0)
{
char buffer[MAX_BUFFER_SIZE];
char result[MAX_BUFFER_SIZE];
int resultIndex = 0;
list_files(buffer);
result[0] = 0;
char tempBuffer[MAX_BUFFER_SIZE];
strcpy(tempBuffer, buffer);
token = strtok(tempBuffer, "\n");
while(token != NULL)
{
char* scanToken = token;
char* scanArgument = argument;
int found = 1;
while(*scanToken && *scanArgument)
{
if(*scanToken++ != *scanArgument++)
{
found = 0;
break;
}
}
if(found == 1)
{
if(resultIndex > 0)
{
result[resultIndex++] = ' ';
}
strcpy(&result[resultIndex], token);
resultIndex += strlen(token);
result[resultIndex] = 0;
}
token = strtok(NULL, "\n");
}
if (send_all(newfd, result, strlen(result) + 1) < 0)
{
perror("send buffer to client failed");
return 1;
}
printf("Sent a message to a client!\n");
}
close(newfd);
exit(0);
}
else
close(newfd);
}
close(listenS);
return 0;
}
If you run the server, and then run the client with commands like:
./client list-files
./client download-file test.txt
it will work fine, the client will receive messages from the server and vice versa.
The problem occurs when I try to run:
./client upload-file test.txt
which is essentially the same as download-file command, just copied and pasted to the server from the client (same logic, should work the same), except it doesn't.
Specifically the program fail at line 175 of the server (recv(newfd, sizeBuffer, 256, 0);), it gets 0 instead of the value the client is sending it.
Any idea what I am missing?
(I tried searching online but didn't find anything)
TCP is a streaming protocol. There is no message boundaries, and server's recvs do not correspond to client's sends.
The client sends the command with
send_all(sock, sendBuffer, strlen(sendBuffer) + 1)
OTOH, the server tries to receive it with
nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0))
recv does not care whether the stream contains a '\0' or not. It blindly waits for MAX_BUFFER_SIZE bytes to come in. Some (valuable) data sent by the client is in someBuffer just past the command, but ignored by the server.
The server must parse the reply more diligently. For that you likely need a more elaborated protocol (e.g. prefix each string with its length).
You are assuming the stream socket will preserve your message boundaries. It will not. So, you're sending something like:
upload-file filename\0
NNN\0\0\0...\0 [256 bytes containing filesize]
<content of filename>
So probably the first recv you do on the server side (of MAX_BUFFER_SIZE bytes) is receiving not just the command string and the filename, but also the file size block and the entire file content. That is, you've already received it all and it's sitting in someBuffer. Your initial command string is null-terminated so you would not be aware of the rest of it unless you check nrecv. Hence, your next recv call gets end-of-file because the client is finished and has closed its end of the connection.
To send defined records over TCP, you need to know exactly how many bytes are expected at each point, and receive exactly those (or be prepared to parse the received data). See also https://stackoverflow.com/a/47440054/1076479
I am trying to write a web server in C and my code is segfaulting at the moment and I have no idea why. It seems to have something to do with my strcats but thats as far as I've been able to get. I have posted the code and the gdb output. Any help is greatly appreciated.
*** CODE ***
/* web-server.c */
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFLEN 1500
#define BACKLOG 10
static int server_socket(int port) {
int fd;
struct sockaddr_in servaddr;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("Unable to create socket");
return -1;
}
printf("created socket\n");
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(port);
if (bind(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
perror("Unable to bind to port");
return -1;
}
printf("binding completed\n");
if (listen(fd, BACKLOG) == -1){
perror("Unable to listen for connections");
return -1;
}
printf("socket setup\n");
return fd;
}
static char *html_request(char *request) {
char request_code[3], url[BUFLEN], http_code[BUFLEN];
printf(request);
printf("\n");
sscanf(request, "%3s %s %s", request_code, url, http_code);
return url;
}
int main() {
int connfd, servfd, rlen, i, eof;
int content_length = 0;
char buf[BUFLEN];
struct sockaddr_in saddr;
char *response;
char *read_buffer = malloc(1);
char *response200 = "HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: close\nConnection-Length: ";
char *response404 = "HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnection: close\nConnection-Length: ";
FILE *in;
char buffer[1500], content_buffer[5];
char *headers = malloc(BUFLEN);
char *output = malloc(BUFLEN);
socklen_t saddr_len = sizeof(saddr);
servfd = server_socket(8080);
printf("before connection\n");
if ((connfd = accept(servfd, (struct sockaddr *)&saddr, &saddr_len)) == -1) {
perror("Unable to accept connection");
return -1;
}
else {
printf("Accept connection\n");
}
if ((rlen = read(connfd, buf, BUFLEN)) > 0) {
realloc(read_buffer,rlen);
for (i = 0; i < rlen; i++){
printf("%c", buf[i]);
read_buffer[i] = buf[i];
}
printf("\n");
}
printf("IMMA BEAST!\n");
response = html_request(read_buffer);
printf(response);
if (strcmp(response, "/index.html") == 0){
printf("\nMatches!");
in = fopen("index.txt", "r");
if (in == NULL){
printf("Read fail!");
}
while ((eof = fread(buffer, 1, 296, in )) > 0){
content_length = content_length + 1;
}
snprintf(content_buffer, 5, "%d" ,content_length);
headers = strcat(response200, content_buffer);
}
else{
printf("\nFails!");
in = fopen("index-error.txt", "r");
if (in == NULL){
printf("Read fail!");
}
while ((eof = fread(buffer, 1, 300, in )) > 0){
content_length = content_length + 1;
}
snprintf(content_buffer, 5, "%d" ,content_length);
headers = strcat(response404, content_buffer);
}
fclose(in);
output = strcat(headers, buffer);
write(connfd, output, BUFLEN);
close(connfd);
free(read_buffer);
free(headers);
free(output);
return 0;
}
* GDB OUTPUT *
Program received signal SIGSEGV, Segmentation fault.
0x0000003d4647eff0 in strcat () from /lib64/libc.so.6
(gdb) where
#0 0x0000003d4647eff0 in strcat () from /lib64/libc.so.6
#1 0x0000000000400ecd in main ()
(gdb) n
Single stepping until exit from function strcat,
which has no line number information.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
This is not OK at all:
strcat("HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnection: close\nConnection-Length: ", content_buffer);
strcat() appends the second argument to the first. The first argument there is a literal string, which must never be modified. The only reason C allows this to compile at all is the unfortunate fact that old code depends on literal strings being of type char[] rather than const char[].
http://www.manpagez.com/man/3/strcat/
You have to malloc your first string with the right size (length of first string + length of second string + 1.
http://www.manpagez.com/man/3/malloc/
Something like:
char *str;
int len = (strlen("HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: close\nConnection-Length: ") + strlen(content_buffer));
str = malloc((len + 1) * sizeof(char));
bzero(str, len);
strcpy(str, "HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: close\nConnection-Length: ");
strcat(str, content_buffer);
str[len] = 0;
I am trying to implement a simple HTTP server with C that
reads a request
checks if it is a GET request
reads the URL from the request
Checks if file is on server and tries to open it
I am using strtok for String tokenizing and I think it messes up the filepath. open and fopen always return error codes and are not able to open any files.
Here is my code:
/*
** parser.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MYPORT 3499 // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
#define MAXLEN 1024 //upper limit of the length of the string
int main(void)
{
char input[MAXLEN]; //the line that is read from the client
char * token1; //GET request
char * token2; //filepath
char tmpstring[MAXLEN]; //filesize
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd, file open on file_fd
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
int sin_size;
int yes=1;
int n; //the amount of read characters from the client
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
while(1) { // main accept() loop
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
perror("accept");
continue;
}
printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
n = readline(new_fd, input, MAXLEN); //n is the amount of read characters
if (n == -1) {
perror("Unable to read line");
}
//Check if it is a GET message
token1 = strtok(input," ");
if(strcmp(token1, "GET") != 0)
{
send(new_fd, "Bad request\n", 30, 0);
}
else
{
//Retrieve the file path
token2 = strtok(NULL, " ");
if(token2 == NULL)
{
send(new_fd, "File path not specified\n", 23, 0); //Check if filename is empty
}
send(new_fd, token2, strlen(token2), 0); //test
printf("%s", token2);
if(token2[0] == '/') //remove the initial slash
memmove(token2, token2 + 1, strlen(token2));
//char * path = "test.html"; //test line
//char * buff;
//int len = sprintf(buff, "1: %d 2: %d\n", strlen(token1), strlen(token2));
//send(new_fd, buff, len, 0);
//Check if file is on the server
if(open(token2, O_RDONLY) < 0) //Error opening file
{
if(errno == EACCES)
send(new_fd, "Access error\n", 30, 0);
else
send(new_fd, "Not existed\n", 30, 0);
}
else
{
FILE * requested_file = fopen(token2, "r");
if(requested_file == NULL) //
{
send(new_fd, "Error in fopen\n", 30, 0);
}
else
{
send(new_fd, "File found\n", 30, 0); //successful
}
fseek(requested_file, 0, SEEK_END); // move to the end of the file
int end= ftell(requested_file); // get the position of the end of file
int stringlen = sprintf(tmpstring, "file size: %d\n", end);
send(new_fd, tmpstring, stringlen, 0);
}
}
close(new_fd); //close connection
}
return 0;
}
//helper function for recieving text
int readline(int fd, char *buf, int maxlen)
{
int n, rc;
char c;
for (n = 1; n < maxlen; n++) {
if ((rc = read(fd, &c, 1)) == 1) {
*buf++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
if (n == 1)
return 0; // EOF, no data read
else
break; // EOF, read some data
} else
return -1; // error
}
*buf = '\0'; // null-terminate
return n;
}
So I'm placing a test.html in the same folder as the server. Then im telnetting to localhost and port 3499. This is the output when entering GET /test.html:
/test.html
Not existed
rError in fopen
Connection closed by foreign host.
try opening "test.html" instead of "\test.html"