filetrans

UDP Server

    A. Fourth Edition

    This is a hard-working edition that I spent all my thanks-giving day to write and debug. I was dog-tired.

    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.

Can you allow me to write more details in near future?

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

    I will add more comment later.

    ¡¡

    F.File listing

    1. error.h

    2. error.cpp

    3. log.h

    4. log.cpp

    5. packet.h

    6. socket.h

    7. socket.cpp

    8. client.cpp

    9. server.cpp

    10. router.h

    11. router.cpp

    ¡¡

    ¡¡

    ¡¡

    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

    ¡¡

    project router: (This is given by the assignment.)

    1. router.h

    2. router.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. "new" a project named "router" by "add to current workspace".

    6. switch between two project and from "project" menu "add files" to project.

    7. 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=17;
    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
    #define FAILURE_IN_SELECT					14
    #define GET_BUFFER_ERROR					15
    #define MAX_ERROR_NUMBER_REACHED				16

    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",
    	"FAILURE_IN_SELECT",
    	"GET_BUFFER_ERROR",
    	"MAX_ERROR_NUMBER_REACHED",
    };

    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]);
    	sprintf(errbuf, "The last error is %d", GetLastError());
    	/*
    	//if (errorNo==CONNECTION_INTERRUPT||errorNo==ERROR_DATA_RCVED)
    	{
    		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)
    		if ((log=CreateFile(logName, GENERIC_WRITE, 0, NULL, CREATE_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;
    typedef unsigned long ulong;
    typedef unsigned char uchar;

    //const int PacketDataSize=120;
    const int SequenceBitNumber=1;

    /*
    //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
    */

    //acknowledgement 0000.0001
    #define ACKTYPE1		0x01
    //acknowledgement 0000.0000
    #define ACKTYPE0		0x00

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

    //put contains the file name as data, size of file as tail
    //	0001.0000
    #define PUTTYPE			0x10

    /*
    //get reply just acknowledge the get information, the file name is in data, plus file size in tail
    //	0000.1000
    #define GETREPLYTYPE	0x08

    //just reply to put, and file name is still in the data.
    //	0001.0000
    #define PUTREPLYTYPE	0x10
    */

    //the size of data is contained in size, in tail, it may contains the index number of packet
    //	0010.0000
    const uchar DATATYPE=   0x20;

    //this is a special packet, the data may also contains "data"
    //	0100.0000
    const uchar EOFTYPE	 =  0x40;

    //whenever anything goes wrong
    //	0111.0000
    const uchar ERRORTYPE=  0x70;

    //this type_mask happens to be same as error-type?????
    const uchar TYPEMASK=	0xF0;

    //this mask get you the sequence number
    const uchar SEQUENCEMASK=	0x0F;
    /*
    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
    #define SERVER_PORT 5000				//port number of peer host 1
    #define CLIENT_PORT 5001				//port number of peer host 2
    #define SERVER_ROUTER_PORT	7000
    #define CLIENT_ROUTER_PORT	7001
    #define TIMEOUT_USEC		300000
    #define TIMEOUT_MSEC		300
    //int port=REQUEST_PORT;

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

    //for debug
    static bool bTracing=true;

    class Socket
    {

    private:
    	//socket data types
    	fd_set readfds;
    	timeval timeout;
    	bool bServer;
    	int port;
    	SOCKET s, ser;
    	SOCKADDR_IN sa;         // filled by bind
    	SOCKADDR_IN sa_to;      // fill with server info, IP, port
    	sockaddr sa_from;	//just for recv
    	HOSTENT *hp;
    	HOSTENT *rp;
    	WSADATA wsadata;
    	int byteSend, byteRecv;
    	ulong fileSize;
    	ushort sequence, MaxLength;
    	char buffer[128];
    	char msgbuf[128];
    	HANDLE hfile;
    	void writeToLog(bool toDisplay=false);
    	void initialize();
    	void serverConn();
    	void recvFile();
    	void sendFile();
    	void connectTo(char* serverName);
    	uchar getType(uchar theType);
    	void randomStartSequence(char& theType);
    	bool mustRepeat();
    	void incSequence();
    	bool isNewMsg();
    	void sendRequest();
    	void replyRequest();
    public:
    	char localhost[11];
        char remotehost[11];
    	void clientConn(char* serverName);
    	char incoming[MaxMsgSize];	//incoming message is here
    	char outgoing[MaxMsgSize];
    	void closeFile();
    	void exceptionHandler(const FTPExcep& excep);
    	~Socket();
    	Socket(bool isServer=false);
    	bool listening();
    	void sendPacket();
    	void recvPacket();

    	void clientTranFile();
    	void serverTranFile();
    };

    #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;

    const int MaxErrorNumber=16;
    int errorCount;

    bool Socket::mustRepeat()
    {
    	ushort ack;
    	//if no reply, must repeat
    	if (!listening())
    	{
    		return true;
    	}
    	//if reply error,

    	//if reply, but sequence is old, must repeat
    	ack=incoming[0]&SEQUENCEMASK;
    	sprintf(msgbuf, "Sender: received ACK for packet %d\n", ack);
    	writeToLog();
    	//if reply, but sequence is old, must repeat
    	if (ack!=sequence)
    	{
    		return true;
    	}
    	else
    	{
    		//sequence=ack;
    		return false;
    	}
    }

    //this is independent how many bits you have for sequence
    void Socket::incSequence()
    {
    	sequence=(sequence+1)%MaxLength;
    	//clear old sequence
    	outgoing[0]&=TYPEMASK;
    	outgoing[0]|=sequence;
    }

    void Socket::replyRequest()
    {
    	byteSend=1;
    	outgoing[0]=incoming[0];
    	errorCount=0;
    	while (true)
    	{
    		if (listening())
    		{
    			if (getType((uchar)incoming[0])==DATATYPE)
    			{
    				break;
    			}
    			else
    			{
    				errorCount++;
    				sendPacket();
    			}
    		}
    		else
    		{
    			errorCount++;
    		}
    		if (errorCount==MaxErrorNumber)
    		{
    			errorHandler(MAX_ERROR_NUMBER_REACHED);
    		}
    	}
    }

    void Socket::sendRequest()
    {
    	errorCount=0;
    	do
    	{
    		errorCount++;
    		if (errorCount==MaxErrorNumber)
    		{
    			//for debug
    			errorHandler(MAX_ERROR_NUMBER_REACHED);
    		}
    		sendPacket();
    	}while (!listening());
    }

    //sequence already started
    void Socket::sendFile()
    {
    	int counter=0, errorCount=0;
    	bool bLastPacket=false;
    	fileSize=0;

    	//now start sending data
    	outgoing[0]=DATATYPE;
    	outgoing[0]+=sequence;
    	while (true)
    	{
    		//initialize error count for every time of sending
    		errorCount=0;
    		//only execute once
    		if (!bLastPacket)
    		{
    			ReadFile(hfile, outgoing+1, MaxMsgSize-1, (LPDWORD)&byteSend, NULL);
    			if (byteSend==0)
    			{
    				//only execute once
    				bLastPacket=true;

    				//outgoing[0]=EOFTYPE;
    				//outgoing[0]+=sequence;
    				//sprintf(msgbuf, "Sender: this is eof\n");
    				//writeToLog();
    				outgoing[0]&=SEQUENCEMASK;
    				outgoing[0]|=EOFTYPE;
    			}
    		}
    		byteSend++;//because of header
    		//even for the last EOF packet, the algorithm is same
    		do
    		{
    			errorCount++;
    			if (errorCount==MaxErrorNumber)
    			{
    				//for debug
    				errorHandler(MAX_ERROR_NUMBER_REACHED);
    			}
    			sendPacket();
    			sprintf(msgbuf, "Sender: send packet %d\n", sequence);
    			writeToLog();
    		}
    		while (mustRepeat());//error handler is inside
    		//succed sending out last packet
    		//succeed!
    		incSequence();
    		counter++;
    		fileSize+=byteSend-1;//calculate filesize,minus size of header
    		if (bLastPacket)
    		{
    			break;
    		}

    	}
    	//CLEAR TYPE

    	sprintf(msgbuf, "Sender: number of packets sent: %d\n", counter);
    	writeToLog(true);
    	sprintf(msgbuf, "Sender: number of bytes sent: %d\n", fileSize);
    	writeToLog(true);
    	CloseHandle(hfile);
    	//closesocket(s);
    }

    bool Socket::isNewMsg()
    {
    	ushort seq;
    	if (listening())
    	{
    		seq=incoming[0]&SEQUENCEMASK;
    		sprintf(msgbuf, "Receiver: received packet %d\n", seq);
    		writeToLog();
    		if (seq!=sequence)//as receiver
    		{
    			//update sequence here
    			sequence=seq;

    			return true;
    		}
    	}
    	return false;
    }

    //already received data
    void Socket::recvFile()
    {
    	int counter=0, number;
    	bool bStart=false;

    	fileSize=0;
    	byteSend=1;//always one byte to send

    	errorCount=0;
    	WriteFile(hfile, incoming+1, (DWORD)(byteRecv-1), (LPDWORD)&number, NULL);
    	counter++;
    	fileSize+=byteRecv-1;

    	sequence=incoming[0]&SEQUENCEMASK;
    	sprintf(msgbuf, "Receiver: received packet %d\n", sequence);
    	writeToLog();
    	outgoing[0]=incoming[0];
    	sendPacket();//will send anyway
    	sprintf(msgbuf, "Receiver: sent an ACK for packet %d\n", sequence);
    	writeToLog();
    	//cannot allow single packet has infinite errors
    	while (true)
    	{
    		if (isNewMsg())
    		{
    			WriteFile(hfile, incoming+1, (DWORD)(byteRecv-1), (LPDWORD)&number, NULL);
    			counter++;
    			fileSize+=byteRecv-1;
    			errorCount=0;//restart counting
    			//only new message need update
    			outgoing[0]=incoming[0];
    		}
    		else
    		{
    			errorCount++;
    		}

    		if (errorCount==MaxErrorNumber)
    		{
    			errorHandler(MAX_ERROR_NUMBER_REACHED);
    		}
    		sendPacket();
    		sprintf(msgbuf, "Receiver: sent an ACK for packet %d\n", sequence);
    		writeToLog();
    		//the way to break loop
    		if (getType((uchar)incoming[0])==EOFTYPE)
    		{
    			sprintf(msgbuf, "Receiver: transfer completed\n");
    			writeToLog(true);
    			sprintf(msgbuf, "Receiver: number of packet received: %d", counter);
    			writeToLog(true);
    			sprintf(msgbuf, "Receiver: number of bytes received: %d", fileSize);
    			writeToLog(true);
    			SetEndOfFile(hfile);
    			CloseHandle(hfile);
    			break;
    		}
    	}
    }

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

    uchar Socket::getType(uchar theType)
    {
    	//return theType>>4;
    	return TYPEMASK&theType;
    }

    void Socket::randomStartSequence(char& theType)
    {
    	sequence=rand()%MaxLength;//for the very first time
    	theType+=sequence;
    }

    void Socket::clientTranFile()
    {
    	char* fileName;
    	fileName=outgoing+1;
    	//the file name starts from second byte, plus the first type byte
    	byteSend=strlen(fileName)+1;

    	switch(getType((uchar)outgoing[0]))
    	{
    	case PUTTYPE:
    		sprintf(msgbuf, "prepare to send file \"%s\"", fileName);
    		writeToLog(true);
    		if ((hfile=CreateFile(fileName, 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);
    		}
    		else
    		{
    			//file create successfully
    			if (GetFileSize(hfile, NULL)==0)
    			{
    				errorHandler(EMPTY_FILE_TO_BE_SENT);
    			}
    			//the send has to start the sequence

    			sendRequest();
    			randomStartSequence(outgoing[0]);
    			sendFile();
    		}
    		break;
    	case GETTYPE:
    		sprintf(msgbuf, "prepare to receive file \"%s\"", fileName);
    		writeToLog(true);
    		if ((hfile=CreateFile(fileName, 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
    			sendRequest();
    			replyRequest();
    			recvFile();
    		}
    		break;
    	}
    }

    void Socket::serverTranFile()
    {
    	char* fileName;
    	fileName=incoming+1;
    	//usually the data received is no null-terminated.
    	incoming[byteRecv]='\0';
    	//because the answer is always one byte
    	//byteSend=1;
    	//for server, this is to be replied
    	switch (getType((uchar)incoming[0]))
    	{
    	case PUTTYPE:
    		//strncpy(fileName, packet.data, packet.size);
    		sprintf(msgbuf, "host \"%s\" requested file \"%s\" to be received.\n",
    			remotehost, fileName);
    		writeToLog(true);
    		printf("Prepare receiving file from \"%s\", waiting...\n", remotehost);

    		if ((hfile=CreateFile(fileName, 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
    		{
    			replyRequest();
    			recvFile();
    		}
    		break;
    	case GETTYPE:
    		sprintf(msgbuf, "host \"%s\" requested file \"%s\" to be send.\n",
    			remotehost, fileName);
    		writeToLog(true);
    		printf("Prepare sending file to \"%s\", waiting...\n", remotehost);
    		//strncpy(fileName, packet.data, packet.size);
    		if ((hfile=CreateFile(fileName, 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
    		{
    			if (GetFileSize(hfile, NULL)==0)
    			{
    				errorHandler(EMPTY_FILE_TO_BE_SENT);
    			}
    			//replyRequest();
    			randomStartSequence(outgoing[0]);
    			sendFile();
    		}
    		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]);
    	writeToLog(true);
    	//closeFile();
    	if (excep.errNo!=ERROR_DATA_RCVED)
    	{
    		//if it is local error, we need to notify remote.
    		outgoing[0]=ERRORTYPE;
    		byteSend=1;
    		//an error packet must be sent out, otherwise remote host will be freezed
    		sendPacket();
    	}
    }

    void Socket::writeToLog(bool toDisplay)
    {
    	//only write log when tracing mode
    	if (bTracing)
    	{
    		writeLog(msgbuf, toDisplay);
    	}
    }

    void Socket::sendPacket()
    {
    	if (sendto(s, outgoing, byteSend, 0, (const sockaddr*)(&sa_to), sizeof(sa_to))==SOCKET_ERROR)
    	{
    		errorHandler(CONNECTION_INTERRUPT);
    	}
    }

    void Socket::recvPacket()
    {
    	int fromLen=sizeof(sockaddr);
    	if ((byteRecv=recvfrom(ser, incoming, MaxMsgSize, 0, &sa_from, &fromLen))==SOCKET_ERROR)
    	{
    		errorHandler(GET_BUFFER_ERROR);
    	}
    }

    void Socket::connectTo(char* serverName)
    {
    	strcpy(remotehost, serverName);
    	sprintf(msgbuf, "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_to,0,sizeof(sa_to));
    	memcpy(&sa_to.sin_addr,rp->h_addr,rp->h_length);
    	sa_to.sin_family = rp->h_addrtype;
    	sa_to.sin_port = htons(bServer?SERVER_ROUTER_PORT:CLIENT_ROUTER_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_DGRAM,0))==INVALID_SOCKET)
    		  //throw "Socket failed\n";
    	{
    		errorHandler(SOCKET_FAILED);
    	}
    	connectTo(serverName);
    }

    void Socket::serverConn()
    {
    	if((ser = socket(AF_INET,SOCK_DGRAM,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(msgbuf, "starting at host:[%s]\n", localhost);
    	writeToLog(true);
    	//sprintf(buffer, "Waiting to be contacted for transferring files...");
    	//writeLog(buffer, true);

    	//listening();
    }

    bool Socket::listening()
    {
    	int infds=1, outfds=0;//, outNum, inNum;

    	FD_ZERO(&readfds);

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

    	if(outfds=select(infds,&readfds,NULL,NULL,&timeout))
    	{
    		if (outfds == SOCKET_ERROR)
    		{
    			//throw "failure in Select";
    			errorHandler(FAILURE_IN_SELECT);
    		}
    		else
    		{
    			if (FD_ISSET(ser,&readfds))
    			{
    				recvPacket();
    				if (getType((uchar)incoming[0])==ERRORTYPE)
    				{
    					printf("error data received at listening\n");
    					errorHandler(ERROR_DATA_RCVED);
    				}

    				return true;
    			}
    		}
    	}
    	//timeout or other error????
    	return false;
    }

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

    Socket::Socket(bool isServer)
    {
    	bServer=isServer;
    	initialize();
    	serverConn();
    }

    void Socket::initialize()
    {
    	startLog();
    	//local port
    	port=bServer?SERVER_PORT:CLIENT_PORT;
    	MaxLength=1;
    	for (int i=0; i<SequenceBitNumber; i++)
    	{
    		//MaxLength is initialized to be 1 at first.
    		MaxLength*=2;
    	}
    	//sequence=0;

    	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
    	timeout.tv_sec=0;
    	timeout.tv_usec=TIMEOUT_USEC;

    }

    ¡¡

    ¡¡

    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 <string.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(int argc, char* argv[])
    {
    	char choice[10];
    	char routerName[11];
    	Socket client;

    	if (argc>2)
    	{
    		printf("usage: client.exe [trace]\n");
    		exit(0);
    	}
    	if (argc==2)
    	{
    		if (strcmp(argv[1], "trace")==0)
    		{
    			bTracing=true;
    		}
    	}

    	try
    	{
    		printf("input host name for router:");
    		//Ask for name of remote server
    		scanf("%s", routerName);
    		client.clientConn(routerName);
    	}
    	catch (FTPExcep excep)
    	{
    		client.exceptionHandler(excep);
    	}

    	while (true)
    	{
    		printf("Type name of file to be transferred:");
    		//Ask for name of remote server
    		//message starts from second byte
    		scanf("%s", client.outgoing+1);
    		if (strcmp(client.outgoing+1, "quit")==0)
    		{
    			break;
    		}
    		printf("Type direction of transfer:( get ;  put)");
    		scanf("%s", choice);
    		if (strcmp(choice, "get")==0)
    		{
    			client.outgoing[0]=GETTYPE;
    		}
    		else
    		{
    			client.outgoing[0]=PUTTYPE;
    		}
    		try
    		{
    			client.clientTranFile();
    		}
    		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);
    		}
    	}
        return 0;
    }

    file name: server.cpp

    #include "socket.h"
    #include "packet.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include "error.h"

    int main(int argc, char* argv[])
    {
    	Socket server(true);
    	char routerName[11];

    	if (argc>2)
    	{
    		printf("usage: server.exe [trace]\n");
    		exit(0);
    	}
    	if (argc==2)
    	{
    		if (strcmp(argv[1], "trace")==0)
    		{
    			bTracing=true;
    		}
    	}

    	try
    	{
    		printf("input host name for router:");
    		scanf("%s", routerName);
    		server.clientConn(routerName);
    	}
    	catch (FTPExcep excep)
    	{
    		server.exceptionHandler(excep);
    	}

    	while (true)
    	{
    		try
    		{
    			if (server.listening())
    			{
    				server.serverTranFile();
    			}
    		}
    		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);
    		}
    	}

    	return 0;
    }

    ¡¡

    file name: router.h

    //Router Head file
    #ifndef ROUTER_H
    #define ROUTER_H

    #include <winsock.h>
    #include <fstream.h>
    #include <iostream.h>
    #include <time.h>
    #include <list>
    #include <stdio.h>

    using namespace std ;

    #define MAXBUFSIZE 2048				//maximum packet size
    #define MAXHOSTNAMELEN 256			//maximum length of host name
    #define ROUTER_PORT1 7000			//router port number 1
    #define ROUTER_PORT2 7001			//router port number 2
    #define PEER_PORT1 5000				//port number of peer host 1
    #define PEER_PORT2 5001				//port number of peer host 2
    #define TIMEOUT_USEC 300000			//time-out value

    #define TRACE 1

    struct EVENT_LIST
    {
    	bool empty;
    	DWORD count;					//count is the packet number
    	short destination;				//destination of this packet
    	int len;						//length of this packet
    	char Buffer[MAXBUFSIZE];		//buffer for packet
    };

    class Router
    {
    public:
    	char localhost[MAXHOSTNAMELEN];		//local host name
    	//Constructor
    	Router(char *fn="log.txt");

    	//Destructor
    	~Router();

    	void Run();

    private:
    	class ofstream fout;			//log file
    	float damage_rate, delay_rate;				//damage rate: dropped and delayed
    	SOCKET Sock1, Sock2;			//sockets used for communcation with peer host 1 and 2
    	EVENT_LIST FileBuf;		//buffer for delayed packets

    protected:
    	SOCKADDR_IN sa_in_peer1;		// address structure for peer host 1 address
    	SOCKADDR_IN sa_in_peer2;		// address structure for peer host 2 address

    	bool IsDamage() const;
    	bool IsDelayed() const;
    	void SendProc();
    };

    #endif

    ¡¡

    file name: router.cpp

    //Router.cpp
    #include "Router.h"
    //////////////////////////////////////////////////////////
    //
    //	Router Constructor
    //	arguements:
    //		fn: A string of log file name
    //
    //////////////////////////////////////////////////////////

    Router::Router(char *fn)		//Constructor
    {
    	WSADATA wsadata;
    	HOSTENT* hp;
    	char peer_name1[MAXHOSTNAMELEN], peer_name2[MAXHOSTNAMELEN];
    	SOCKADDR_IN sa_in;

    	FileBuf.empty=true;

        try
    	{
    		if (WSAStartup(0x0202,&wsadata)!=0)
    			throw "Error in starting WSAStartup()\n";
    	}

    	//Display any needed error response.
    	catch (char *str) { cerr<<str<<":"<<dec<<WSAGetLastError()<<endl; return;}

    	//Get Host name
    	gethostname(localhost,MAXHOSTNAMELEN);
    	cout<<"Router starting on host:"<<localhost<<endl<<flush;

    	//Open the log file
    	fout.open(fn);

    	try
    	{
    		//Create the Udp Sock1
    		if((Sock1 = socket(AF_INET,SOCK_DGRAM,0))==INVALID_SOCKET)
    			throw "Create UDP Socket1 failed\n";

    		//Fill-in UDP Port and Address info.
    		sa_in.sin_family = AF_INET;
    		sa_in.sin_port = htons(ROUTER_PORT1);
    		sa_in.sin_addr.s_addr = htonl(INADDR_ANY);

    		//Bind the UDP port1
    		if (bind(Sock1,(LPSOCKADDR)&sa_in,sizeof(sa_in)) == SOCKET_ERROR)
    			throw "can't bind the socket1";

    		//Create the Udp Sock2
    		if((Sock2 = socket(AF_INET,SOCK_DGRAM,0))==INVALID_SOCKET)
    			throw "Create UDP Socket2 failed\n";

    		//Fill-in UDP Port and Address info.
    		sa_in.sin_family = AF_INET;
    		sa_in.sin_port = htons(ROUTER_PORT2);
    		sa_in.sin_addr.s_addr = htonl(INADDR_ANY);

    		//Bind the UDP port2
    		if (bind(Sock2,(LPSOCKADDR)&sa_in,sizeof(sa_in)) == SOCKET_ERROR)
    			throw "can't bind the socket2";

    		cout<<"\nPlease enter the first peer host name:"<<flush;		//enter the dropping rate.
    		cin>>peer_name1;
    		cout<<"\nPlease enter the second peer host name:"<<flush;		//enter the dropping rate.
    		cin>>peer_name2;
    		cout<<"\nPlease enter the drop rate:"<<flush;		//enter the dropping rate.
    		cin>>damage_rate;
    		cout<<"\nPlease enter the delay rate:"<<flush;		//enter the dropping rate.
    		cin>>delay_rate;

    		//creat peer host1
    		if((hp=gethostbyname(peer_name1)) == NULL)
    			throw "get server name failed\n";
    		memset(&sa_in_peer1,0,sizeof(sa_in_peer1));
    		memcpy(&sa_in_peer1.sin_addr,hp->h_addr,hp->h_length);
    		sa_in_peer1.sin_family = hp->h_addrtype;
    		sa_in_peer1.sin_port = htons(PEER_PORT1);

    		//creat peer host2
    		if((hp=gethostbyname(peer_name2)) == NULL)
    			throw "get client name failed\n";
    		memset(&sa_in_peer2,0,sizeof(sa_in_peer2));
    		memcpy(&sa_in_peer2.sin_addr,hp->h_addr,hp->h_length);
    		sa_in_peer2.sin_family = hp->h_addrtype;
    		sa_in_peer2.sin_port = htons(PEER_PORT2);

    		if (TRACE)
    		{
    			fout<<"Peer host 1: "<<peer_name1<<endl;
    			fout<<"Peer host 2: "<<peer_name2<<endl;
    			fout<<"Damage Rate: "<<damage_rate<<endl;
    		}
    	}
    	catch (char *str) {cerr<<str<<":"<<dec<<WSAGetLastError()<<endl; exit(1);}

    	srand( (unsigned)time( NULL ) );
    }

    //////////////////////////////////////////////////////////
    //
    //	Router::IsDamage
    //		The function that generates random damages according to damage rate.
    //
    //////////////////////////////////////////////////////////

    bool Router::IsDamage() const
    {
    	return ( (((float)rand())/RAND_MAX) < ((float)damage_rate/100));
    }

    //////////////////////////////////////////////////////////
    //
    //	Router::IsDelayed
    //		The function that generates random delayed according to delay rate.
    //
    //////////////////////////////////////////////////////////

    bool Router::IsDelayed() const
    {
    	return ( (((float)rand())/RAND_MAX) < ((float)delay_rate/100));
    }

    //////////////////////////////////////////////////////////
    //
    //	Router::Run
    //		The function receives packets from peer hosts and forwards to destinations.
    //		It also drops packets and stores delayed packets for future sending.
    //		It calls SendProc to send delayed packets.
    //
    //////////////////////////////////////////////////////////

    void Router::Run()
    {
    	fd_set readfds;
    	struct timeval *tp=new timeval;
    	SOCKADDR from;
    	int RetVal, fromlen, recvlen, wait_count;
    	EVENT_LIST temp;
    	DWORD CurrentTime, count1, count2;

    	count1=0;
    	count2=0;
    	wait_count=0;
    	tp->tv_sec=0;
    	tp->tv_usec=TIMEOUT_USEC;

    	while (1)
    	{
    		try
    		{
    			FD_ZERO(&readfds);
    			FD_SET(Sock1,&readfds);
    			FD_SET(Sock2,&readfds);
    			fromlen=sizeof(from);
    			if((RetVal=select(1,&readfds,NULL,NULL,tp))==SOCKET_ERROR)	//check for incoming packets.
    				throw "Timer error!";
    			else if(RetVal>0)	//There are incoming packets.
    			{
    				if(!FileBuf.empty) wait_count++;
    				if(FD_ISSET(Sock1, &readfds))	//incoming packet from peer host 1
    				{
    					if((recvlen=recvfrom(Sock1, temp.Buffer, sizeof(temp.Buffer), 0, &from, &fromlen))==SOCKET_ERROR)
    					{
    						cout<<"last error is:"<<WSAGetLastError()<<endl;
    						throw " Get buffer error!";
    					}
    					if (TRACE)
    					{
    						fout<<"Router: Receive packet "<<count1<<" from peer host 1"<<endl;
    						cout<<"Router: Receive packet "<<count1<<" from peer host 1"<<endl;
    					}
    					temp.count=count1;
    					count1++;
    					temp.destination=2;
    				}
    				else if(FD_ISSET(Sock2, &readfds))	//incoming packet from peer host 2
    				{
    					if((recvlen=recvfrom(Sock2, temp.Buffer, sizeof(temp.Buffer), 0, &from, &fromlen))==SOCKET_ERROR)
    						throw " Get buffer error!";
    					if (TRACE)
    					{
    						fout<<"Router: Receive packet "<<count2<<" from peer host 2"<<endl;
    						cout<<"Router: Receive packet "<<count2<<" from peer host 2"<<endl;
    					}
    					temp.count=count2;
    					count2++;
    					temp.destination=1;
    				}
    				else continue;
    				temp.len=recvlen;
    				CurrentTime=GetTickCount();
    				if(FileBuf.empty&&IsDelayed())		//if the packet is delayed.
    				{
    					FileBuf=temp;
    					FileBuf.empty=false;
    					if (TRACE)
    					{
    						fout<<"Router: Packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1)<<" has been delayed!"<<endl;
    						cout<<"Router: Packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1)<<" has been delayed!"<<endl;
    					}
    				}
    				else if(IsDamage())	//if the packet is dropped: dropping packet by no forwarding the packet.
    				{
    					if (TRACE)
    					{
    						fout<<"Router: Packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1)<<" has been dropped by router!"<<endl;
    						cout<<"Router: Packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1)<<" has been dropped by router!"<<endl;
    					}
    				}
    				else		//otherwise, packet is forwarded to destination
    				{
    					if(temp.destination==1)	//forward packets received from 2 to 1.
    					{
    						if(sendto(Sock1, temp.Buffer, temp.len,0,(SOCKADDR*)&sa_in_peer1,sizeof(sa_in_peer1))==SOCKET_ERROR)
    							throw "Send packet error!";
    						if (TRACE)
    						{
    							fout<<"Router: Send packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1) <<" to host "<<temp.destination<<endl;
    							cout<<"Router: Send packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1) <<" to host "<<temp.destination<<endl;
    						}
    						if(!FileBuf.empty&&FileBuf.destination==1)
    						{
    							wait_count=0;
    							SendProc();
    						}
    					}
    					else
    					{	//forward packets received from 1 to 2.
    						if(sendto(Sock2, temp.Buffer, temp.len,0,(SOCKADDR*)&sa_in_peer2,sizeof(sa_in_peer2))==SOCKET_ERROR)
    							throw "Send packet error1";
    						if (TRACE)
    						{
    							fout<<"Router: Send packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1) <<" to host "<<temp.destination<<endl;
    							cout<<"Router: Send packet "<<temp.count<<" received from peer host "<<(temp.destination==1?2:1) <<" to host "<<temp.destination<<endl;
    						}
    						if(!FileBuf.empty&&FileBuf.destination==2)
    						{
    							wait_count=0;
    							SendProc();
    						}
    					}
    				}
    			}
    			else //If there is no incoming packet and there is a delayed packets storing in buffer for 3 cycle times (about 0.9 second), call SendProc to send delayed packet.
    			{
    				if(!FileBuf.empty)
    				{
    					wait_count++;
    					if(wait_count>=3)
    					{
    						SendProc();
    						wait_count=0;
    					}
    				}
    			}
    		} //end of try
    		catch(char *str) {cerr<<str<<":"<<dec<<WSAGetLastError()<<endl;}
    	}//end of while
    }

    //////////////////////////////////////////////////////////
    //
    //	Router::SendProc
    //		Send delayed packets to the destinations.
    //
    //////////////////////////////////////////////////////////

    void Router::SendProc()
    {
    	try
    	{
    		if(FileBuf.destination==1)
    			if(sendto(Sock1, FileBuf.Buffer, FileBuf.len,0,(SOCKADDR*)&sa_in_peer1,sizeof(sa_in_peer1))==SOCKET_ERROR)
    						throw "Send packet error!";
    		else
    			if(sendto(Sock2, FileBuf.Buffer, FileBuf.len,0,(SOCKADDR*)&sa_in_peer2,sizeof(sa_in_peer2))==SOCKET_ERROR)
    				throw "Send packet error!";
    		if (TRACE)
    		{
    			fout<<"Router: Send delayed packet "<<FileBuf.count<<" received from peer host "<<(FileBuf.destination==1?2:1)<<" to host "<<FileBuf.destination<<endl;
    			cout<<"Router: Send delayed packet "<<FileBuf.count<<" received from peer host "<<(FileBuf.destination==1?2:1)<<" to host "<<FileBuf.destination<<endl;
    		}
    	}
    	catch(char *str){cerr<<str<<":"<<dec<<WSAGetLastError()<<endl;}
    	FileBuf.empty=true;
    }

    //////////////////////////////////////////////////////////
    //
    //	Router Destructor
    //	arguements:
    //		fn: A string of log file name
    //
    //////////////////////////////////////////////////////////

    Router :: ~Router()
    {
    	closesocket(Sock1);
    	closesocket(Sock2);

    	/* When done, uninstall winsock.dll (WSACleanup()) and exit */
    	WSACleanup();

    	//close log file
    	fout.close();
    }

    //////////////////////////////////////////////////////////
    //
    //	Main function
    //
    //////////////////////////////////////////////////////////

    void main()
    {
    	Router router;
    	router.Run();
    }

    ¡¡

    ¡¡

    How to run?

    1. run the router first and input the host name of client and server. Choose 1-100 for drop rate and 0 for delay rate.

    2. run server first and input the host of router. Then run client and input host name of router.

    3. type in command "get" or "put" and file name which must be in current file path.

    ¡¡