I've tried everything. No idea what's happening with my program. I've written a tcp server and a client. I can issue some commands from the client to the server, they are: ls-local, ls-remote, get filename, put filename (i have not yet implemented put, because I am stuck on get).
Everything works perfectly until I issue the get command. The server.c and client.c files are in different folders. When you issue 'get filename' where filename is the name of the file you want to get, the server checks if the file exists in its current directory, if it does, it sends it to the client.
On the first iteration, this works perfectly. The file is sent to the client and the client creates a copy of said file. However, ANY command besides 'ls-local' results in pure garbage being printed out to the client terminal and I have no idea why. I think it might have to do with me needing to clear a buffer but I tried that and it still didn't work so I'm obviously doing something very wrong. Here is what happens when I issue a command after get file: when I issue get filename again, I get a line of garbage and nothing is sent even though the file exists in the server.
If I issue the command ls-remote, this is where it goes crazy printing gibberish non stop to my client terminal.
I've included both files in case you want to test them to see what I mean. Usage is: 'get filename', no single quotes. Remember, it breaks after the first time, works fine the first. Sorry for the long files, the sending file code is in the method named sendfile, I've tried to organize it so its easy to read.
Need some major help on this, thanks in advance!!
server.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <dirent.h>
/**
* Structure used to contain the type of command: 'get' or 'put'
* as well as the name of the file
*/
struct command{
char* type;
char* filename;
};
void syserr(char *msg) { perror(msg); exit(-1); }
/* function prototypes */
void handleConnection(int);
void sendDirectories(int);
struct command getCommand(char*);
int fileExists(char*);
void sendFile(char*, int);
int main(int argc, char *argv[]){
int sockfd, newsockfd, portno, processId;
struct sockaddr_in serv_addr, clt_addr;
socklen_t addrlen;
switch(argc) {
case 1:
portno = 5555;
break;
case 2 :
portno = atoi(argv[1]);
break;
default :
fprintf(stderr,"Usage: %s <port>\n", argv[0]);
return 1;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) syserr("can't open socket");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
syserr("can't bind");
}
printf("bind socket to port %d...\n", portno);
listen(sockfd, 5);
for(;;) {
printf("wait on port %d...\n", portno);
addrlen = sizeof(clt_addr);
newsockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
if(newsockfd < 0) syserr("can't accept");
printf("connected to client....\n");
signal(SIGCHLD,SIG_IGN);//prevent zombie process
processId = fork();
if(processId < 0){
// printf("process id < 0, nothing closed\n");
syserr("ERROR while attempting to fork");
}
if(processId == 0){//child process
// printf("process id == 0, close(sockfd)\n");
close(sockfd);
handleConnection(newsockfd);
exit(0);
}
else{//parent process
// printf("else statement, close(newsockfd)\n");
close(newsockfd);
}
}
// printf("outside of loop, close(sockfd)\n");
// close(sockfd);
return 0;
}
/**
* Method that handles the commands received and routes them to the proper methods
* @param newsockfd the file descriptor created when the connection was accepted
*/
void handleConnection(int newsockfd){
int n;
char buffer[256];
while(1){
bzero(buffer,256);
printf("waiting for client's command.....\n");
n = recv(newsockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from client");
else buffer[n] = '\0';
printf("SERVER GOT MESSAGE: %s\n", buffer);
/**
* exit command
*/
if (strcmp(buffer, "exit") == 0){
printf("Ending session with client....\n");
break;
}
/*
* ls-remote command
*/
if (strcmp(buffer, "ls-remote") == 0){
sendDirectories(newsockfd);
}else{
struct command userCommand = getCommand(buffer);
//USER SENDS VALID COMMAND
if(strcmp(userCommand.type, "invalid") != 0){
printf("Command %s %s is valid\n", userCommand.type, userCommand.filename);
/**
* 'get' command
*/
if(strcmp(userCommand.type, "get") == 0){
//File exists
if(fileExists(userCommand.filename) == 1){
sendFile(userCommand.filename, newsockfd);
}
//File does not exist
else{
n = send(newsockfd, "ERROR: FILE DOES NOT EXIST IN REMOTE DIRECTORY", 255, 0);
if(n < 0) syserr("Unable to send FILE DOES NOT EXIST ERROR to client");
printf("Sending FILE DOES NOT EXIST ERROR..\n");
}
}
/**
* 'put' command
* no check needed for 'put' because only possible options at this point are 'get' and 'put'
* since invalid commands are filtered out in getCommand method
*/
else{
}
}
//USER SENDS INVALID COMMAND
else{
n = send(newsockfd, "INVALID COMMAND: you may only send 'ls-remote' OR ' get ' or ' put ' followed by a filename", 255, 0);
if(n < 0) syserr("Unable to send INVALID COMMAND ERROR' to client");
printf("Sending INVALID COMMAND ERROR...\n");
}
}
}
// printf("in handleConnection, close(sockfd)\n");
// close(newsockfd);
}
/**
* Method that executes when the command 'ls-remote' is received, sends entire file listing in working directory
* @param newsockfd the file descriptor created when the connection was accepted, will be used to send file listing
*/
void sendDirectories(int newsockfd){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
int n;
if (directory){
int count = 0;
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
// // printf("%s\n", dir->d_name);
// strcat(dir->d_name, "\n");//concatenates new line char to end of dir name, consider sending without new line and handle new line in client
// n = send(newsockfd, dir->d_name,255, 0);
// if(n < 0) syserr("can't send file list to client");
// printf("sending file name: '%s' to client...\n", dir->d_name);
count++;
}
}
int32_t convCount = htonl(count);
n = send(newsockfd, &convCount, sizeof(convCount), 0);
if(n < 0) syserr("can't send file list count");
printf("sending file list count: '%d' to client...\n", count);
rewinddir(directory);
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
// printf("%s\n", dir->d_name);
// strcat(dir->d_name, "\n");//concatenates new line char to end of dir name, consider sending without new line and handle new line in client
n = send(newsockfd, dir->d_name,255, 0);
if(n < 0) syserr("can't send file list to client");
printf("sending file name: '%s' to client...\n", dir->d_name);
}
}
closedir(directory);
}
}
/**
* Method that extracts a valid command from a given string
* @param string the string containing the commands
* @return the valid command if found, a command with type 'invalid' if not found
*/
struct command getCommand(char* string){
char* temp;
int count;
struct command userCommand;
count = 0;
//Split the string on spaces, if more than one space then # of arguments is > 2, thus invalid command
temp = strtok(string, " ");
while(temp != NULL){
if(count == 0){
userCommand.type = temp;
}else if(count == 1){
userCommand.filename = temp;
}else{
userCommand.type = "invalid";
break;
}
temp = strtok(NULL, " ");
count++;
}
//We test count ==1 because this means only one space in string but also only one word because count did not increment again
//which is still an invalid command
if(strcmp(userCommand.type, "get") != 0 && strcmp(userCommand.type, "put") != 0 || count == 1){
userCommand.type = "invalid";
}
return userCommand;
}
/**
* Method that searches server directory for a given file name
* @param filename the name of the file to search for
* @return 1 if the file is found, 0 if not found
*/
int fileExists(char* filename){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
int n;
if (directory){
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
if(strcmp(dir->d_name, filename) == 0) {
printf("File: '%s' was FOUND!\n", filename);
return 1;
}
}
}
closedir(directory);
}
printf("Sorry file: '%s' was NOT found!\n", filename);
return 0;
}
/**
* Method that sends the specified file using the file descriptor passed
* @param filename the name of the file to be sent
* @param newsockfd the file descriptor to use to send the file
*/
void sendFile(char* filename, int newsockfd){
int n, fd;
struct stat file_stat;
char buffer[256];
char* file_buffer;
char file_chunk[BUFSIZ];
fd = open(filename, O_RDONLY);
if(fd < 0){
n = send(newsockfd, "ERROR: File could not be opened by server....",255, 0);
if(n < 0) syserr("Error opening file client has requested");
}
if (fstat(fd, &file_stat) < 0){
n = send(newsockfd, "ERROR:File stats could not be obtained by server....",255, 0);
if(n < 0) syserr("Error getting stats of file client has requested");
}
n = send(newsockfd, "Server found file and successfully opened...",255, 0);
if(n < 0) syserr("can't send file open confirmation to client");
printf("File found and successfully opened....\n");
// int data_remaining = file_stat.st_size;
// int32_t convSize = htonl(data_remaining);
// n = send(newsockfd, &convSize, sizeof(convSize), 0);
// if(n < 0) syserr("can't send file size ");
// printf("N IS: %d\n", n);
// printf("Sending file size to client, size is : %d bytes....\n", file_stat.st_size);
int data_remaining = file_stat.st_size;
int sent_bytes = 0;
char str[255];//store file size in string to send, for some reason the above code was sending fine but client could not receive, even though it works perfectly for ls-remote
sprintf(str, "%d", data_remaining);
file_buffer = (char*) malloc (sizeof(char)*data_remaining);
n = send(newsockfd,str,255, 0);
if(n < 0) syserr("can't send file size to client...");
printf("Sending file size to client, size is : %d bytes....\n", file_stat.st_size);
int read_bytes;
read_bytes = read(fd, file_buffer, data_remaining);
while((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0 && data_remaining > 0){
data_remaining -= sent_bytes;
printf("Sent %d bytes of file, %d bytes remain\n", sent_bytes, data_remaining);
}
printf("File %s has finished sending....\n", filename);
close(fd);
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
/**
* Structure used to contain the type of command: 'get' or 'put'
* as well as the name of the file
*/
struct command{
char* type;
char* filename;
};
void syserr(char* msg) { perror(msg); exit(-1); }
/*function prototypes*/
void listDirectories();
struct command getCommand(char*);
int main(int argc, char *argv[]){
int sockfd, portno, n;
int32_t *convCount, *convSize;
struct hostent* server;
struct sockaddr_in serv_addr;
FILE *received_file;
char buffer[256];
if(argc != 3){
fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]);
return 1;
}
server = gethostbyname(argv[1]);
if(!server){
fprintf(stderr, "ERROR: no such host: %s\n", argv[1]);
return 2;
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0) syserr("Error opening socket.");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr = *((struct in_addr*)server->h_addr);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0){
syserr("can't connect to server");
}
printf("connection to %s:%s established. Now awaiting commands...\n", argv[1], argv[2]);
while(1){
// printf("PLEASE ENTER MESSAGE: ");
printf("%s:%s> ", argv[1], argv[2]);
fgets(buffer, 255, stdin);
n = strlen(buffer);
if(n>0 && buffer[n-1] == '\n'){
buffer[n-1] = '\0';
}
if(strcmp(buffer, "exit") == 0){
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send 'exit' command to server");
printf("send...\n");
break;
}else if(strcmp(buffer, "ls-local") == 0){
listDirectories();
}else if(strcmp(buffer, "ls-remote") == 0){
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send to server");
printf("send...\n");
n = recv(sockfd, convCount, sizeof(int32_t), 0);
if(n < 0) syserr("can't receive from server");
int fileCount = ntohl(*convCount);
// printf("CLIENT RECEIVED FILE COUNT: %d\n", fileCount);
printf("Files at server (%s:%s):\n", argv[1], argv[2]);
int i = 0;
while(i < fileCount){
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from server");
printf("%s\n", buffer);
i++;
}
}else{
char cmdCopy[256];
strcpy(cmdCopy, buffer);
struct command userCommand = getCommand(cmdCopy);
if(strcmp(userCommand.type, "invalid") != 0){
printf("Command %s %s is valid\n", userCommand.type, userCommand.filename);
/**
* 'get' command
*/
if(strcmp(userCommand.type, "get") == 0){
//Re-append the entire command since
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send to server");
printf("send GET request for file: %s\n", userCommand.filename);
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from server");
else buffer[n] = '\0';
if(strcmp(buffer, "ERROR: FILE DOES NOT EXIST IN REMOTE DIRECTORY") != 0
&& strcmp(buffer, "ERROR: File could not be opened by server....") != 0
&& strcmp(buffer, "ERROR:File stats could not be obtained by server....") != 0){
printf("%s\n", buffer);
//// Not working for some reason, must receive as char*
// n = recv(sockfd, convSize, sizeof(int32_t), 0);
// printf("N IS: %d\n", n);
// if(n < 0) syserr("can't receive file size from server");
// int fileSize = ntohl(*convSize);
// printf("Size of file: %s to be received is %d\n bytes",buffer, fileSize);
// printf("Receiving file: %s \n....", userCommand.filename);
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive size from server");
else buffer[n] = '\0';
int data_remaining = atoi(buffer);
printf("file size is %d\n", data_remaining);
received_file = fopen(userCommand.filename, "w");
if (received_file == NULL){
syserr("Failed to open file.");
}else{
char file_buffer[BUFSIZ];
int bytes_received;
while((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) >0 && data_remaining > 0){
fwrite(file_buffer, 1, bytes_received, received_file);
data_remaining -= bytes_received;
printf("Received %d bytes of file, %d bytes remain\n", bytes_received, data_remaining);
}
printf("File receive complete\n");
fclose(received_file);
}
}else{
printf("%s\n", buffer);
}
}
/**
* 'put' command
* no check needed for 'put' because only possible options at this point are 'get' and 'put'
* since invalid commands are filtered out in getCommand method
*/
else{
// //File exists
// if(fileExists(userCommand.filename) == 1){
// sendFile(userCommand.filename, newsockfd);
// }
// //File does not exist
// else{
// printf( "ERROR: FILE DOES NOT EXIST IN LOCAL DIRECTORY\n");
// }
}
}
//USER TYPES INVALID COMMAND
else{
printf("INVALID COMMAND: you may only send 'ls-remote' OR ' get ' or ' put ' followed by a filename");
}
}
// n = send(sockfd, buffer, strlen(buffer), 0);
// if(n < 0) syserr("can't send to server");
// printf("send...\n");
// while( (n = recv(sockfd , buffer , 255 , 0)) > 0 ){
// printf("in while and n is: %d\n", n);
// printf("%s", buffer);
// }
// if(n < 0){
// printf("in if and n is: %d\n", n);
// syserr("can't receive from server");
// }else{
// printf("in else and n is: %d\n", n);
// buffer[n] = '\0';
// }
// printf("in no loop and n is: %d\n", n);
}
close(sockfd);
return 0;
}
// printf("connection to %s:%s established. Now awaiting commands...\n", argv[1], argv[2]);
// do{
// printf("%s:%s> ", argv[1], argv[2]);
// fgets(buffer, 255, stdin);
// n = strlen(buffer);
// if(n>0 && buffer[n-1] == '\n'){
// buffer[n-1] = '\0';
// }
// n = send(sockfd, buffer, strlen(buffer), 0);
// if(n < 0) syserr("can't send to server");
// printf("send...\n");
// n = recv(sockfd, buffer, 255, 0);
// if(n < 0) syserr("can't receive from server");
// else buffer[n] = '\0';
// printf("CLIENT RECEIVED MESSAGE: %s\n", buffer);
// }while(strcmp(buffer, "exit") != 0);
// close(sockfd);
// return 0;
/**
* Method that extracts a valid command from a given string
* @param string the string containing the commands
* @return the valid command if found, a command with type 'invalid' if not found
*/
struct command getCommand(char* string){
char* temp;
int count;
struct command userCommand;
count = 0;
//Split the string on spaces, if more than one space then # of arguments is > 2, thus invalid command
temp = strtok(string, " ");
while(temp != NULL){
if(count == 0){
userCommand.type = temp;
}else if(count == 1){
userCommand.filename = temp;
}else{
userCommand.type = "invalid";
break;
}
temp = strtok(NULL, " ");
count++;
}
//We test count ==1 because this means only one space in string but also only one word because count did not increment again
//which is still an invalid command
if(strcmp(userCommand.type, "get") != 0 && strcmp(userCommand.type, "put") != 0 || count == 1){
userCommand.type = "invalid";
}
return userCommand;
}
/**
* Method that executes when the command 'ls-local' is received, sends entire file listing in working directory
*/
void listDirectories(){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
if (directory){
printf("Files at the client:\n");
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
printf("%s\n", dir->d_name);
}
}
closedir(directory);
}
}
Addressing your immediate problem, there are two issues:
First in server.c:
while((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0 && data_remaining > 0){
On each send, you're sending file_stat.st_size
bytes, instead of data_remaining
bytes.
Also, you should be checking data_remaining > 0
first. Since the &&
operator is a short circuit operator, it won't evaluate the second part unless the first part evaluates to true. So if you have 0 bytes left to send, it will try to send them anyway.
So the above line should be:
while((data_remaining > 0) && ((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0)) {
Similarly in client.c, you have this:
while((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) >0 && data_remaining > 0){
If you've received the whole file, it will try one more recv
before it realizes that data_remaining > 0
is false. Since the server is done sending, it gets stuck waiting forever.
As before, switch the order of the &&
operands:
while((data_remaining > 0) && ((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) > 0)){
This should at least get you running.
There are further issues in addition to this. As David Schwartz mentioned, the streaming nature of TCP does not preserve message boundaries.
That means that just because you try to read 255 bytes doesn't mean you'll actually get that many. Anyplace you expect a message of a particular size, make sure you actually got that many bytes. If you didn't get enough, keep reading until you do.