tftp-server2.c

来自「socket编程, 查看server下相应目录的所有文件, 并且可通过tftp下」· C语言 代码 · 共 314 行

C
314
字号
/* TFTP-server1.c
 * 
 * this is a server that recives UDP frames and the sends a talkback
 * signal to the clients specific frame changes with an ACK.
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*******************************************
 * Structure declarations for the format
 * of the TFTP list system 
 ******************************************/
struct tftp_list
{
  unsigned short id_Number;
  char	TFTPlist_type;
  char	TFTPlist_ack;
  unsigned short block_Number;
  char	TFTPlist_cmd[4];
};

/*******************************************
 * This function reports the error and
 * exits back to the shell :
 ******************************************/
static void bail(const char *on_what) 
{
  fputs(strerror(errno),stderr);
  fputs(": ",stderr);
  fputs(on_what,stderr);
  fputc('\n',stderr);
  exit(1);
}

/*******************************************
 * Function to form a frame
 * arguments include:
 * STRUCT	frame type
 * SHORT 	ID number
 * CHAR		U or T UDP or TCP
 * CHAR		A only used by Server as ACK
 * SHORT	block number for data frames
 * CHAR[4]	command string LIST/GET/PUT
 ******************************************/
int format_frame( struct tftp_list *frame, short *id, char t, char a, 
		short *bk, char *cmd ) 
{
  frame->id_Number 	= *id;
  frame->TFTPlist_type 	= t;
  frame->TFTPlist_ack 	= a;
  frame->block_Number 	= *bk;
  strncpy (frame->TFTPlist_cmd, cmd, 4);
  return (0);
}

/*******************************************
 * Function to print a frame
 * arguments include:
 * STRUCT	frame type
 ******************************************/
int print_frame( struct tftp_list *frame ) 
{
unsigned short i;
  printf("[%d]", frame->id_Number);
  printf("[%c]", frame->TFTPlist_type);
  printf("[%c]", frame->TFTPlist_ack);
  printf("[%d]", frame->block_Number);
  printf("[");
  for(i=0; i<4;i++) printf("%c", frame->TFTPlist_cmd[i]);
  printf("]\n");
}

/*******************************************
 * This function reads a directory and
 * places the result into a text file
 * parameters include:
 * char *	dirName
 * char *	sFileOut
 ******************************************/
void ReadDirectory(char * dirName, char * fileName) 
{
FILE *fileOut;
struct dirent **namelist;
int n,c;

  if((fileOut=fopen(fileName,"w"))==NULL)
    bail("B1 cannot open dierctory listing output file");
  n = scandir( dirName, &namelist, 0, alphasort);
  if (n < 0) bail("scandir");
  else 
  {
    c=2;
    while((c)<(n)) 
    {
//      printf("%d\t%s\n", c-1, namelist[c]->d_name);
      fprintf(fileOut, "%d\t%s\n",c-1, namelist[c]->d_name);
      free(namelist[c++]);
    }
    free(namelist);
  }
  fclose(fileOut);
}

/*******************************************
 * this is the main body of the program with 
 * a simple state machine
 ******************************************/
int main(int argc,char **argv)
{
  FILE			*fileListIn;
  int 			z, x, iLen, iLoop;
  char 			cBuf1[512],cBuf2[512], ptrChar, cChar;
  int			RxSocket, TxSocket;
  struct sockaddr_in	server_address, client_address;
  char			*srvr_addr = "IPADDR_ANY";
  char 			*ListFileName="LIST.OUT", *tftpDIR="/tftpboot";
  unsigned int		addr_len;
  unsigned short 	srvr_port, iID, *ptr, iBlockNumber, iExit;
  struct tftp_list 	tftp_list_frame, *ptrFrame;
  time_t		CurrentTime, CheckTime;
  long			lFlags, lFlagsOld;

  printf("-------------------------------------------------\n");
  printf("TFTP list server\n");
  printf("-------------------------------------------------\n");
  printf("usage: TFTP-server <port>\n");
  printf("the port can be omitted and the program defaults \n");
  printf("to UDP port 6969\n");
  printf("-------------------------------------------------\n");

  if( argc == 2 ) srvr_port = atoi(argv[1]);
  else srvr_port = 6969;    
  printf("port is  %d\n", srvr_port );

  memset( &cBuf1, 0, sizeof cBuf1);
  ptrFrame = (struct tftp_list *) &cBuf1[0];

//setting RX socket in here - Blocking
  memset( &server_address, 0, sizeof server_address);
  addr_len 			= sizeof(server_address);
  srvr_addr 			= "192.168.0.203";
  server_address.sin_family 	= AF_INET; 
  server_address.sin_port	= htons(srvr_port);
  server_address.sin_addr.s_addr= inet_addr(srvr_addr);    
  RxSocket = socket(AF_INET,SOCK_DGRAM,0); 
  if(z == -1) bail("01 error in creating a socket");
  z = bind(RxSocket, (struct sockaddr *)&server_address, addr_len);
  if(z == -1) bail("02 could not bind to address and port");    

//get flags for socket - blocking work
  lFlagsOld = fcntl(RxSocket, F_GETFL); 

  while (1)
  {

//set RX socket to BLOCKing modes
    lFlags = lFlagsOld;
    if(fcntl(RxSocket, F_SETFL, lFlags)==-1 )
      bail("02.5 error setting RX flags");

    printf("-------------------------------------------------\n");    
    printf("Waiting... (blocking)...\n");
    printf("-------------------------------------------------\n");    

//RX frame from a cleint
    memset( &cBuf1, 0, sizeof cBuf1);
    z = recvfrom(RxSocket, cBuf1, 22, 0, (struct sockaddr *) &client_address, &addr_len );
    if(z == -1) bail("03 receive error");
//Display RX'ed frame
    ptrFrame = (struct tftp_list *) &cBuf1;
    printf("RX frame\t"); print_frame( ptrFrame );
printf("To make server multi threaded put fork in here..\n");

//clear buffer
    memset( &cBuf2, 0, sizeof cBuf2);

//show IP of sender (not rearly needed but just for reference
    sprintf(cBuf2, "%s:%d", inet_ntoa(client_address.sin_addr), srvr_port );
    printf("return address\t[%s]\n", cBuf2);

// override port talk back on standard port number 6969
    client_address.sin_port = htons(srvr_port);
    if (z == -1) bail("04 Bad address");
    TxSocket = socket(AF_INET, SOCK_DGRAM, 0);  
    if(z == -1) bail("05 error in creating a socket");
    ptrFrame = (struct tftp_list *) cBuf1;

//Set ACK in the frame
    ptrFrame->TFTPlist_ack = 'A';
    printf("-------------------------------------------------\n");    
    printf("TX frame\t"); print_frame ( ptrFrame );
//sleep (1L);
//Send frame
    z = sendto(TxSocket,cBuf1,22,0,(struct sockaddr *)&client_address, addr_len);     

//Reread the contents of the directory
    ReadDirectory( tftpDIR, ListFileName);
    iBlockNumber = 1;

//create non blocking socket
    lFlags = lFlagsOld;
    lFlags |= O_NONBLOCK;
    if(fcntl(RxSocket, F_SETFL, lFlags)==-1 )
      bail("08 error setting RX flags");

//Opne the temp file with LIST information in it
    if((fileListIn=fopen(ListFileName, "r")) ==NULL)
      bail ("09 cannot read the LIST temp file");

    iExit = 0;
//start loop for number of blocks to TX
    while(!iExit)
    {
// clear the payload portion of the frame
      for(iLoop=10; iLoop<512; iLoop++) cBuf1[iLoop]=0;
      ptrFrame = (struct tftp_list *) &cBuf1[0];
      ptrFrame->block_Number = iBlockNumber;
      ptrFrame->TFTPlist_ack = ' ';
      client_address.sin_port = htons(srvr_port);

//get 502 bytes from DIR file list
      for (iLoop=0; iLoop<502; iLoop++)
      {
        cChar = fgetc(fileListIn);
        if (cChar != EOF) 
        {
          cBuf1[10+iLoop] = cChar;
//          printf("%c ", cChar);
        }
        else
        {
          cBuf1[10+iLoop] = cChar;
          iExit = 1;
          break;
        }
      }
      printf("\n");
//transmit the frame
      printf("-------------------------------------------------\n");
      ptrFrame = (struct tftp_list *) &cBuf1;
      printf("TX data frame : "); print_frame( ptrFrame );

printf("client addr\t%s port %d\n", inet_ntoa(client_address.sin_addr), 
ntohs(client_address.sin_port) );

      z = sendto(TxSocket,cBuf1,512,0,(struct sockaddr *)&client_address, addr_len);
      if(z == -1) bail ("10 Error transmitting data block");

//Wait for 2 seconds for an ACK else EXIT
      time(&CheckTime);
      z=-1; iLoop=0;
      memset(&cBuf2,0,sizeof cBuf2);    
      while ( ((CheckTime+2) > time(&CurrentTime)) && (z==(-1)) )
      {
        z = recvfrom(RxSocket,cBuf2,22,0,(struct sockaddr *)&client_address, &addr_len);
      printf("\r%d %d",iLoop++, z);
      }
      printf("\n");
      ptrFrame = (struct tftp_list *) &cBuf2[0];

//error if timeout then no ACK received
      if(z<=0) 
      {
        printf("\tNot received an ACK to TX data frame resetting\n");
        iExit = 1;
      }

//if not correct block number or an ACK exit 
      else if((ptrFrame->block_Number!=iBlockNumber) || (ptrFrame->TFTPlist_ack!='A'))
      {
        printf("RX ACK frame : "); print_frame ( ptrFrame );
        printf("\tBlock number on ACK does not match or Not Ack in frame\n");
        printf("\tBlock number should be %d and is %d\n", iBlockNumber, ptrFrame->block_Number );
        iExit = 1;
      }
      else
      {
        printf("RX ACK frame : "); print_frame ( ptrFrame );
        printf("\tACK Frame received and is ok\n");
        printf("\tBlock number should be %d and is %d\n", iBlockNumber, ptrFrame->block_Number );
        iBlockNumber++;
      }
    } // end while for TX as many blocks as needed

    if (cChar == EOF)
    {
      printf("\tSending MAX block\n");
      for(iLoop=10; iLoop<512; iLoop++) cBuf1[iLoop] = 0;
      ptrFrame = (struct tftp_list *) &cBuf1[0];
      ptrFrame->block_Number = 65535;
      ptrFrame->TFTPlist_ack = ' ';
      client_address.sin_port = htons(srvr_port);  
      z = sendto(TxSocket,cBuf1,22,0,(struct sockaddr *)&client_address, addr_len);
      if(z == -1) bail ("10 Error transmitting data block");
    }
    fclose(fileListIn);
    printf("Resetting server\n");
    
  }
  close (RxSocket);
  close (TxSocket);

}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?