CSS 432
FAQ on Final Project: Network Application
Note: Q1 - Q9 refer to the design and implementation of the ftp client.
Please ignore Q11 - Q20, which refer to the design and implementation of an ftp server,
which is not included in the current final project.
Q1: How many lines should the ftp client expect to read as a server
reply?
I have a question about the replies the server sends back. For
replies, what can the client assume as far as the message size or line
count? I am running into a problem where if I don't know how many
lines to read for each reply, I am not getting all of the data. If I
just keep looping, I will read all of the data, but as soon as no data
is left, I go into a read call that blocks and waits for incoming
data. So basically my question is, what can we assume about the server
replies, and if we can assume nothing, how can we make sure we read
the entire reply without going into a read call that blocks?
I read Section 4.2 in RFC 959 about server replies and how the first line and the
last line should contain a common sequence to tell the client that
more data exists on additional lines and that no more data is left
after the last line. Should we use this implementation for our client?
A: Use poll( &ufds, 1, 1000 ) to check if you have more
data to read
While RFC 959 mentions the first and the last line format returned from
the server, all you need to use is the poll() system call.
#include <sys/poll.h>
struct pollfd ufds;
ufds.fd = sd; // a socket descriptor to exmaine for read
ufds.events = POLLIN; // check if this sd is ready to read
ufds.revents = 0; // simply zero-initialized
int val = poll( &ufds, 1, 1000 ); // poll this socket for 1000msec (=1sec)
if ( val > 0 ) { // the socket is ready to read
char buf[BUFLEN];
int nread = read( sd, buf, BUFLEN ); // guaranteed to return from read
// even if nread < BUFLEN
Q2: How do we read from cin without knowing the # of reads in advance?
Since we have to have the program handle input in a variety of ways,
how do we read from the console (cin) without know the # of reads in
advance?
A: Use cin.getline( )
Here is a code example. After receiving a cin input in command[], you
have to lexically analyze its contents.
#define 10000 // allocate a big character array
char command[CMD_MAX]; // receive a cin input
cin.getline( command, CMD_MAX );
Q3: Which messages need to have the CRLF delimiter and how do we
add it to them?
A: All commands must be terminated with a Telnet string end-of-line CRLF ("\r\n") code.
All server responses must have this CRLF code at the end, too.
Q4: Do we need to fork() the client application in the case of
receiving data over the passive ftp connection?
For our project you talked about forking the client application in
class when receiving data over the passive ftp connection.
For example in order:
Send PASV
Passively Connect()
Fork() a child
Send LIST
What I cannot figure out is why the client needs to fork in order to get
the data? Currently my client is complete and, get, put and ls work
just like the actual ftp program without using a fork. Is there some
specific reason we need to use fork() that I am not realizing?
A: It is always safe to let a child process or a thread read data
from the server.
If your client sends an ls (LIST) command and thereafter tries to read
data from the server, the server might have already sent data and
closed the passive connection before your client actually reads the
data, in which case you will receive a "broken pipe" error. To prevent
this error, you should spawn a child process (or thread) and
have the child read the data, (i.e., call read()) before you actually
send an ls command. Although the spawned child process or thread may be
initially blocked for a read, once the server receives ls and returns
data, the child can immediately read data. That's the reason why you may need
to spawn another instance to read data. In summary, the recommended
sequence is:
send a PASV command to the server
receive a server response
establish a "data" connection
fork a child process/thread and let it be blocked on read()
send a RETR command to the server
the child reads all data from the server and terminates itself.
Q5: For the client do we have to get the system username who is logged and
display it in the prompt like the real ftp client?
A: Yes. Use getlogin()
A code example is:
#include <unistd.h>
#include <stdio.h>
char *serverIp; // let it point to one of argv[] that includes serverIp.
string userString( getlogin() );
cout << "Name (" << serverIp << ":" << userString << "): ";
Q6: What file modes does the client have to specify when writing to
its local disk a file received from the server?
A: Use S_IRUSR, S_IWUSR, S_IRGRP, and S_IROTH.
For the get command, the client should set these file modes before
writing a fie received from the server:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int file = open( filename, O_WRONLY | OCREAT, mode );
Q7: I received a multiple definition error when compiling my program.
When i tried to put the socket into one class and compile the ftp and
ftpd programs, i am getting a multiple definition error for all of the
functions. any idea why?
A: Use compiler directives #ifndef, #define and #endif
If you code many classes that may refer to each other, the compiler
may read the same header file repeatedly, which causes this multiple
definition error. Use #ifndef, #define, and #endif in your header
file. Assuming your header file name is Socket.h, your file should be:
#ifndef _SOCKET_H_
#define _SOCKET_H_
your class definition
#endif
This will prevent the compiler from scanning the same class definition
multiple times.
Q8: Although I converted all C++ string into C chars, write() still does not work.
Here is my code:
char sendCmd[100];
cin >> cmd;
strcpy( sendCmd, "PASS " );
strcat( sendCmd, cmd );
strcat( sendCmd, "\r\n" );
int nwrite = write( clientSd, sendCmd, sizeof( sendCmd ) );
What's wrong?
A: Make sure to determine the exact number of bytes you will send
If, for example, cmd
is "abc", sendCmd contains "PASS abc\r\n", which is 10 bytes
long. However sizeof( sendCmd ) always return 100 in the above case.
Q9: I have am having trouble with getline()
A: Use cin >>.
The following FAQs only apply to the design of an ftp server
They can be ignored for the current version of this project, which focuses
only on the ftp client.
Q11: Should our FTP server be able to accept multiple client
connections?
For our FTP server do we have to assume we will multiple clients
connected at once? Just for clarification on how an FTP servers work:
if you have multiple clients connecting at once to ONE ftp server, do
you have to spawn an instance of the server process for each client
connected? In such a case then, wouldn't each instance of the server
process have to use a unique port number for the persistent active
connection generally on port 21?
A: Yes
Once the ftp server accepts a new connection, it must spawn a child
process that handles this connection. The parent ftp server must
always wait for a new connection on a given port or a default port
#21. Each child must use a free port when a passive mode is requested
by its client.
Q12: Why is the discrepancy between "telnet hostname port" and ftp
-p port hostname
In the example on a lecture slide, you create an ftp with the command sequence:
>telnet hostname port
but in the assignment we do
>ftp -p port hostname.
why the discrespancy
A: No reason
If I try to find some reasons, the unix ftp can receive only one
argument, (i.e., hostname). All the other parameters must be given as
-vdingkfxut options. So, the project specification somewhat follows
this format.
Q13: Does the client first create a temporary connection of port 21?
To clarify, the client create a temp connection of port 21. at this
point it has to send a userID and a password in order to connect to
the server.
A: If the client does not receive a -p option, you can do so.
If the client has receive a -p option, it must establish a connection
of the port given in -p. This option is necessary to communicate with
your own ftpd that must run on a port rather than 21.
Q14: How can the server get its local IP address?
I am following up about my question how to get the local IP address
for my server to send back its IP address to the client. I tried using
getsockname on the binded socket, but that did not work.
A: Use gethostname, gethostbyname, and inet_ntoa
Use the following code:
#define MAX 200 or what you like
char hostname[MAX];
bzero( hostname, MAX );
gethostname( hostname, MAX );
struct hostent* host = gethostbyname( hostname );
char *ipAddr = inet_ntoa( *( struct in_addr * )*host->h_addr_list );
You'll receive the IP address in ipAddr. For more details of each
system call, use man.
Q15: For the FTP server, how should it handle sending files in binary or
ascii mode?
According to the RFC for FTP, ASCII mode should send each line with a
CRLF character (\r\n) at the end. The problem I am having is when the
server open’s a binary file but in ASCII mode as requested by the
client, how do we figure out where the end of line is to append CRLF
when we are dealing with binary data? Is it sufficient to just open a
file in the corresponding mode and read its entire contents and send
it, or do we need to do further processing depending on the mode?
A: Simply assume that the FTP server can handle only the binary
mode for file transfer.
For the FTP client, you have to always direct the FTP server to
transfer data in the binary mode. Use "TYPE I". For the "ls" command,
both the client and server should assume the ASCII mode.
Q16: How can we check a valid password on the client when not using
anonymous?
A: The server should read the passwd file in its local directory
for a validation.
The server must scan all account/password pairs in this file and check
if there is such a paper that matches the account and password given
by the client through the USER and PASS commands.
Q17: What file modes does the server have to specify when writing to
its local disk a file received from the client?
A: Use S_IRUSR, S_IWUSR, S_IRGRP, and S_IROTH.
Q18: How does the server change its working directory?
A: Use the open and fchdir commands.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int dirfd = open( directoryName, O_DIRECTORY | O_RDONLY );
fchdir( dirfd );
Q19: How can the server send the contents of its current working
directory?
A: Use system( "/bin/ls -l" ) and dup2.
Here is a recommended squence:
create a pipe.
fork a child process.
let the child map the pipe input to descritor 1, (i.e., stdout) using dup2.
let the child run system("/bin/ls -l") to pass ls outputs to the pipe input.
let the parent read the ls outputs through the pipe output.
Q20: I got "./ftpd: Permission denied"
When trying to run your server program, i get a permission denied
error. Here is the ouput:
[user@uw1-320-29 put]$ ./ftpd -p 3375
-bash: ./ftpd: Permission denied
A: Try chmod and a different port.
You might have changed your ftpd's file mode for some
reason. Furthermore, use the last five digitas of your student id for
the sever port.
chmod 700 ftpd
./ftpd -p 33750