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]