Program Socket Programming
Program Socket Programming
Actions: - connect to server and request service - send size-of-sile info to server - start receiving file from server - close connection */ #include #include #include #include #include #include #include <stdio.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <arpa/inet.h> <time.h> <errno.h>
#define SERVER_PORT_ID 6081 #define CLIENT_PORT_ID 6086 #define SERVER_HOST_ADDR "128.119.40.186" /* gaia.cs.umass.edu */ #define MAXSIZE 512 #define ACK 2 #define NACK 3 #define REQUESTFILE 100 #define COMMANDNOTSUPPORTED 150 #define COMMANDSUPPORTED 160 #define BADFILENAME 200 #define FILENAMEOK 400 #define STARTTRANSFER 500 int readn(int sd,char *ptr,int size); int writen(int sd,char *ptr,int size); main(int argc,char *argv[]) { int sockid, newsockid,i,getfile,ack,msg,msg_2,c,len; int no_writen,start_xfer, num_blks,num_last_blk; struct sockaddr_in my_addr, server_addr; FILE *fp; char in_buf[MAXSIZE]; if(argc != 2) {printf("error: usage : sftp filename\n"); exit(0);} no_writen = 0; num_blks = 0; num_last_blk = 0; len = strlen(argv[1]); printf("client: creating socket\n"); if ((sockid = socket(AF_INET,SOCK_STREAM,0)) < 0) { printf("client: socket error : %d\n", errno); exit(0); } printf("client: binding my local socket\n"); bzero((char *) &my_addr,sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = htonl(INADDR_ANY); my_addr.sin_port = htons(CLIENT_PORT_ID); if (bind(sockid ,(struct sockaddr *) &my_addr,sizeof(my_addr)) < 0) {printf("client: bind error :%d\n", errno); exit(0); } printf("client: starting connect\n"); bzero((char *) &server_addr,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_HOST_ADDR); server_addr.sin_port = htons(SERVER_PORT_ID); if (connect(sockid ,(struct sockaddr *) &server_addr, Page 1
sdkjfk.txt sizeof(server_addr)) < 0) {printf("client: connect error :%d\n", errno); exit(0); } /* Once we are here, we've got a connection to the server */ /* tell server that we want to get a file */ getfile = htons(REQUESTFILE); printf("client: sending command request to ftp server\n"); if((writen(sockid,(char *)&getfile,sizeof(getfile))) < 0) {printf("client: write error :%d\n", errno); exit(0);} /* want for go-ahead from server */ msg = 0; if((readn(sockid,(char *)&msg,sizeof(msg)))< 0) {printf("client: read error :%d\n", errno); exit(0); } msg = ntohs(msg); if (msg==COMMANDNOTSUPPORTED) { printf("client: server refused command. goodbye\n"); exit(0); } else printf("client: server replied %d, command supported\n",msg); /* send file name to server */ printf("client: sending filename\n"); if ((writen(sockid,argv[1],len))< 0) {printf("client: write error :%d\n", errno); exit(0);} /* see if server replied that file name is OK */ msg_2 = 0; if ((readn(sockid,(char *)&msg_2,sizeof(msg_2)))< 0) {printf("client: read error :%d\n", errno); exit(0); } msg_2 = ntohs(msg_2); if (msg_2 == BADFILENAME) { printf("client: server reported bad file name. goodbye.\n"); exit(0); } else printf("client: server replied %d, filename OK\n",msg_2); /* CLIENT KNOWS SERVER HAS BEEN ABLE TO OPEN THE FILE IN READ MODE AND IS ASKING FOR GO-AHEAD*/ /* CLIENT NOW OPENS A COPY OF THE FILE IN WRITE MODE AND SENDS THE GOAHEAD TO SERVER*/ printf("client: sending start transfer command\n"); start_xfer = STARTTRANSFER; start_xfer = htons(start_xfer); if ((writen(sockid,(char *)&start_xfer,sizeof(start_xfer)))< 0) {printf("client: write error :%d\n", errno); exit(0); } if ((fp = fopen(argv[1],"w")) == NULL) {printf(" client: local open file error \n");exit(0);}
/*NOW THE CLIENT IS READING INFORMATION FROM THE SERVER REGARDING HOW MANY FULL BLOCKS OF SIZE MAXSIZE IT CAN EXPECT. IT ALSO RECEIVES THE NUMBER OF BYTES REMAINING IN THE LAST PARTIALLY FILLED BLOCK, IF ANY */ if((readn(sockid,(char *)&num_blks,sizeof(num_blks))) < 0) {printf("client: read error on nblocks :%d\n",errno);exit(0);} num_blks = ntohs(num_blks); printf("client: server responded: %d blocks in file\n",num_blks); ack = ACK; ack = htons(ack); if((writen(sockid,(char *)&ack,sizeof(ack))) < 0) {printf("client: ack write error :%d\n",errno);exit(0); Page 2
sdkjfk.txt } if((readn(sockid,(char *)&num_last_blk,sizeof(num_last_blk))) < 0) {printf("client: read error :%d on nbytes\n",errno);exit(0);} num_last_blk = ntohs(num_last_blk); printf("client: server responded: %d bytes last blk\n",num_last_blk); if((writen(sockid,(char *)&ack,sizeof(ack))) < 0) {printf("client: ack write error :%d\n",errno);exit(0); } /* BEGIN READING BLOCKS BEING SENT BY SERVER */ printf("client: starting to get file contents\n"); for(i= 0; i < num_blks; i ++) { if((readn(sockid,in_buf,MAXSIZE)) < 0) {printf("client: block error read: %d\n",errno);exit(0);} no_writen = fwrite(in_buf,sizeof(char),MAXSIZE,fp); if (no_writen == 0) {printf("client: file write error\n");exit(0);} if (no_writen != MAXSIZE) {printf("client: file write error : no_writen is less\n");exit(0);} /* send an ACK for this block */ if((writen(sockid,(char *)&ack,sizeof(ack))) < 0) {printf("client: ack write error :%d\n",errno);exit(0);} printf(" %d...",i); } /*IF THERE IS A LAST PARTIALLY FILLED BLOCK, READ IT */ if (num_last_blk > 0) { printf("%d\n",num_blks); if((readn(sockid,in_buf,num_last_blk)) < 0) {printf("client: last block error read :%d\n",errno);exit(0);} no_writen = fwrite(in_buf,sizeof(char),num_last_blk,fp); if (no_writen == 0) {printf("client: last block file write err :%d\n",errno);exit(0);} if (no_writen != num_last_blk) {printf("client: file write error : no_writen is less 2\n");exit(0);} if((writen(sockid,(char *)&ack,sizeof(ack))) < 0) {printf("client :ack write error :%d\n",errno);exit(0);} } else printf("\n"); /*FILE TRANSFER ENDS. CLIENT TERMINATES AFTER AND SOCKETS*/ fclose(fp); printf("client: FILE TRANSFER COMPLETE\n"); close(sockid); } /* DUE TO THE FACT THAT BUFFER LIMITS IN REACHED, IT IS POSSIBLE THAT READ AND LESS THAN THE NUMBER REQUESTED. HENCE BELOW TO TAKE CARE OF SUCH EXIGENCIES int readn(int sd,char *ptr,int size) { int no_left,no_read; no_left = size; while (no_left > 0) { no_read = read(sd,ptr,no_left); if(no_read <0) return(no_read); if (no_read == 0) break; no_left -= no_read; ptr += no_read; } return(size - no_left); Page 3 KERNEL FOR THE SOCKET MAY BE WRITE MAY RETURN A POSITIVE VALUE WE CALL THE TWO PROCEDURES */ CLOSING ALL ITS FILES
sdkjfk.txt } int writen(int sd,char *ptr,int size) { int no_left,no_written; no_left = size; while (no_left > 0) { no_written = write(sd,ptr,no_left); if(no_written <=0) return(no_written); no_left -= no_written; ptr += no_written; } return(size - no_left); }
---------------------------/* client code using sockets to download a file */ #include #include #include #include #include #include #include <stdio.h> <string.h> <stdlib.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <netdb.h>
#define SERVER_PORT 12345 #define BUF_SIZE 4096 int main( int argc, char **argv) { int c, s, bytes; char buf[BUF_SIZE]; struct hostent *h; struct sockaddr_in channel; if (argc != 3) fatal("Usage: Client server-name file-name"); h = gethostbyname(argv[1]); if (!h) fatal("gethostbyname failed"); s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s<0) fatal("socket"); memset(&channel,0,sizeof(channel)); channel.sin_family = AF_INET; memcpy(&channel.sin_addr.s_addr,h->h_addr, h->h_length); channel.sin_port = htons(SERVER_PORT); c = connect(s,(struct sockaddr *)&channel, sizeof(channel)); if (c<0) fatal("connect failed"); /*Coonection is now established. Send file name including 0 byte at end. */ send(s, argv[2], strlen(argv[2])+1,0); /* Go get the file and write it to standard output */ while (1){ bytes = recv(s, buf, BUF_SIZE,0); if (bytes <= 0 ) exit(0); write(1,buf, bytes); } }/* main */ fatal(char * string) { Page 4
sdkjfk.txt printf("%s\n",string); exit(1); } ----------------------------------------------------------/* This is the server for a very simple file transfer service. This is a "concurrent server" that can handle requests from multiple simultaneous clients. For each client: - get file name and check if it exists - send size of file to client - send file to client, a block at a time - close connection with client */ #include #include #include #include #include #include #include <stdio.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <arpa/inet.h> <time.h> <errno.h>
#define MY_PORT_ID 6081 #define MAXLINE 256 #define MAXSIZE 512 #define #define #define #define #define #define #define ACK NACK REQUESTFILE COMMANDNOTSUPPORTED COMMANDSUPPORTED BADFILENAME FILENAMEOK 2 3 100 150 160 200 400
int writen(int sd,char *ptr,int size); int readn(int sd,char *ptr,int size); main() {
int sockid, newsd, pid, clilen; struct sockaddr_in my_addr, client_addr; printf("server: creating socket\n"); if ((sockid = socket(AF_INET,SOCK_STREAM,0)) < 0) {printf("server: socket error : %d\n", errno); exit(0); } printf("server: binding my local socket\n"); bzero((char *) &my_addr,sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(MY_PORT_ID); my_addr.sin_addr.s_addr = htons(INADDR_ANY); if (bind(sockid ,(struct sockaddr *) &my_addr,sizeof(my_addr)) < 0) {printf("server: bind error :%d\n", errno); exit(0); } printf("server: starting listen \n"); if (listen(sockid,5) < 0) { printf("server: listen error :%d\n",errno);exit(0);} while(1==1) { /* ACCEPT A CONNECTION AND THEN CREATE A CHILD TO DO THE WORK */ /* LOOP BACK AND WAIT FOR ANOTHER CONNECTION */ printf("server: starting accept\n"); if ((newsd = accept(sockid ,(struct sockaddr *) &client_addr, &clilen)) < 0) {printf("server: accept error :%d\n", errno); exit(0); } printf("server: return from accept, socket for this ftp: %d\n", newsd); Page 5
sdkjfk.txt if ( (pid=fork()) == 0) { /* CHILD PROC STARTS HERE. IT WILL DO ACTUAL FILE TRANSFER */ close(sockid); /* child shouldn't do an accept */ doftp(newsd); close (newsd); exit(0); /* child all done with work */ } /* PARENT CONTINUES BELOW HERE */ close(newsd); /* parent all done with client, only child */ } /* will communicate with that client from now on */ } /* CHILD PROCEDURE, WHICH ACTUALLY DOES THE FILE TRANSFER */ doftp(int newsd) { int i,fsize,fd,msg_ok,fail,fail1,req,c,ack; int no_read ,num_blks , num_blks1,num_last_blk,num_last_blk1,tmp; char fname[MAXLINE]; char out_buf[MAXSIZE]; FILE *fp; no_read = 0; num_blks = 0; num_last_blk = 0; /* START SERVICING THE CLIENT */ /* get command code from client.*/ /* only one supported command: 100 - get a file */ req = 0; if((readn(newsd,(char *)&req,sizeof(req))) < 0) {printf("server: read error %d\n",errno);exit(0);} req = ntohs(req); printf("server: client request code is: %d\n",req); if (req!=REQUESTFILE) { printf("server: unsupported operation. goodbye\n"); /* reply to client: command not OK (code: 150) */ msg_ok = COMMANDNOTSUPPORTED; msg_ok = htons(msg_ok); if((writen(newsd,(char *)&msg_ok,sizeof(msg_ok))) < 0) {printf("server: write error :%d\n",errno);exit(0);} exit(0); } /* reply to client: command OK (code: 160) */ msg_ok = COMMANDSUPPORTED; msg_ok = htons(msg_ok); if((writen(newsd,(char *)&msg_ok,sizeof(msg_ok))) < 0) {printf("server: write error :%d\n",errno);exit(0);} fail = FILENAMEOK; if((read(newsd,fname,MAXLINE)) < 0) { printf("server: filename read error :%d\n",errno); fail = BADFILENAME ; } /* IF SERVER CANT OPEN FILE THEN INFORM CLIENT OF THIS AND TERMINATE */ if((fp = fopen(fname,"r")) == NULL) /*cant open file*/ fail = BADFILENAME; tmp = htons(fail); if((writen(newsd,(char *)&tmp,sizeof(tmp))) < 0) {printf("server: write error :%d\n",errno);exit(0); } if(fail == BADFILENAME) {printf("server cant open file\n"); close(newsd);exit(0);} printf("server: filename is %s\n",fname); Page 6
sdkjfk.txt req = 0; if ((readn(newsd,(char *)&req,sizeof(req))) < 0) {printf("server: read error :%d\n",errno);exit(0);} printf("server: start transfer command, %d, received\n", ntohs(req)); /*SERVER GETS FILESIZE AND CALCULATES THE NUMBER OF BLOCKS OF SIZE = MAXSIZE IT WILL TAKE TO TRANSFER THE FILE. ALSO CALCULATE NUMBER OF BYTES IN THE LAST PARTIALLY FILLED BLOCK IF ANY. SEND THIS INFO TO CLIENT, RECEIVING ACKS */ printf("server: starting transfer\n"); fsize = 0;ack = 0; while ((c = getc(fp)) != EOF) {fsize++;} num_blks = fsize / MAXSIZE; num_blks1 = htons(num_blks); num_last_blk = fsize % MAXSIZE; num_last_blk1 = htons(num_last_blk); if((writen(newsd,(char *)&num_blks1,sizeof(num_blks1))) < 0) {printf("server: write error :%d\n",errno);exit(0);} printf("server: told client there are %d blocks\n", num_blks); if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error :%d\n",errno);exit(0); } if (ntohs(ack) != ACK) { printf("client: ACK not received on file size\n"); exit(0); } if((writen(newsd,(char *)&num_last_blk1,sizeof(num_last_blk1))) < 0) {printf("server: write error :%d\n",errno);exit(0);} printf("server: told client %d bytes in last block\n", num_last_blk); if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error :%d\n",errno);exit(0); } if (ntohs(ack) != ACK) { printf("server: ACK not received on file size\n"); exit(0); } rewind(fp); /* ACTUAL FILE TRANSFER STARTS BLOCK BY BLOCK*/
for(i= 0; i < num_blks; i ++) { no_read = fread(out_buf,sizeof(char),MAXSIZE,fp); if (no_read == 0) {printf("server: file read error\n");exit(0);} if (no_read != MAXSIZE) {printf("server: file read error : no_read is less\n");exit(0);} if((writen(newsd,out_buf,MAXSIZE)) < 0) {printf("server: error sending block:%d\n",errno);exit(0);} if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error :%d\n",errno);exit(0);} if (ntohs(ack) != ACK) { printf("server: ACK not received for block %d\n",i); exit(0); } printf(" %d...",i); } if (num_last_blk > 0) { printf("%d\n",num_blks); no_read = fread(out_buf,sizeof(char),num_last_blk,fp); if (no_read == 0) {printf("server: file read error\n");exit(0);} if (no_read != num_last_blk) {printf("server: file read error : no_read is less 2\n");exit(0);} if((writen(newsd,out_buf,num_last_blk)) < 0) {printf("server: file transfer error %d\n",errno);exit(0);} if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error %d\n",errno);exit(0);} if (ntohs(ack) != ACK) { printf("server: ACK not received last block\n"); Page 7
sdkjfk.txt exit(0); } } else printf("\n"); /* FILE TRANSFER ENDS */ printf("server: FILE TRANSFER COMPLETE on socket %d\n",newsd); fclose(fp); close(newsd); } /* TO TAKE CARE OF THE POSSIBILITY OF BUFFER LIMMITS IN THE KERNEL FOR THE SOCKET BEING REACHED (WHICH MAY CAUSE READ OR WRITE TO RETURN FEWER CHARACTERS THAN REQUESTED), WE USE THE FOLLOWING TWO FUNCTIONS */ int readn(int sd,char *ptr,int size) { int no_left,no_read; no_left = size; while (no_left > 0) { no_read = read(sd,ptr,no_left); if(no_read <0) return(no_read); if (no_read == 0) break; no_left -= no_read; ptr += no_read; } return(size - no_left); } int writen(int sd,char *ptr,int size) { int no_left,no_written; no_left = size; while (no_left > 0) { no_written = write(sd,ptr,no_left); if(no_written <=0) return(no_written); no_left -= no_written; ptr += no_written; } return(size - no_left); }
------------------------------------------/* Server code using sockets to download a file */ #include #include #include #include #include #include #include #include <stdio.h> <string.h> <stdlib.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <netdb.h> <sys/fcntl.h>
#define SERVER_PORT 12345 #define BUF_SIZE 4096 #define QUEUE_SIZE 10 int main( int argc, char **argv) { int s,b, l, fd, sa, bytes, on =1; char buf[BUF_SIZE]; struct sockaddr_in channel; Page 8
sdkjfk.txt s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s<0) fatal("socket failed"); /* Build address structure to bind to socket */ memset(&channel,0,sizeof(channel)); channel.sin_family = AF_INET; channel.sin_addr.s_addr = htonl(INADDR_ANY); channel.sin_port = htons(SERVER_PORT); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)); b = bind(s,(struct sockaddr *)&channel,sizeof(channel)); if (b<0) fatal("bind failed"); l = listen(s, QUEUE_SIZE); if (l < 0 ) fatal("listen failed"); /* Socket is now set up and bound. Wait for connection and process it */ while (1){ sa = accept(s,0,0); */ if (sa<0) fatal("accept failed"); recv(sa, buf, BUF_SIZE,0); /*Get and return the file */ fd = open(buf, O_RDONLY); if (fd < 0) fatal("open failed"); while (1){ bytes = read(fd,buf, BUF_SIZE); if (bytes <= 0 ) break; send(sa,buf, bytes,0); } close(fd); close(sa); } }/* main */ fatal(char * string) { printf("%s\n",string); exit(1); } ------------------------------------------/* Sample TCP client */ #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> int main(int argc, char**argv) { int sockfd,n; struct sockaddr_in servaddr,cliaddr; char sendline[1000]; char recvline[1000]; if (argc != 2) { printf("usage: exit(1); } /* Block for connection request
sdkjfk.txt servaddr.sin_addr.s_addr=inet_addr(argv[1]); servaddr.sin_port=htons(32000); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); while (fgets(sendline, 10000,stdin) != NULL) { sendto(sockfd,sendline,strlen(sendline),0, (struct sockaddr *)&servaddr,sizeof(servaddr)); n=recvfrom(sockfd,recvline,10000,0,NULL,NULL); recvline[n]=0; fputs(recvline,stdout); } } ------------------------------------------------------------------/* * tcpserver.c - A simple TCP echo server * usage: tcpserver <port> */ #include #include #include #include #include #include #include #include #include <stdio.h> <unistd.h> <stdlib.h> <string.h> <netdb.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <arpa/inet.h>
#define BUFSIZE 1024 #if 0 /* * Structs exported from in.h */ /* Internet address */ struct in_addr { unsigned int s_addr; }; /* Internet style socket address struct sockaddr_in { unsigned short int sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[...]; }; /* * Struct exported from netdb.h */ /* Domain name service (DNS) struct hostent { char *h_name; /* char **h_aliases; /* int h_addrtype; /* int h_length; /* char **h_addr_list; /* } #endif /* * error - wrapper for perror */ Page 10 host entry */ official name of host */ alias list */ host address type */ length of address */ list of addresses */ */ /* /* /* /* Address family */ Port number */ IP address */ Pad to size of 'struct sockaddr' */
sdkjfk.txt void error(char *msg) { perror(msg); exit(1); } int main(int argc, char **argv) { int parentfd; /* parent socket */ int childfd; /* child socket */ int portno; /* port to listen on */ int clientlen; /* byte size of client's address */ struct sockaddr_in serveraddr; /* server's addr */ struct sockaddr_in clientaddr; /* client addr */ struct hostent *hostp; /* client host info */ char buf[BUFSIZE]; /* message buffer */ char *hostaddrp; /* dotted decimal host addr string */ int optval; /* flag value for setsockopt */ int n; /* message byte size */ /* * check command line arguments */ if (argc != 2) { fprintf(stderr, "usage: %s <port>\n", argv[0]); exit(1); } portno = atoi(argv[1]); /* * socket: create the parent socket */ parentfd = socket(AF_INET, SOCK_STREAM, 0); if (parentfd < 0) error("ERROR opening socket"); /* setsockopt: Handy debugging trick that lets * us rerun the server immediately after we kill it; * otherwise we have to wait about 20 secs. * Eliminates "ERROR on binding: Address already in use" error. */ optval = 1; setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int)); /* * build the server's Internet address */ bzero((char *) &serveraddr, sizeof(serveraddr)); /* this is an Internet address */ serveraddr.sin_family = AF_INET; /* let the system figure out our IP address */ serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* this is the port we will listen on */ serveraddr.sin_port = htons((unsigned short)portno); /* * bind: associate the parent socket with a port */ if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) error("ERROR on binding"); /* * listen: make this socket ready to accept connection requests */ if (listen(parentfd, 5) < 0) /* allow 5 requests to queue up */ error("ERROR on listen"); Page 11
sdkjfk.txt /* * main loop: wait for a connection request, echo input line, * then close connection. */ clientlen = sizeof(clientaddr); while (1) { /* * accept: wait for a connection request */ childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen); if (childfd < 0) error("ERROR on accept"); /* * gethostbyaddr: determine who sent the message */ hostp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr, sizeof(clientaddr.sin_addr.s_addr), AF_INET); if (hostp == NULL) error("ERROR on gethostbyaddr"); hostaddrp = inet_ntoa(clientaddr.sin_addr); if (hostaddrp == NULL) error("ERROR on inet_ntoa\n"); printf("server established connection with %s (%s)\n", hostp->h_name, hostaddrp); /* * read: read input string from the client */ bzero(buf, BUFSIZE); n = read(childfd, buf, BUFSIZE); if (n < 0) error("ERROR reading from socket"); printf("server received %d bytes: %s", n, buf); /* * write: echo the input string back to the client */ n = write(childfd, buf, strlen(buf)); if (n < 0) error("ERROR writing to socket"); close(childfd); } } -------------------------end
Page 12