filetrans

             FTP Server

A. Third Edition
This is almost the perfect edition that I can make except that some strange behaviour happens when you try to 
transfer file of size bigger than 1M. I highly suspect it is due to the size of buffer of windows socket. The
lab instructor agrees to my opinion. 
Here it comes...
Hi Nick,

I encountered the same error before. Try to run WSAGetLastError() to see the
code of error you get. I also think that it is due to the receiver buffer.
Unfortunately, as far as I know, there is no way to adjust the buffer size since
this is in another layer. You may try to slow the sending process a little bit
or to increase the packet size from 128 up to 1024.
Also, a log file on both sides may help you to figure out what is happening. If
you a doing this, you have to close it after each writing process and open it
again, otherwise you will lose all the data in case of program failure.
Anyway, don't panic, if all other requirements are accomplished, this will not
significantly affect your mark.
Here it goes...
Thank you very much for your message and suggestion. Your suggestions are exactly what I am thinking!
1.  I do have a log functionality which records both client and server actions. However,  in my log there is only "socket error" which results from "send" and "recv".
2.  There are two intuitive way of slowing down transmission: a) sleep after a certain times of sending; b) Continue to try to send for a certain number of times if "send" is not successful. And both ways doesn't solve the problem.
3.  After tracing for many times, I found the problem which is unbelieveable: There are some unknown "packet-like" message received by my "recv" function. What I mean is that in each of my packet, there is a certain type id. However, this received "packet-like" message has unrecognized id. After I ignore these "noise", the program works correctly with up to 22M size of file. At least it is the result of my testing at home.
4. This is unexplainable phenomenon and I want to share with other people in case it is common. The only possible reason I can think about is that this unknown signals are either noise in network or some unknown program which are probing all ports of computers. What do you think?
 
Thank you for your time and attention,

Here it comes...

> Hi Nick,
>
> it is very interesting what you have found about this strange "network
noise".
> Do you have the same "noise" in the lab? Since at home they might be some
> programs or spies which are trying to connect to your port, at school they
must
> be filtered by the server. Please let me know if you trace down such "UFO"
> packets in H905. If yes, could you store them in a file (files)? It would
be
> interesting to take a look inside them.
> Anyway, good that you have solved the problem.
>
> Thanks,
 

Here it goes...

Yes, it seems positive that some "spy" program is residing in my roommate's
computer. You see, when I transfer file from my computer to his computer, I
traced these "noise" which apparently communicating with same port in his
computer. However, when the direction changed, that is when I transfer file
from his computer to my computer, it is quiet. You see, I install
"anti-virus" and a "spysweeper" which will sweep my spywares. So, it is
clean in my computer and I hear no noise coming in or out of my computer.
In Lab H905, it is also quiet even though occasionally I have problem of
disk quota when I transfer file of size of more than 20M.
I record the noise in log file. However, it is almost useless since I don't
know the format. In VC6 debugger, I try to read the message. But it is
definitely not any simple text or system message because it is so frequent.
Enclosed the two logs are for your reference.

Thank you and by the way, should I submit the code file to you?

see you tomorrow,
 

Here it goes...

 

Hi all,
 
I just want to confirm my problem: When I try to transfer file with size more than 1 M, I intercept some unknown messages during transmission. The "unknown messages" mean that these packets are not produced by my program as each packet sent out by my program has a "type ID" defined by me. And these "unknown" messages intercepted usually have random contents, or encrypted.  They always cause my transmission fail because I never expect some other program are sharing the connection with me. It is only after I modified my program to ignore all these "noise" packets that I can succeed in transmitting.
 
I noticed this phenomenon when I transfer file to the computer of my roommate who probably has less sense to resist "hacker's program" like "trojan" etc. And when I change the direction, to transfer file to my computer, the channel is absolute quiet. I guess my computer is clean of those "unknown spyware" who continually communicate with their master program through internet via some ports in computer. And it is by chance that I use the same port that they are using. (I have no knowelege of TCP/IP until this course. That is why I want to ask your experience to confirm my theory. )
 
But I think our lab H905 should be clear of spywares, however, I did notice the similar problem in some cases. (Much less than my roommates' computers.) That is why I begin to suspect my theory. You see, computers in lab will not allow user to install any program from us, so, it is almost impossible to be infected with those spywares, right? Then what are these unknown noise?
 
Another interesting phenomenon in lab is that the transfer rate is different from computer to computer. For example, it is always faster if I transfer file from computer A to computer B even though I switch the position of client and server program in A and B. It is said that it is due to the "token ring" which is asynchronize in routing. I am not quite convinced.
 
have a nice day, 
 
Nick Huang/Qingzhe Huang

 


B.The problem

In network, a client program may request files to be put to or get from server program. You have to use the

socket to implement with a basic file transfer protocol by your own.

 

/********************************************************************************
features:
1. all data and command are sent in a data unit of "packet" (see packet.h)
2. socket transimission function are similar in both server and client side, so,
I decide to write socket as a single class just like "delphi".
3. socket is closed at both client and server side after each file transmission.
4. robust error handling mechnism combined with "exception" handling.
a) file name is checked before send out request.
b) empty file doesn't allow to be sent.
c) when a packet of type of "ErrorType" is received, exception is thrown.
d) when file open error occurs, an "ErrorType" packet is sent to remote host to
abort current operation along with error no message so that remote host
can display error information accordingly.

********************************************************************************/
/*********************************************************************************
file transmission protocol:
1. put file:
a) Client sends an outgoing packet of "type" of "PutType" to start. The "data" field stores
the file name to be sent, "tail" field stores the size of file. The offset of a
constant number of bytes in "data" field also stores the "user ID".
b) Server replies an outgoing packet of type of "PutReplyType" by copying incoming packet to
confirm.
c) Client starts sending data packet till end of file.
d) Client sends out a packet of type of "EOFType" to indicate end of transimission even
though server already gets file size from first packet.
2. get file:
a) Client sends an outgoing packet of "type" of "GetType" to start. The "data" field stores
the file name to be sent, "tail" field stores the size of file. The offset of a
constant number of bytes in "data" field also stores the "user ID".
b) Server replies an outgoing packet of type of "GetReplyType" by copying incoming packet to
confirm.
c) Server starts sending data packet till end of file.
d) Server sends out a packet of type of "EOFType" to indicate end of transimission even
though server already gets file size from first packet.

*********************************************************************************/
 

C.The idea of program
 
It is said that if you want to make a good work you need a good tool. So does it when you want to write a good
 
program. The workspace of VC6 gives you a lot of luxury for tracing unless you know how to do it properly. When
 
programming multi-project, it is very vital to keep consistent coding. Say, server and client, they almost do
 
the exactly same job when transmitting a packet. Then why don't we write a universal class to avoid copy&paste?
 
And why don't we let the "server" project and "client" project shares the same "socket" code in workspace so
 
that whenever you modify the socket, you are updating both projects? Do you understand what I mean?
 
 
Another thing is "exception handling". I guess very few people in computer science never heard about this name.
 
But do people really understand a thing if it is not figured out by his/her own mind? I highly suspect it. If
 
you are given a superior tool and you never know where to apply it. You cannot be regarded how to use it. Many
 
people struggled in "c" with ugly "exit" which is such an arrogant solution for programming. Users are slapping
 
on faces by suddenly losing all their work when they make some trivial typing mistakes. How rude are the "c"
 
programmers! I began to realize the usage of "throw" when I originally transport all my error-handling code to a
 
separate module. Then I realized that I was stuck there: In my driver program I may have a "while loop" menu to
 
ask user to input command repeatedly. But whenever I reached the error module, how can I "float" up to the top
 
level of user interface? At this very moment, I realize the essence of "exception-throw" mechanism. If there is
 
no "exception-handling", my exception will continually "float" up to higher level without "exit" program. It is
 
unable for me to predict what exactly the kind of error-handling way I might apply at the very beginning stage
 
of coding. By doing this, I postpone the error-handling scheme till the last stage of coding.
 
 
The log is quite useful, especially for this kind of server-client program as you cannot monitor the server
 
easily.
 
 
I am using "file-API" functions like "CreateFile", "ReadFile", "WriteFile" which maybe a little faster than
 
those "streams". And probably it is part of reason my socket buffer gets busted. I planned a "try-certain-times-
 
when-error-happens" instead of slowing down transmission by "Sleep" a while. I plan to test this this evening
 
to see if it works.
 
D.The major functions
 
E.Further improvement
It is said that TCP/IP won't lose packet of transfer, but I just continually plan to re-implement this functionality
 
F.File listing
project client:
1. error.h
2. error.cpp
3. log.h
4. log.cpp
5. packet.h
6. socket.h
7. socket.cpp
8. client.cpp
 
project server:
1. error.h
2. error.cpp
3. log.h
4. log.cpp
5. packet.h
6. socket.h
7. socket.cpp
8. server.cpp
workspace setup: 
1. create an empty "workspace".
2. copy all above source code under the workspace.
3. "new" a project named "client" by "add to current workspace".
4. "new" a project named "server" by "add to current workspace".
5. switch between two project and from "project" menu "add files" to project.
6. in "project" menu choose "setting" and "link", add "wsock32.lib" at end.
By doing above, you don't have to modify the "file path" of all head files.
 
file name: error.h
#ifndef ERROR_H
#define ERROR_H
#include <stdio.h>
#include <stdlib.h>
#include "log.h"

const int ErrorNumber=14;
const int SeriousProblem=9;

#define CANNOT_OPEN_LOG				0
#define CANNOT_START_WSASTARTUP			1
#define GETHOSTNAMEFAILED				2
#define REMOTE_GETHOSTNAME_FAILED	 		3
#define SOCKET_FAILED				4
#define CONNECT_FAILED				5
#define CANNOT_BIND_SOCKET				6
#define CANNOT_SETUP_LISTEN				7
#define CONNECTION_INTERRUPT			8
#define CANNOT_OPEN_FILE_FOR_PUT			9
#define CANNOT_OPEN_FILE_FOR_GET			10
#define WAIT_TIME_OUT				11
#define ERROR_DATA_RCVED				12
#define EMPTY_FILE_TO_BE_SENT			13


static char* errorStrings[ErrorNumber]=
{
	"CANNOT_OPEN_LOG",
	"Error in starting WSAStartup",
	"GET HOST NAME FAILED",
	"GET REMOTE HOST NAME FAILED",
	"SOCKET_FAILED",
	"CONNECT_FAILED",
	"CANNOT_BIND_SOCKET",
	"CANNOT_SETUP_LISTEN",
	"TRANSMISSION INTERRUPTED UNEXPECTEDLY",
	"CANNOT_OPEN_FILE_FOR_PUT",
	"CANNOT_OPEN_FILE_FOR_GET",
	"WAIT_TIME_OUT",
	"transmission error happened at remote host",
	"EMPTY_FILE_TO_BE_SENT",
};

struct FTPExcep
{
	int errNo;
	int remoteErrNo;
};

void errorHandler(int errorNo, int remoteErrNo=0);

#endif
 

file name: error.cpp
#include "error.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>


void errorHandler(int errorNo, int remoteErrNo)
{
	FTPExcep excep;
	char errbuf[20];
	writeLog(errorStrings[errorNo]);
	if (errorNo==CONNECTION_INTERRUPT)
	{
		sprintf(errbuf, "The last error is %d", WSAGetLastError());
	}
	else
	{
		sprintf(errbuf, "The last error is %d", GetLastError());
	}
	writeLog(errbuf, true);
	
	excep.errNo=errorNo;
	//this remoteErrno would be shown at remote host
	excep.remoteErrNo=remoteErrNo;
	throw excep;
	//only the minor error need to be handled with exception
	/*
	switch (errorNo)
	{
	case REMOTE_GETHOSTNAME_FAILED:
	case CANNOT_OPEN_FILE_FOR_PUT:
	case CANNOT_OPEN_FILE_FOR_GET:
	case WAIT_TIME_OUT:
	case ERROR_DATA_RCVED:
	case EMPTY_FILE_TO_BE_SENT:
	case CONNECT_FAILED:
		{
			FTPExcep excep;
			excep.errNo=errorNo;
			//this remoteErrno would be shown at remote host
			excep.remoteErrNo=remoteErrNo;
			throw excep;
		}
	}
	//default is to exit
	exit(1);
	*/
}
file name: log.h
#ifndef LOG_H
#define LOG_H	
#include <stdio.h>
#include <stdlib.h>
#include "error.h"

typedef unsigned long ulong;

//this will only be called once
void startLog();

void closeLog();

//write anything you want
ulong writeLog(char* buffer, bool toDisplay=false);

#endif
 
file name: log.cpp
#include "log.h"
#include "error.h"
#include <time.h>
#include <windows.h>

const int MaxLogNameLength=20;

char logName[MaxLogNameLength];

HANDLE log=NULL;

void getTimeStamp(char* buf);


void getTimeStamp(char* buf)
{
	char timeBuf[128];
	_strtime(timeBuf);
	strcpy(buf, timeBuf);
	strcat(buf, " ");
	_strdate(timeBuf);
	strcat(buf, timeBuf);
}

void closeLog()
{
	CloseHandle(log);
}

//this should be called only once
void startLog()
{
	//getTimeStamp(logName);
	//strcat(logName, timeBuf);
	
	//sprintf(logName, "%u", time(0));
	//this should be called only once, so....
	if (log==NULL)
	{
		strcpy(logName, "log");
		strcat(logName, ".txt");
		if ((log=CreateFile(logName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 
			FILE_ATTRIBUTE_NORMAL, NULL))==INVALID_HANDLE_VALUE)
		{
			errorHandler(CANNOT_OPEN_LOG);
		}
	}
}

ulong writeLog(char* buffer, bool toDisplay)
{
	char timeBuf[128];
	//long distance;
	ulong result, temp;
	getTimeStamp(timeBuf);
	
	strcat(timeBuf, ":\t");
	//GetFileSize(log, &distance);
	SetFilePointer(log, 0, NULL, FILE_END);
	WriteFile(log, timeBuf, strlen(timeBuf), &result, NULL);
	WriteFile(log, buffer, strlen(buffer), &result, NULL);
	if (toDisplay)
	{
		printf("%s\n", buffer);
	}
	//strcpy(timeBuf, "\n");
	timeBuf[0]=13;
	timeBuf[1]=10;
	timeBuf[2]=0;
	WriteFile(log, timeBuf, strlen(timeBuf), &temp, NULL);
	return result;
}


file name: packet.h
#ifndef PACKET_H
#define PACKET_H
typedef unsigned short ushort;

const int PacketDataSize=120;


/*
//if it is command, the "data" is the command string
#define COMMAND       0x01
//if it is information, "data" contains "filename", 
//"tail" contains the file size.
#define INFORMATION   0x02
//if it is data, "data" contains data, and "tail" contains "index"
#define DATATYPE	  0x03
//since we have the file size at "negotiation, we don't need sign of eof
//but we have error which contains error msg in "data"
#define ERRORTYPE	  0x04
*/


//all size indicates the length of "data" field of struct packet
//get contains the file name as "data", size of file as "tail"
#define GETTYPE           '1'

//put contains the file name as data, size of file as tail
#define PUTTYPE           '2'

//get reply just acknowledge the get information, the file name is in data, plus file size in tail
#define GETREPLYTYPE	  '3'

//just reply to put, and file name is still in the data.
#define PUTREPLYTYPE	  '4'

//whenever anything goes wrong
#define ERRORTYPE	      '5'

//the size of data is contained in size, in tail, it may contains the index number of packet 
#define DATATYPE	      '6'

//this is a special packet, the data may also contains "data"
#define EOFTYPE	          '7'





struct Packet
{
	ushort type;
	ushort size;
	unsigned long tail;
	char data[PacketDataSize];
};

#endif
file name: socket.h
#ifndef SOCKET_H
#define SOCKET_H
#include <winsock.h>
#include "packet.h"
#include "error.h"

#define REQUEST_PORT 0x7070

//int port=REQUEST_PORT;

const int MessageBufferSize=128;
const int SecondStart=100;



class Socket
{

private:
	union 
	{
		struct sockaddr generic;
		struct sockaddr_in ca_in;
	}ca;
	//socket data types
	fd_set readfds;
	bool bServer;
	int port;
	SOCKET s, ser;
	SOCKADDR_IN sa;         // filled by bind
	SOCKADDR_IN sa_in;      // fill with server info, IP, port
	HOSTENT *hp;
	HOSTENT *rp;
	WSADATA wsadata;
	char buffer[128];
	HANDLE hfile;	
	void writeToLog(bool toDisplay=false);
	void initialize();
	void serverConn();
	void recvFile(unsigned long fileSize);
	void sendFile(unsigned long fileSize);
	void connectTo(char* serverName);
public:
	char localhost[11];
    char remotehost[11];
	void clientConn(char* serverName);
	Packet incoming, outgoing;
	char msg[256];
	void closeFile();
	void exceptionHandler(const FTPExcep& excep);
	~Socket();
	Socket(bool isServer=false);
	void listening();
	int sendPacket(const Packet& packet);
	int recvPacket(Packet& packet);
	int sendMsg();
	int recvMsg();
	void tranFile(Packet& packet);
};


#endif
file name: socket.cpp
/*******************************************************************************
*socket.h   encapsulate socket functions and can act as both client and server
*author: Huang, Qingzhe(Nick), ID: 5037735  GUAN, Huihua(Angel), ID:4778375
*date: Sep. 16, 2004          
*	
*
*
********************************************************************************/

/********************************************************************************
features:
1. all data and command are sent in a data unit of "packet" (see packet.h)
2. socket transimission function are similar in both server and client side, so,
	I decide to write socket as a single class just like "delphi".
3. socket is closed at both client and server side after each file transmission.
4. robust error handling mechnism combined with "exception" handling.
	a) file name is checked before send out request.
	b) empty file doesn't allow to be sent.
	c) when a packet of type of "ErrorType" is received, exception is thrown.
	d) when file open error occurs, an "ErrorType" packet is sent to remote host to
		abort current operation along with error no message so that remote host
		can display error information accordingly.
	
********************************************************************************/
/*********************************************************************************
file transmission protocol:
1. put file:
	a) Client sends an outgoing packet of "type" of "PutType" to start. The "data" field stores
		the	file name to be sent, "tail" field stores the size of file. The offset of a
		constant number of bytes in "data" field also stores the "user ID".			
	b) Server replies an outgoing packet of type of "PutReplyType" by copying incoming packet to
		confirm.
	c) Client starts sending data packet till end of file.
	d) Client sends out a packet of type of "EOFType" to indicate end of transimission even
		though server already gets file size from first packet.
2. get file:
	a) Client sends an outgoing packet of "type" of "GetType" to start. The "data" field stores 
		the	file name to be sent, "tail" field stores the size of file. The offset of a
		constant number of bytes in "data" field also stores the "user ID".			
	b) Server replies an outgoing packet of type of "GetReplyType" by copying incoming packet to
		confirm.
	c) Server starts sending data packet till end of file.
	d) Server sends out a packet of type of "EOFType" to indicate end of transimission even
		though server already gets file size from first packet.

*********************************************************************************/


#include <winsock2.h>
#include <time.h>
#include "socket.h"
#include <string.h>
#include "log.h"
#include "error.h"
#include "packet.h"

const long TimeOutLimit=10;
const int ErrorLimit=40;
char msgbuf[128];

//add a little indicator of ">" to indicating the progress of file transfer
void Socket::sendFile(unsigned long fileSize)
{	
	int counter=0, times2print=fileSize/PacketDataSize;	
	bool begin=false;
	if (times2print>80)
	{
		//divide job into 80 slots and print 1 ">" every slot
		times2print=times2print/80;
	}
	else
	{
		//otherwise, print everytime
		times2print=1;
	}
	while (ReadFile(hfile, outgoing.data, PacketDataSize, (LPDWORD)&outgoing.size, NULL)
		&&outgoing.size!=0)
	{
		outgoing.type=DATATYPE;
		sendPacket(outgoing);
		if (!begin)
		{
			begin=true;
			sprintf(msgbuf, "Begin sending first byte from \"%s\"\n", remotehost);
			writeLog(msgbuf, true);
		}
		counter++;
		if (counter==times2print)
		{
			counter=0;
			printf(">");
		}		
	}
	outgoing.type=EOFTYPE;
	sendPacket(outgoing);
	sprintf(msgbuf, "End of transmission to \"%s\"\n", remotehost);
	writeLog(msgbuf, true);
	sprintf(msgbuf, "Succeed sending total %d bytes\n", fileSize);
	writeLog(msgbuf, true);
	CloseHandle(hfile);
	closesocket(s);
}

void Socket::recvFile(unsigned long fileSize)
{
	double number;
	int counter=0, times2print=fileSize/PacketDataSize;
	bool begin=false;	
	//divide into 80 times for printing progress
	if (times2print>80)
	{
		times2print=times2print/80;
	}
	else
	{
		//otherwise, print everytime
		times2print=1;
	}
	while (true)
	{
		if (recvPacket(incoming)>0)
		{
			if (incoming.type==DATATYPE)
			{
				if (!begin)
				{
					begin=true;
					sprintf(msgbuf, "Begin recieving first byte from \"%s\"\n", remotehost);
					writeLog(msgbuf, true);
				}
				WriteFile(hfile, incoming.data, (DWORD)(incoming.size), (LPDWORD)&number, NULL);
				counter++;
				if (counter==times2print)
				{
					counter=0;
					printf(">");
				}
			}
			else
			{
				if (incoming.type==EOFTYPE)
				{
					sprintf(msgbuf, "End of transmission from \"%s\"\n", remotehost);
					writeLog(msgbuf, true);
					break;
				}
				else
				{
					if (incoming.type<EOFTYPE)
					{
						errorHandler(ERROR_DATA_RCVED);
					}
					//ELSE JUST PRETEND IGNORE
				}
			}
		}
	}
	if (SetEndOfFile(hfile)>0)
	{
		sprintf(msgbuf, "Succeed receiving total %d bytes\n", fileSize);
		writeLog(msgbuf, true);
	}
	
	CloseHandle(hfile);
	closesocket(s);	
}

void Socket::closeFile()
{
	CloseHandle(hfile);
	hfile=NULL;
}

void Socket::tranFile(Packet& packet)
{
	if (!bServer)
	{
		//for client, these are to be sent
		switch(packet.type)
		{
		case PUTTYPE:
			sprintf(msgbuf, "prepare to send file \"%s\"", packet.data);
			writeLog(msgbuf);
			if ((hfile=CreateFile(packet.data, GENERIC_READ, 0, NULL, OPEN_EXISTING, 
				0, 0))==INVALID_HANDLE_VALUE)
			{
				//if cannot open file, then nothing is sent.
				errorHandler(CANNOT_OPEN_FILE_FOR_PUT);
				//packet.type=ERRORTYPE;
				//packet.tail=CANNOT_OPEN_FILE_FOR_PUT;
			}
			else
			{
				packet.tail=GetFileSize(hfile, NULL);
				if (packet.tail==0)
				{
					errorHandler(EMPTY_FILE_TO_BE_SENT);
				}
				unsigned long nameLen=PacketDataSize-SecondStart-1;
				//char temp[256];
				GetUserName(packet.data+SecondStart, &nameLen);
				//GetUserName(temp, &nameLen);
				//int ok=GetLastError();
				sendPacket(packet);
				//for client have to wait for reply	
				long start=time(0);
				do
				{
					if (time(0)-start>TimeOutLimit)
					{
						errorHandler(WAIT_TIME_OUT);
					}
					recvPacket(incoming);
					//error happened at server side
					if (incoming.type==ERRORTYPE)
					{
						//so, here we got the error no from remote host
						errorHandler(ERROR_DATA_RCVED, incoming.tail);
					}
				}while (incoming.type!=PUTREPLYTYPE);
				sendFile(packet.tail);
			}
			break;
		case GETTYPE:
			sprintf(msgbuf, "prepare to receive file \"%s\"", packet.data);
			writeLog(msgbuf);
			if ((hfile=CreateFile(packet.data, GENERIC_WRITE, 0, NULL,
				CREATE_ALWAYS, 0, 0))==INVALID_HANDLE_VALUE)
			{
				//if cannot open file, then nothing is sent
				errorHandler(CANNOT_OPEN_FILE_FOR_GET);
			}
			else
			{
				//send out the request
				unsigned long nameLen=PacketDataSize-SecondStart-1;
				GetUserName(packet.data+SecondStart, &nameLen);
				sendPacket(packet);
				long start=time(0);
				do
				{
					if (time(0)-start>TimeOutLimit)
					{
						errorHandler(WAIT_TIME_OUT);
					}
					recvPacket(incoming);
					//error happened at server side
					if (incoming.type==ERRORTYPE)
					{
						errorHandler(ERROR_DATA_RCVED, incoming.tail);
					}

				}while (incoming.type!=GETREPLYTYPE);
				recvFile(incoming.tail);
			}
			break;
		}
	}
	else
	{
		//for server, this is to be replied
		switch (packet.type)
		{
			case PUTTYPE:
			//strncpy(fileName, packet.data, packet.size);
			sprintf(msgbuf, "User \"%s\" at host \"%s\" requested file \"%s\" to be received.\n", 
				packet.data+SecondStart, remotehost, packet.data);
			writeLog(msgbuf, true);
			printf("Prepare receiving file from \"%s\", waiting...\n", remotehost);

			if ((hfile=CreateFile(packet.data, GENERIC_WRITE, 0, NULL,
				CREATE_ALWAYS, 0, 0))==INVALID_HANDLE_VALUE)			
			{
				//if cannot open file, then nothing is sent.
				errorHandler(CANNOT_OPEN_FILE_FOR_PUT);
				//packet.type=ERRORTYPE;
				//packet.tail=CANNOT_OPEN_FILE_FOR_PUT;
			}
			else
			{
				//GetFileSize(hfile, &packet.tail);
				//this is to reply put request
				outgoing.type=PUTREPLYTYPE;
				outgoing.size=packet.size;
				strncpy(outgoing.data, packet.data, packet.size);
				outgoing.tail=packet.tail;
				sendPacket(outgoing);
				//pass size of file as param
				recvFile(outgoing.tail);
			}
			break;
		case GETTYPE:
			sprintf(msgbuf, "User \"%s\" at host \"%s\" requested file \"%s\" to be send.\n",
				packet.data+SecondStart, remotehost, packet.data);
			writeLog(msgbuf, true);
			printf("Prepare sending file to \"%s\", waiting...\n", remotehost);
			//strncpy(fileName, packet.data, packet.size);
			if ((hfile=CreateFile(packet.data, GENERIC_READ, 0, NULL, OPEN_EXISTING, 
				0, 0))==INVALID_HANDLE_VALUE)			
			{
				//if cannot open file, then nothing is sent
				errorHandler(CANNOT_OPEN_FILE_FOR_GET);
			}
			else
			{
				//send out the request
				outgoing.type=GETREPLYTYPE;
				outgoing.size=packet.size;
				strncpy(outgoing.data, packet.data, packet.size);
				outgoing.tail=GetFileSize(hfile, NULL); //file size is param
				//
				sendPacket(outgoing);
				sendFile(outgoing.tail);
			}
			break;
		}
	}
}

//exception handling is only to send error packet out if it is 
//local error. For remote error, you simply display it.
//in previous version, I send everything, it is meaningless.
void Socket::exceptionHandler(const FTPExcep& excep)
{
	sprintf(msgbuf, "Unexpected error of \"%s\" happened\n", errorStrings[excep.errNo]);
	writeLog(msgbuf, true);
	closeFile();
	if (excep.errNo==ERROR_DATA_RCVED)
	{
		//it is remote error, just show the message
		sprintf(msgbuf, "remote host error \"%s\" happened\n", errorStrings[excep.remoteErrNo]);
		writeLog(msgbuf, true);
	}
	//if (excep.errNo
	else
	{
		if (excep.errNo>=SeriousProblem)
		{
			//if it is local error, we need to notify remote.
			outgoing.type=ERRORTYPE;
			outgoing.tail=excep.errNo;
			//an error packet must be sent out, otherwise remote host will be freezed
			sendPacket(outgoing);
		}
	}
}


void Socket::writeToLog(bool toDisplay)
{
	writeLog(buffer, toDisplay);

	/*
	<< "wsadata.wVersion "       << wsadata.wVersion       << endl
	<< "wsadata.wHighVersion "   << wsadata.wHighVersion   << endl
	<< "wsadata.szDescription "  << wsadata.szDescription  << endl
	<< "wsadata.szSystemStatus " << wsadata.szSystemStatus << endl
	<< "wsadata.iMaxSockets "    << wsadata.iMaxSockets    << endl
	<< "wsadata.iMaxUdpDg "      << wsadata.iMaxUdpDg      << endl;
	*/
}  

int Socket::sendPacket(const Packet& packet)
{
	int result=0;	
	int counter=0;
	while ((result=send(s, (char*)(&packet), sizeof(Packet), 0))==SOCKET_ERROR)
	{
		Sleep(10);
		counter++;
		if (counter==ErrorLimit)
		{
			errorHandler(CONNECTION_INTERRUPT);
		}
	}
	return result;
}

int Socket::recvPacket(Packet& packet)
{
	int result=0;
	int counter=0;
	while ((result=recv(s, (char*)(&packet), sizeof(Packet), 0))==SOCKET_ERROR)
	{
		counter++;
		if (counter==ErrorLimit)
		{
			errorHandler(CONNECTION_INTERRUPT);
		}
	}

	return result;
}

int Socket::sendMsg()
{
	int result=0;	
	result=send(s, msg, strlen(msg), 0);
	return result;
}

int Socket::recvMsg()
{
	int result=0;
	msg[0]='\0';
	result=recv(s, msg, MessageBufferSize, 0);
	msg[result]='\0';
	return result;
}

void Socket::connectTo(char* serverName)
{
	strcpy(remotehost, serverName);
	sprintf(buffer, "Remote host name is: \"%s\"", remotehost);
	writeToLog();
	if((rp=gethostbyname(remotehost)) == NULL)
	{
		  //throw "remote gethostbyname failed\n";
		errorHandler(REMOTE_GETHOSTNAME_FAILED);
	}


      /* For UDP protocol replace SOCK_STREAM with SOCK_DGRAM */

	  //Specify server address for client to connect to server.
	memset(&sa_in,0,sizeof(sa_in));
	memcpy(&sa_in.sin_addr,rp->h_addr,rp->h_length);
	sa_in.sin_family = rp->h_addrtype;   
	sa_in.sin_port = htons(port);

	//Display the host machine internet address
	sprintf(buffer, "Connecting to remote host:%s\n", inet_ntoa(sa_in.sin_addr));
	writeLog(buffer);

	//Connect Client to the server
	if (connect(s,(LPSOCKADDR)&sa_in,sizeof(sa_in)) == SOCKET_ERROR)
	//throw "connect failed\n";
	{
		errorHandler(CONNECT_FAILED);
	}
}

void Socket::clientConn(char* serverName)
{
	if((s = socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET) 
		  //throw "Socket failed\n";
	{
		errorHandler(SOCKET_FAILED);
	}
	connectTo(serverName);
}

void Socket::serverConn()
{
	if((ser = socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET) 
		  //throw "Socket failed\n";
	{
		errorHandler(SOCKET_FAILED);
	}
	//Fill-in Server Port and Address info.
    sa.sin_family = AF_INET;
	sa.sin_port = htons(port);
	sa.sin_addr.s_addr = htonl(INADDR_ANY);
 

	//Bind the server port

	if (bind(ser,(LPSOCKADDR)&sa,sizeof(sa)) == SOCKET_ERROR)
	{
		errorHandler(CANNOT_BIND_SOCKET);
		//throw "can't bind the socket";
	}
     
    sprintf(buffer, "bind is successful\n");
	writeToLog();

	//Successfull bind, now listen for client requests.
	
	sprintf(buffer, "Ftp server starting at host:[%s]\n", localhost);
	writeLog(buffer, true);
	sprintf(buffer, "Waiting to be contacted for transferring files...");
	writeLog(buffer, true);
	

	//listening();
}

void Socket::listening()
{
	int infds=1, outfds=0;//, outNum, inNum;
	int calen=sizeof(ca);
	struct timeval timeout;
	hostent* phost;

	if(listen(ser,10) == SOCKET_ERROR)
	{
		errorHandler(CANNOT_SETUP_LISTEN);
	}
     //    throw "couldn't  set up listen on socket";
    else 
	{
		sprintf(buffer, "Listen was successful\n");
		writeToLog();
	}

	const struct timeval *tp=&timeout;
	FD_ZERO(&readfds);

	//wait loop

	while(true)
	{
		FD_SET(ser,&readfds);  //always check the listener

		if(!(outfds=select(infds,&readfds,NULL,NULL,tp))) {}

		else if (outfds == SOCKET_ERROR) throw "failure in Select";

		else if (FD_ISSET(ser,&readfds))  
		{
			//cout << "got a connection request" << endl; 
			sprintf(buffer, "got a connection request\n");
			writeToLog();
		}

 		//Found a connection request, try to accept. 
		if((s=accept(ser,&ca.generic,&calen))==INVALID_SOCKET)
		{
			throw "Couldn't accept connection\n";
		}
		else
		{
			sprintf(buffer, "accepted connection from %s %d\n",
				inet_ntoa(ca.ca_in.sin_addr), htons(ca.ca_in.sin_port));
			//calen=sizeof(ca);
			//getpeername(s, &ca.generic, &calen);
			//printf("generic.data %s,\n", ca.generic.sa_data);
			phost=gethostbyaddr((char*)&ca.ca_in.sin_addr, sizeof(ca.ca_in.sin_addr), AF_INET);
			//printf("%s\n", phost->h_name);
			strcpy(remotehost, phost->h_name);
			writeToLog();
			break;
		}

	/*
		if ((inNum=recvMsg())>0)
		{
			printf("message from client %s\n", msg);
		}
		if ((outNum=sendMsg())>0)
		{
			printf("message to client %s\n", msg);
		}
		*/
	}
}

Socket::~Socket()
{
	closeLog();
	closesocket(s);
	if (bServer)
	{
		closesocket(ser);
	}
	WSACleanup();
}

Socket::Socket(bool isServer)
{
	initialize();
	bServer=isServer;
	if (!isServer)//CLIENT
	{
		//clientConn();
	}
	else
	{
		serverConn();
	}
}


void Socket::initialize()
{
	startLog();
	port=REQUEST_PORT;
	if (WSAStartup(0x0202,&wsadata)!=0)
	{  
		errorHandler(CANNOT_START_WSASTARTUP);
	} 
	else 
	{
		strcpy(buffer, "WSAStartup was successful\n");
		writeToLog(false);
		sprintf(buffer, "%s %d\n", "wsadata.wVersion ", wsadata.wVersion);
		writeToLog(false);
		sprintf(buffer, "%s %d\n", "wsadata.wVersion ", wsadata.wHighVersion);
		writeToLog(false);
		sprintf(buffer, "%s %s\n", "wsadata.szDescription ", wsadata.szDescription);
		writeToLog(false);
		sprintf(buffer, "%s %s\n", "wsadata.szSystemStatus ", wsadata.szSystemStatus);
		writeToLog(false);
		sprintf(buffer, "%s %d\n", "wsadata.iMaxSockets ", wsadata.iMaxSockets);
		writeToLog(false);
		sprintf(buffer, "%s %d\n", "wsadata.iMaxUdpDg ", wsadata.iMaxUdpDg);
		writeToLog(false);
	 //WriteFile(test,buffer,sizeof(buffer),&dwtest,NULL); 
	}          
    //Display name of local host.

	gethostname(localhost,10);
	/*
	sprintf(buffer, "%s%s%s\n", "server starting on host [", localhost, "]");
	writeToLog(true);
	sprintf(buffer, "Waiting to be contacted for transferring files...");
	writeToLog(true);
	*/

	if((hp=gethostbyname(localhost)) == NULL) 
		//throw "gethostbyname failed\n";
	{
		errorHandler(GETHOSTNAMEFAILED);
	}
		  //Create the socket
 
}
 
file name: client.cpp
/* send and receive codes between client and server */
/* This is your basic WINSOCK shell */
#include <stdio.h>
#include <stdlib.h>
#include "socket.h"
#include "packet.h"
#include "log.h"
#include "error.h"

//reference for used structures

/*  * Host structure

struct  hostent {
        char    FAR * h_name;             official name of host *
        char    FAR * FAR * h_aliases;    alias list *
        short   h_addrtype;               host address type *
        short   h_length;                 length of address *
        char    FAR * FAR * h_addr_list;  list of addresses *
#define h_addr  h_addr_list[0]            address, for backward compat *
};

    * Socket address structure

struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
}; */



int main()
{
	char msgbuf[20];
	char serverName[10];
	Socket client;

	while (true)
	{
		
		printf("\n%s", "Type name of ftp server(quit for exit):");
	
		//Ask for name of remote server
		scanf("%s", serverName);
		if (strcmp(serverName, "quit")==0)
		{
			break;
		}
		try 
		{
			client.clientConn(serverName);
			printf("\nType name of file to be transferred:");
			//strcpy(client.outgoing.data, "nicktest.txt");
			scanf("%s", client.outgoing.data);
			
			//packet.type=packet.type-'0';//change to 
			client.outgoing.size=strlen(client.outgoing.data);
			printf("Type direction of transfer:( get ;  put)");
			//client.outgoing.type='2';
			scanf("%s", msgbuf);
			if (strcmp(msgbuf, "get")==0)
			{
				client.outgoing.type=GETTYPE;
			}
			else
			{
				client.outgoing.type=PUTTYPE;
			}
			printf("Sent request to %s, waiting...\n", serverName);
			
			client.tranFile(client.outgoing);
			}
				
		catch (FTPExcep excep)
		{	
			/*
			printf("Unexpected error of \"%s\" happened\n", errorStrings[excep.errNo]);
			client.closeFile();
			if (excep.errNo==ERROR_DATA_RCVED)
			{
				//it is remote error, just show the message
				printf("remote host error \"%s\" happened\n", errorStrings[excep.remoteErrNo]);
			}
			//if (excep.errNo
			else
			{
				//if it is local error, we need to notify remote.
				client.outgoing.type=ERRORTYPE;
				client.outgoing.tail=excep.errNo;
				//an error packet must be sent out, otherwise remote host will be freezed
				client.sendPacket(client.outgoing);
			}
			*/
			client.exceptionHandler(excep);			
		}		
	}


			
/*
	while (true)
	{
		//scanf("%s\n", client.msg);
		printf("type your message:");
		//gets(msgbuf);
		
		//scanf("type:%c\ndata:%s\n", &type, msgbuf);
		//sprintf(&client.msg[1], "%u", strlen(msgbuf));
		//sprintf(&client.msg[sizeof(int)+1], "%s", msgbuf);
		client.msg[0]=COMMAND;
		//gets(msgbuf);
		strcpy(msgbuf, "their is message here");
		 
		sprintf(&client.msg[1], "%u", strlen(msgbuf));
		sprintf(&client.msg[sizeof(int)+1], "%s", msgbuf);

		
		//packet.size=strlen(msgbuf);
		//packet.tail=0xff;
		sprintf(&client.msg[PacketDataSize+sizeof(int)+1], "%d", 158);
		
		client.sendPacket();
	} 
	*/


    return 0;
}





file name: server.cpp
#include "socket.h"
#include "packet.h"
#include <stdio.h>
#include <stdlib.h>
#include "error.h"

int main()
{
	Socket server(true);
	//int size, tail;
	//char type;
	//char mbuf[128];
	//printf("I am waiting...\n");
	while (true)
	{
		server.listening();
		if (server.recvPacket(server.incoming)>0)
		{
			//printf("User \"%s\" at host \"%s\" requested file \"%s\" to be transmitted\n",
			//	server.incoming.data+ SecondStart, server.remotehost, server.incoming.data);
			try
			{
				server.tranFile(server.incoming);
			}
			//FTPExcep is defined in "error.h" and it is handled in a similar way
			//by both server-side and client-side
			catch (FTPExcep excep)
			{
				/*
				printf("Unexpected error \"%s\" happened\n", errorStrings[excep.errNo]);
				server.closeFile();
				server.outgoing.type=ERRORTYPE;
				server.outgoing.tail=excep.errNo;
				//should send error info to remote host
				server.sendPacket(server.outgoing);
				*/

				server.exceptionHandler(excep);				
			}

			printf("Ftp server starting at host:[%s]\n", server.localhost);
			
			printf("Waiting to be contacted for transferring files...\n");
			
		}

	}

	/*
	while (true)
	{
		if (server.recvMsg()>0)
		{
			//printf("message from client:%s\n", server.msg);
			sscanf(server.msg, "%1c", &type);
			sscanf(&server.msg[1], "%u", &size);
			printf("type:%c\nsize:%u\n", type, size);
			strncpy(mbuf, &server.msg[sizeof(int)+1], size);
			mbuf[size]='\0';
			sscanf(&server.msg[sizeof(int)+1+PacketDataSize], "%u", &tail);
			printf("data:%s\ntail:%u\n", mbuf, tail);
		}
	}
	*/

	
	return 0;
}
 
How to run?
1. run server first.
2. run client and type in computer name of server.
3. type in command "get" or "put" and file name which must be in current file path.

 




                                 back.gif (341 bytes)       up.gif (335 bytes)         next.gif (337 bytes)