Hi,

attached are the sources (2 very small C source files).

to compile them on a current Linux system, just run:

gcc daytime-client.c -o daytime-client

and

gcc multi-client-echo-server.c -o multi-client-echo-server

to run the client - you need a working daytime server.

to run the server - just run it in one window. then in another window:

telnet localhost 5060

type a line and see it echoed back to you.
open another telnet session and check that they both can get a proper service.

try to write a client that will only send data, never read data, and see if you can get the server stuck (as we discussed in the meeting today). try to fix the code, by adding the write socket group and see that it overcomes the problem, and yet does not get into a 'busy-wait' situation.

let me know if there are any questions, of even just if you did this and how it worked (i think it'll be safe to discuss this on the mailing list too, if you want - we'll not cause too much noise).

--guy
/* daytime-client.c - a daytime service client */

#include <stdio.h>		/* Basic I/O routines          */
#include <stdlib.h>		/* exit()                      */
#include <string.h>		/* memset(), memcpy()...       */
#include <unistd.h>		/* read(), close()...          */
#include <sys/types.h>		/* standard system types       */
#include <netinet/in.h>		/* Internet address structures */
#include <sys/socket.h>		/* socket interface functions  */
#include <netdb.h>		/* host to IP resolution       */

#define	HOSTNAMELEN	40	/* maximal host name length */
#define	BUFLEN		1024	/* maximum response size    */
#define	PORT		13	/* port of daytime server   */

int main(int argc, char *argv[])
{
    int			rc;            /* system calls return value storage */
    int            	s;             /* socket descriptor */
    char		buf[BUFLEN+1]; /* buffer server answer */
    char*		pc;            /* pointer into the buffer */
    struct sockaddr_in	sa;            /* Internet address struct */
    struct hostent*     hen; 	       /* host-to-IP translation */

    /* check there are enough parameters */
    if (argc < 2) {
	fprintf(stderr, "Missing host name\n");
	exit (1);
    }

    /* Address resolution stage */
    hen = gethostbyname(argv[1]);
    if (!hen) {
  	perror("couldn't resolve host name");
        return -1;
    }

    /* initiate machine's Internet address structure */
    /* first clear out the struct, to avoid garbage  */
    memset(&sa, 0, sizeof(sa));
    /* Using Internet address family */
    sa.sin_family = AF_INET;
    /* copy port number in network byte order */
    sa.sin_port = htons(PORT);
    /* copy IP address into address struct */
    memcpy(&sa.sin_addr.s_addr, hen->h_addr_list[0], hen->h_length);

    /* allocate a free socket                 */
    /* Internet address family, Stream socket */
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
	perror("socket: allocation failed");
        return -1;
    }

    /* now connect to the remote server. the system will	*/
    /* use the 4th binding method (see section 3)		*/
    /* note the cast to a struct sockaddr pointer of the	*/
    /* address of variable sa.					*/
    rc = connect(s, (struct sockaddr *)&sa, sizeof(sa));

    /* check there was no error */
    if (rc) {
	perror("connect");
        return -1;
    }

    /* now that we are connected, start reading the socket	*/
    /* till read() returns 0, meaning the server closed	*/
    /* the connection.					*/
    pc = buf;

    while ((rc = read(s, pc, BUFLEN - (pc-buf))) > 0) {
        pc += rc;
    }
    if (rc < 0) {
        perror("read");
        return -1;
    }

    /* close the socket */
    close(s);

    /* pad a null character to the end of the result */
    *pc = '\0';

    /* print the result */
    printf("Time: %s\n", buf);

    /* and terminate */
    return 0;
}
/* multi-client-echo-server.c - a multi-client echo server */
/* Copyrights - Guy Keren 1999 (c) 			   */

#include <stdio.h>		/* Basic I/O routines 		*/
#include <string.h>		/* memset()      		*/
#include <sys/types.h>		/* standard system types 	*/
#include <netinet/in.h>		/* Internet address structures 	*/
#include <sys/socket.h>		/* socket interface functions 	*/
#include <netdb.h>		/* host to IP resolution 	*/
#include <sys/time.h>		/* for timeout values 		*/
#include <unistd.h>		/* for table size calculations 	*/

#define	PORT		5060	/* port of our echo server */
#define	BUFLEN		1024	/* buffer length 	   */

int main()
{
    int			i;		/* index counter for loop operations */
    int			rc; 		/* system calls return value storage */
    int			s; 		/* socket descriptor */
    int			cs; 		/* new connection's socket descriptor */
    char		buf[BUFLEN+1];  /* buffer for incoming data */
    struct sockaddr_in	sa; 		/* Internet address struct */
    struct sockaddr_in	csa; 		/* client's address struct */
    size_t         	size_csa; 	/* size of client's address struct */
    fd_set		rfd; 		/* set of open sockets */
    fd_set		c_rfd; 		/* set of sockets waiting to be read */
    int			dsize; 		/* size of file descriptors table */

    /* initiate machine's Internet address structure */
    /* first clear out the struct, to avoid garbage  */
    memset(&sa, 0, sizeof(sa));
    /* Using Internet address family */
    sa.sin_family = AF_INET;
    /* copy port number in network byte order */
    sa.sin_port = htons(PORT);
    /* we will accept cnnections coming through any IP	*/
    /* address that belongs to our host, using the	*/
    /* INADDR_ANY wild-card.				*/
    sa.sin_addr.s_addr = INADDR_ANY;

    /* allocate a free socket                 */
    /* Internet address family, Stream socket */
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
	perror("socket: allocation failed");
        return -1;
    }

    /* bind the socket to the newly formed address */
    rc = bind(s, (struct sockaddr *)&sa, sizeof(sa));

    /* check there was no error */
    if (rc) {
	perror("bind");
        return -1;
    }

    /* ask the system to listen for incoming connections	*/
    /* to the address we just bound. specify that up to		*/
    /* 5 pending connection requests will be queued by the	*/
    /* system, if we are not directly awaiting them using	*/
    /* the accept() system call, when they arrive.		*/
    rc = listen(s, 5);

    /* check there was no error */
    if (rc) {
	perror("listen");
        return -1;
    }

    /* remember size for later usage */
    size_csa = sizeof(csa);

    /* calculate size of file descriptors table */
    dsize = getdtablesize(); /* NOTE 1 */

    /* close all file descriptors, except our communication socket	*/
    /* this is done to avoid blocking on tty operations and such.	*/
    for (i=0; i<dsize; i++)
	if (i != s)
	    close(i);

    /* we innitialy have only one socket open,	*/
    /* to receive new incoming connections.	*/
    FD_ZERO(&rfd);
    FD_SET(s, &rfd);
    /* enter an accept-write-close infinite loop */
    while (1) {
	/* the select() system call waits until any of	*/
	/* the file descriptors specified in the read,	*/
	/* write and exception sets given to it, is	*/
	/* ready to give data, send data, or is in an 	*/
	/* exceptional state, in respect. the call will	*/
	/* wait for a given time before returning. in	*/
	/* this case, the value is NULL, so it will	*/
	/* not timeout. dsize specifies the size of the	*/
	/* file descriptor table.			*/
	c_rfd = rfd;
	rc = select(dsize, &c_rfd, NULL, NULL, (struct timeval *)NULL);

	/* if the 's' socket is ready for reading, it	*/
	/* means that a new connection request arrived.	*/
	if (FD_ISSET(s, &c_rfd)) {
	    /* accept the incoming connection */
       	    cs = accept(s, (struct sockaddr *)&csa, &size_csa);

       	    /* check for errors. if any, ignore new connection */
       	    if (cs < 0)
       		continue;

	    /* add the new socket to the set of open sockets */
	    FD_SET(cs, &rfd);

	    /* and loop again */
	    continue;
	}

	/* check which sockets are ready for reading,	*/
	/* and handle them with care.			*/
	for (i=0; i<dsize; i++)
	    if (i != s && FD_ISSET(i, &c_rfd)) {
	    /* read from the socket */
	    rc = read(i, buf, BUFLEN);

	    /* if client closed the connection, or there was an error... */
	    if (rc == 0 || rc < 0) {
		/* close the socket */
		close(i);
	        FD_CLR(i, &rfd);
	    }
	    /* if there was data to read */
	    else {
		/* echo it back to the client */
		rc = write(i, buf, rc); /* NOTE 2 */
                /* if there was a write error... */
                if (rc < 0) {
		    /* close the socket */
		    close(i);
	            FD_CLR(i, &rfd);
                }
	    }
	}
    }

    return 0;
}

/*
 * NOTE 1: getdtablesize() is not POSIX.
 *         better use: getrlimit(RLIMIT_NOFILE,...) 
 *
 * NOTE 2: 2 problems here.
 *         1. no handling of partial writes.
 *         2. no handling of blocking writes (should use a write-set in
 *            select).
 */

---------------------------------------------------------------------
Haifa Linux Club Mailing List (http://www.haifux.org)
To unsub send an empty message to [EMAIL PROTECTED]

Reply via email to