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);
}
}
printf("SERVER GOT MESSAGE: %s\n", buffer);
No, you didn't get a message. You got some bytes. TCP is not a message protocol. If you want to send and receive messages, you have to write code to send and receive messages, which you have not done.
"TCP is a reliable, byte-stream protocol that does not preserve application message boundaries." Memorize this. Do not write any code that uses TCP until you understand everything in it or your code will be doomed to fail.
Another important point: Do not write any code that uses TCP until you have a specification for the protocol that you are using on top of TCP. Who sends when? Are there timeouts? Which side implements them? How is the connection shut down? Who initiates it? Are there application message boundaries? How are they marked?
And so on. It really is worth the time to document this in detail. You can look at the specifications for existing protocols that use TCP (such as HTTP, SMTP, IRC, and so on) for examples of what s protocol specification should look like.
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.
In addition to the fundamental fallacy in your code noted by David Schwartz, you are committing all the usual mistakes.
bzero(buffer,256);
Redundant. Remove.
n = recv(newsockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from client");
else
Missing test for n == 0 and break here.
buffer[n] = '\0';
Ths is only valid if the data is entirely text. What you should be doing throughout, including the following printf(), is using n as the exclusive upper bound for what you retrieve out of the buffer.
And as noted, you have no reason to believe this constitutes a complete message, or only one.
I have client.c and server.c , I'm trying to send a file from client to server and from server to client...
The file is received by the server but when the server sends the file , the client stops or does not receive the file ,
The sendFile() function:
void sendfile(int fd,char* filename)
{
char buff[0x1000];
FILE *file = fopen(filename, "rb");
if (!file)
{
printf("Can't open file for reading");
return;
}
while (!feof(file))
{
int rval = fread(buff, 1, sizeof(buff), file);
if (rval < 1)
{
printf("Can't read from file\n");
fclose(file);
return;
}
int off = 0;
do
{
int sent = send(fd, &buff[off], rval - off, 0);
printf("Sending ...\n");
if (sent < 1)
{
printf("Can't write to socket");
fclose(file);
return;
}
printf("[SendFile]Fisierul a fost trimis!\n");
off += sent;
}
while (off < rval);
}
fclose(file);
}
The getFile():
void getFile(int fd,char* filename)
{
int rval;
char buff[0x1000];
FILE *file = fopen(filename, "w+");
if (!file)
{
printf("Can't open file for writing");
return;
}
do
{
rval = recv(fd, buff, sizeof(buff), 0);
if (rval < 0)
{
printf("Can't read from socket");
fclose(file);
return;
}
if (rval == 0)
break;
int off = 0;
do
{
int written = fwrite(&buff[off], 1, rval - off, file);
printf("[server]Writing %d bytes\n",written);
if (written < 1)
{
printf("Can't write to file");
fclose(file);
return;
}
off += written;
}
while (off < rval);
printf("[server]File received!\n");
}
while(1);
fclose(file);
}
The client main():
int main (int argc, char *argv[])
{
int sd;
struct sockaddr_in server;
if (argc != 2)
{
printf ("[client] Sintaxa: %s <filename>\n", argv[0]);
return -1;
}
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("[client] Eroare la socket().\n");
return errno;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons (port);
if (connect (sd, (struct sockaddr *) &server,sizeof (struct sockaddr)) == -1)
{
perror ("[client]Eroare la connect().\n");
return errno;
}
sendfile(sd,"test.cpp");
printf("File sent\n");
getFile(sd,"server.c");
close (sd);
}
The loop from server.c
while (1)
{
int client;
int length = sizeof (from);
printf ("[server]Asteptam la portul %d...\n",port);
fflush (stdout);
client = accept (sd, (struct sockaddr *) &from, &length);
if (client < 0)
{
perror ("[server]Eroare la accept().\n");
continue;
}
if((pid=fork()) == 0 )
{
getFile(client,"test.cpp");
sendfile(client,"server.c");
printf("File sent\n");
close(client);
}
}
To Start with.
accept(), connect(), recv() .. all are blocking calls so in theory your program can get blocked anywhere in these calls until you use non blocking sockets. From the program it looks like you are using blocking sockets
I would suggest you to read a good socket programming tutorial like this one first https://beej.us/guide/bgnet/
Secondly
I would suggest you to have a tool like wireshark or any other packet analyzer tool running to debug your problem.
Lastly you can do something like the following code snippets to try
Set sockets to non blocking mode using fnctl API call.
int rc = fcntl(newFd, F_GETFL,0);
rc |= O_NONBLOCK;
rc = fcntl(newFd, F_SETFL,rc);
Poll the socket for events You can use something like select,poll for the
same. Here is a nice example for select in the Beej's tutorial.
https://beej.us/guide/bgnet/html/multi/selectman.html
Handle errors correctly. Non blocking sockets can return a EAGAIN or EWOULDBLOCK error which has to be handled correctly. The error means the socket is not readable or writable at the moment look for events and try again. Someting on the lines of.
rval = recv(fd, buff, sizeof(buff), 0);
if (rval < 0)
{
if((errno == EWOULDBLOCK)||(errno ==EAGAIN))
{
continue;
}
else
//cleanup
}
tre problem was the do { ... } while(1); from getFile ...so I deleted the loop...
I am working on a TCP based file transfer program running on Unix with sockets. I need to create a program that opens two separate data connections similar to the FTP protocol. One connection is used for client and server to send commands to each other and the other is used to actually transfer the bytes from the files. In other words, there is one client, one server, and two connections between them.
I am creating two socket connections on different ports – the hosts are the same. So for example the command connection would be 127.0.0.1:6000 and the IO connection would be 127:0.0.1:6005.
Server:
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("Can't create a socket");
exit(1);
}
bzero((char *)&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(6005);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(socket_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
perror("Can't bind name to socket");
exit(1);
}
listen(socket_fd, 10);
while (TRUE)
{
client_len= sizeof(client);
if ((connect_fd = accept (socket_fd, (struct sockaddr *)&client, &client_len)) == -1)
{
fprintf(stderr, "Can't accept client\n");
exit(1);
}
printf(" Remote Address: %s\n", inet_ntoa(client.sin_addr));
temp = buffer;
num_bytes = BUFLEN;
while ((n = recv (connect_fd, temp, num_bytes, 0)) < BUFLEN)
{
temp += n;
num_bytes -= n;
}
if(strncmp("SEND", buffer, 4) == 0)
{
printf("Client Sending Data...\n");
// setup server type socket for the file transfer data connection
struct sockaddr_in data_conn, data_client;
int conn_fd, new_conn_fd, data_client_len;
char *filename;
if((conn_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Cannot Create Data Connection Socket!\n");
exit(1);
}
// populate data connection socket
bzero((char *)&data_conn, sizeof(struct sockaddr_in));
data_conn.sin_family = AF_INET;
data_conn.sin_port = htons(6000);
data_conn.sin_addr.s_addr = htonl(INADDR_ANY);
// bind the new data connection socket
if (bind(conn_fd, (struct sockaddr *)&data_conn, sizeof(data_conn)) == -1)
{
perror("Can't Bind Name to Data Connection Socket!\n");
exit(1);
}
listen(conn_fd, 5);
bzero(temp, BUFLEN);
bzero(buffer, BUFLEN);
temp = buffer;
num_bytes = BUFLEN;
while ((n = recv (connect_fd, temp, num_bytes, 0)) < BUFLEN)
{
temp += n;
num_bytes -= n;
}
fprintf(stdout, "Receiving File: %s\n", buffer);
FILE *fp;
fp = fopen(buffer, "w");
if(fp == NULL)
{
perror("Could not open destination file\n");
exit(1);
}
data_client_len= sizeof(data_client);
if ((new_conn_fd = accept (conn_fd, (struct sockaddr *)&data_client, &data_client_len)) == -1)
{
fprintf(stderr, "Can't accept data connection client\n");
exit(1);
}
bzero(temp, BUFLEN);
bzero(buffer, BUFLEN);
temp = buffer;
num_bytes = BUFLEN;
printf("foo\n"); // this prints! so issue must be below
while ((n = recv (new_conn_fd, temp, num_bytes, 0)) < BUFLEN)
{
temp += n;
num_bytes -= n;
}
// just printing the file contents for now
fprintf(stdout, "received file contents: %s\n", buffer);
fclose(fp);
}
else if(strncmp("GET", buffer, 3) == 0)
{
// get stuff not implemented yet
}
else
{
perror("Client Issued an Invalid Command\n");
exit(1);
}
close (connect_fd);
}
close(socket_fd);
return(0);
Client:
// first check to make sure that the FTP command was either GET or SET
if(strncmp("GET", command, 3) == 0)
{
operation = GET;
}
else if (strncmp("SEND", command, 4) == 0)
{
operation = SEND;
}
else
{
perror("The command must be either GET or SET (case sensitive)\n");
exit(1);
}
// Create the socket
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Cannot create socket");
exit(1);
}
bzero((char *)&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(port);
if ((host_info = gethostbyname(host)) == NULL)
{
fprintf(stderr, "Unknown server address\n");
exit(1);
}
bcopy(host_info->h_addr, (char *)&server.sin_addr, host_info->h_length);
// Connecting to the server
if (connect (socket_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
fprintf(stderr, "Can't connect to server\n");
perror("connect");
exit(1);
}
printf("Connected:\n");
printf("\t\tServer Name: %s\n", host_info->h_name);
pptr = host_info->h_addr_list;
printf("\t\tIP Address: %s\n", inet_ntop(host_info->h_addrtype, *pptr, str, sizeof(str)));
if(operation == SEND)
{
bzero(send_buffer, BUFLEN);
strcpy(send_buffer, "SEND");
send (socket_fd, send_buffer, BUFLEN, 0);
// send the filename to the server
bzero(send_buffer, BUFLEN);
strcpy(send_buffer, filename);
send(socket_fd, send_buffer, BUFLEN, 0);
// read the files contents
FILE *fp;
fp = fopen(filename, "r");
if(fp == NULL)
{
fprintf(stderr, "File '%s' is invalid! Please choose a valid filename\n", filename);
exit(1);
}
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *string = malloc(fsize+1);
fread(string, fsize, 1, fp);
string[fsize] = 0;
struct sockaddr_in data_conn;
int data_conn_fd;
// establish a client connection on port 6000
if ((data_conn_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Cannot create data connection socket");
exit(1);
}
bzero((char *)&data_conn, sizeof(struct sockaddr_in));
data_conn.sin_family = AF_INET;
data_conn.sin_port = htons(6000);
bcopy(host_info->h_addr, (char *)&data_conn.sin_addr, host_info->h_length);
if (connect (data_conn_fd, (struct sockaddr *)&data_conn, sizeof(data_conn)) == -1)
{
fprintf(stderr, "Can't connect to data connection server\n");
exit(1);
}
send(data_conn_fd, string, fsize, 0);
fclose(fp);
//close(data_conn_fd);
}
else if (operation == GET)
{
// not yet implemented
}
else
{
perror("Invalid Operation!\n");
exit(1);
}
fflush(stdout);
close (socket_fd);
return (0);
}
The program has functions to both send a file to the server and receive a file from the server. I've only implemented the send function for now.
Towards the end of the server code the program hangs. The point where I'm printing the file contents to stdout. I'm doing this to test as dumping it into a file is trivial. I've used a printf(“foo”) which prints but it hangs after that. It seems that the while loop corresponding to the send() in the client is not working. It is hanging after that print statement. The rest seems to be working.
Also note that the dual connections is a requirement I cannot use one.
Any help or advice would be very much appreciated.
The receiving loops do not cover the two cases where recv() did not transfer any data.
As there are:
it detected an error and returned -1
it detected the connection had been closed and returned 0
The calls to send() fully ignore the value returned. Do not do this, as
also send() might fail returning -1
also send() might return having sent few data than it was told to send. The code shall loop around send() and count until all data had been sent out.
I have this scenario. I'm trying to use socket in C to send and receive data. Client sends some string, server manipulates it, sends it back to the client. Everything is fine but one small issue is: The client receives only the first line from server, displays it and then halts till the connection is closed by server after a time out. Although the bytes sent by server = the bytes received by client. As soon as the connection is closed, the rest of the string is displayed by the client.
I would like to know your thoughts and possible issues. Please let me know if you ahve any questions.
Protocol used: TCP
Here is the code for the server:
for (;;)
{
n=recv(s, buf, RBUFLEN-1, 0);
if (n < 0)
{
printf("Read error\n");
closesocket(s);
printf("Socket %d closed\n", s);
break;
}
else if (n==0)
{
printf("Connection closed by party on socket %d\n",s);
closesocket(s);
break;
}
else
{
printf("Received line from socket %03d : \n",s);
printf("N bytes received: %d \n",n);
// DoSomeOperationsOnTheData()
if(send(s, buffer, n, 0) != n)
printf("Write error while replying\n");
else
{
printf("Reply sent: %d bytes\n",n);
}
}
Code for client:
do
{
memset(buffer,0x0,BUFFER_SIZE); // init line
rc = read(sd, buffer, BUFFER_SIZE);
printf("\nReceived bytes: %d", rc);
if( rc > 0)
{
printf("%s",buffer);
size +=rc;
}
}while(rc>0);
printf("\n Total recieved response bytes: %d\n",size);
close(sd);
This
do
{
memset(buffer, 0, BUFFER_SIZE); // init line
rc = read(sd, buffer, BUFFER_SIZE);
printf("\nReceived bytes: %d", rc);
if( rc > 0)
{
printf("%s",buffer);
size +=rc;
}
}while(rc>0);
should be:
memset(buffer,0x0,BUFFER_SIZE); // init line
size_t size = 0;
size_t toread = BUFFER_SIZE;
do
{
ssize_t rc = read(sd, buffer+size, toread);
if (rc > 0)
{
printf("\nReceived bytes:%zd", rc);
printf("%s", buffer);
size += rc;
toread -= rc;
}
else if (rc == 0)
{
printf("The server hung up.\");
break;
}
else
{
perror("read() failed");
break;
}
} while (toread);
if (toread < BUFFERSIZE)
{
printf("Warning: Read less bytes (%zu) then expected (%d).\n", toread, BUFFERSIZE);
}
try flush data after sent.
it needs if you use socket with fdopen
FILE *fdsock = fdopen(sock, "a+");
...
fwrite(fdsock, 1, 1 "A");
fflush(fdsock);
...
fclose(fdsock);
and to finish socket, close with shutdown(sock, SD_SEND);
UPDATE
Take memset out of loop