This file contains exaples of client and servers using several protocols, might be very usefull.
To a programmer a socket looks and behaves much like a low level file descriptor. This is because commands such as read() and write() work with sockets in the same way they do with files and pipes. The differences between sockets and normal file descriptors occurs in the creation of a socket and through a variety of special operations to control a socket. These operations are different between sockets and normal file descriptors because of the additional complexity in establishing network connections when compared with normal disk access.
For most operations using sockets, the roles of client and server must be assigned. A server is a process which does some function on request from a client. As will be seen in this discussion, the roles are not symmetric and cannot be reversed without some effort.
This description of the use of sockets progresses in three stages:
The use of sockets in a connectionless or datagram mode between client and server processes on the same host. In this situation, the client does not explicitly establish a connection with the server. The client, of course, must know the server's address. The server, in turn, simply waits for a message to show up. The client's address is one of the parameters of the message receive request and is used by the server for response.
The use of sockets in a connected mode between client and server on the same host. In this case, the roles of client and server are further reinforced by the way in which the socket is established and used. This model is often referred to as a connection-oriented client-server model.
The use of sockets in a connected mode between client and server on different hosts. This is the network extension of Stage 2, above.
The connectionless or datagram mode between client and server on different hosts is not explicitly discussed here. Its use can be inferred from the presentations made in Stages 1 and 3.
#include <sys/types.h> #include <sys/socket.h> int socketpair(int af, int type, int protocol, int sv[2])socketpair() results in the creation of two connected sockets. sv[] is the array where the file descriptors for the sockets are returned. Each descriptor in sv[] is associated with one end of the communications link. Each descriptor can be used for both input and output. This means that full two-way communication between a parent process and one child process is possible.
Normally, one descriptor is reserved for use by a parent process and the other descriptor is used by a child process. The parent process closes the descriptor used by the child process. Conversely, the child process closes the descriptor used by the parent process. fork() is still required to pass one of the sockets to a child.
af represents the domain or address family to which the socket belongs. type is the type of socket to create.
Domains refer to the area where the communicating processes exist. Commonly used domains include:
Example (borrowed from 4.3BSD IPC Tutorial by Stuart Sechrest)
#define DATA1 "test string 1" #define DATA2 "test string 2" #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <errno.h> main() { int sockets[2], child; char buf[1024]; /* Get the socket pair */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) { printf("error %d on socketpair\n", errno); exit(1); } /* create child process */ if ((child = fork()) == -1) { printf("fork error %d\n", errno); exit(1); } if (child != 0) { /* this is the parent */ /* close child's end of socket */ close(sockets[0]); /* read message from child */ if (read(sockets[1], buf, sizeof(buf)) < 0) { printf("error %d reading socket\n", errno); exit(1); } printf("-->%s\n", buf); /* write message to child */ if (write(sockets[1], DATA1, sizeof(DATA1)) < 0) { printf("error %d writing socket\n", errno); exit(1); } /* finished */ close(sockets[1]); } else { /* the child */ /* close parent's end of socket */ close(sockets[1]); /* send message to parent */ if (write(sockets[0], DATA2, sizeof(DATA1)) < 0) { printf("error %d writing socket\n", errno); exit(1); } /* get message from parent */ if (read(sockets[0], buf, sizeof(buf)) < 0) { printf("error %d reading socket\n", errno); exit(1); } printf("-->%s\n", buf); /* finished */ close(sockets[0]); } }
#include <sys/types.h> #include <sys/socket.h> int socket(int af, int type, int protocol)socket() is very similar to socketpair() except that only one socket is created instead of two. This is most commonly used if the process you wish to communicate with is not a child process. The af, type, and protocol fields are used just as in the socketpair() system call.
On success, a file descriptor to the socket is returned. On failure, -1 is returned and errno describes the problem.
#include <sys/types.h> #include <sys/socket.h> int bind(int s, struct sockaddr *name, int namelen)Recall that, using socketpair(), sockets could only be shared between parent and child processes or children of the same parent. With a name attached to the socket, any process on the system can describe (and use) it.
In a call to bind(), s is the file descriptor for the socket, obtained from the call to socket(). name is a pointer to a structure of type sockaddr. If the address family is AF_UNIX (as specified when the socket is created), the structure is defined as follows:
struct sockaddr { u_short sa_family; char sa_data[14]; };name.sa_family should be AF_UNIX. name.sa_data should contain up to 14 bytes of a file name which will be assigned to the socket. namelen gives the actual length of name, that is, the length of the initialized contents of the data structure.
A value of 0 is return on success. On failure, -1 is returned with errno describing the error.
Example:
struct sockaddr name; int s; name.sa_family = AF_UNIX; strcpy(name.sa_data, "/tmp/sock"); if((s = socket(AF_UNIX, SOCK_STREAM, 0) < 0) { printf("socket create failure %d\n", errno); exit(0); } if (bind(s, &name, strlen(name.sa_data) + sizeof(name.sa_family)) < 0) printf("bind failure %d\n", errno);
#include <sys/types.h> #include <sys/socket.h> int connect(int s, struct sockaddr *name, int namelen)The bind() call only allows specification of a local address. To specify the remote side of an address connection the connect() call is used. In the call to connect, s is the file descriptor for the socket. name is a pointer to a structure of type sockaddr:
struct sockaddr { u_short sa_family; char sa_data[14]; };As with the bind() system call, name.sa_family should be AF_UNIX. name.sa_data should contain up to 14 bytes of a file name which will be assigned to the socket. namelen gives the actual length of name. A return value of 0 indicates success, while a value of -1 indicates failure with errno describing the error.
A sample code fragment:
struct sockaddr name; name.sa_family = AF_UNIX; strcpy(name.sa_data, "/tmp/sock"); if (connect(s, &name, strlen(name.sa_data) + sizeof(name.sa_family)) < 0) { printf("connect failure %d\n", errno); }
int sendto(int s, char *msg, int len, int flags, struct sockaddr *to, int tolen)This function allows a message msg of length len to be sent on a socket with descriptor s to the socket named by to and tolen, where tolen is the actual length of to. flags will always be zero for our purposes. The number of characters sent is the return value of the function. On error, -1 is returned and errno describes the error.
An example:
struct sockaddr to_name; to_name.sa_family = AF_UNIX; strcpy(to_name.sa_data, "/tmp/sock"); if (sendto(s, buf, sizeof(buf), 0, &to_name, strlen(to_name.sa_data) + sizeof(to_name.sa_family)) < 0) { printf("send failure\n"); exit(1); }
#include <sys/types.h> #include <sys/socket.h> int recvfrom(int s, char *msg, int len, int flags, struct sockaddr *from, int *fromlen)This function allows a message msg of maximum length len to be read from a socket with descriptor s from the socket named by from and fromlen, where fromlen is the actual length of from. The number of characters actually read from the socket is the return value of the function. On error, -1 is returned and errno describes the error. flags may be 0, or may specify MSG_PEEK to examine a message without actually receiving it from the queue.
If no message is available to be read, the process will suspend waiting for one unless the socket is set to nonblocking mode (via an ioctl call).
The system I/O call read() can also be used to read data from a socket.
#include <stdio.h> void close(int s).The I/O call close() will close the socket descriptor s just as it closes any open file descriptor.
Example - sendto() and recvfrom()
/* receiver */ #include <sys/types.h> #include <sys/socket.h> struct sockaddr myname; struct sockaddr from_name; char buf[80]; main() { int sock; int fromlen, cnt; sock = socket(AF_UNIX, SOCK_DGRAM, 0); if (sock < 0) { printf("socket failure %d\n", errno); exit(1); } myname.sa_family = AF_UNIX; strcpy(myname.sa_data, "/tmp/tsck"); if (bind(sock, &myname, strlen(myname.sa_data) + sizeof(name.sa_family)) < 0) { printf("bind failure %d\n", errno); exit(1); } cnt = recvfrom(sock, buf, sizeof(buf), 0, &from_name, &fromlen); if (cnt < 0) { printf("recvfrom failure %d\n", errno); exit(1); } buf[cnt] = '\0'; /* assure null byte */ from_name.sa_data[fromlen] = '\0'; printf("'%s' received from %s\n", buf, from_name.sa_data); } /* sender */ #include <sys/types.h> #include <sys/socket.h> char buf[80]; struct sockaddr to_name; main() { int sock; sock = socket(AF_UNIX, SOCK_DGRAM, 0); if (sock < 0) { printf("socket failure %d\n", errno); exit(1); } to_name.sa_family = AF_UNIX; strcpy(to_name.sa_data, "/tmp/tsck"); strcpy(buf, "test data line"); cnt = sendto(sock, buf, strlen(buf), 0, &to_name, strlen(to_name.sa_data) + sizeof(to_name.sa_family)); if (cnt < 0) { printf("sendto failure %d\n", errno); exit(1); } }
The socket type is specified as SOCK_STREAM. As a result, the process receiving a message processes that message by the following rules:
#include <sys/types.h> #include <sys/socket.h> int listen(int s, int backlog)listen establishes the socket as a passive endpoint of a connection. It does not suspend process execution.
No messages can be sent through this socket. Incoming messages can be received.
s is the file descriptor associated with the socket created using the socket() system call. backlog is the size of the queue of waiting requests while the server is busy with a service request. The current system-imposed maximum value is 5.
0 is returned on success, -1 on error with errno indicating the problem.
Example: #include <sys/types.h> #include <sys/socket.h> int sockfd; /* socket file descriptor */ if(listen(sockfd, 5) < 0) printf ("listen error %d\n", errno);
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *name, int *namelen)The accept() call establishes a client-server connection on the server side. (The client requests the connection using the connect() system call.) The server must have created the socket using socket(), given the socket a name using bind(), and established a listen queue using listen().
sockfd is the socket file descriptor returned from the socket() system call. name is a pointer to a structure of type sockaddr as described above
struct sockaddr { u_short sa_family; char sa_data[14]; };Upon successful return from accept(), this structure will contain the protocol address of the client's socket.
The data area pointed to by namelen should be initialized to the actual length of name. Upon successful return from accept, the data area pointed to by namelen will contain the actual length of the protocol address of the client's socket.
If successful, accept() creates a new socket of the same family, type, and protocol as sockfd. The file descriptor for this new socket is the return value of accept(). This new socket is used for all communications with the client.
If there is no client connection request waiting, accept() will block until a client request is queued.
accept() will fail mainly if sockfd is not a file descriptor for a socket or if the socket type is not SOCK_STREAM. In this case, accept() returns the value -1 and errno describes the problem.
#include <sys/types.h> #include <sys/socket.h> int send(int sd, char *buf, int len, int flags) int recv(int sd, char * buf, int len, int flags)In both cases, sd is the socket descriptor.
For send(), buf points to a buffer containing the data to be sent, len is the length of the data and flags will usually be 0. The return value is the number of bytes sent if successful. If not successful, -1 is returned and errno describes the error.
For recv(), buf points to a data area into which the received data is copied, len is the size of this data area in bytes, and flags is usually either 0 or set to MSG_PEEK if the received data is to be retained in the system after it is received. The return value is the number of bytes received if successful. If not successful, -1 is returned and errno describes the error.
/* Generic program structure for establishing connection-oriented client-server environment. */ /* server program */ #include <stdio.h> #include <errno.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> struct sockaddr myname; char buf[80]; main() { int sock, new_sd, adrlen, cnt; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { printf("server socket failure %d\n", errno); perror("server: "); exit(1); } myname.sa_family = AF_UNIX; strcpy(myname.sa_data, "/tmp/billb"); adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family); unlink("/tmp/billb"); /* defensive programming */ if (bind(sock, &myname, adrlen) < 0) { printf("server bind failure %d\n", errno); perror("server: "); exit(1); } if (listen(sock, 5) < 0 { printf("server listen failure %d\n", errno); perror("server: "); exit(1); } /* Ignore child process termination. */ signal (SIGCHLD, SIG_IGN); /* Place the server in an infinite loop, waiting on connection requests to come from clients. In practice, there would need to be a clean way to terminate this process, but for now it will simply stay resident until terminated by the starting terminal or the super-user. */ while (1) { if (new_sd = accept(sock, &myname, &adrlen)) < 0 { printf("server accept failure %d\n", errno); perror("server: "); exit(1); } /* Create child server process. Parent does no further processing -- it loops back to wait for another connection request. */ printf("Socket address in server %d is %x, %s\n", getpid(), myname.sa_data, myname.sa_data); if (fork() == 0) { /* child process */ close (sock); /* child does not need it */ /* . . . . . */ cnt = read(new_sd, buf, strlen(buf)); printf ("Server with pid %d got message %s\n", getpid(), buf); strcpy (buf, "Message to client"); cnt = write(new_sd, buf, strlen(buf)); printf("Socket address in server %d is %x, %s\n", getpid(), myname.sa_data, myname.sa_data); /* . . . . . */ close (new_sd); /* close prior to exiting */ exit(0); } /* closing bracket for if (fork() ... ) */ } /* closing bracket for while (1) ... ) */ } /* closing bracket for main procedure */ /* client program */ #include <stdio.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> char buf[80]; struct sockaddr myname; main() { int sock, adrlen, cnt; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { printf("client socket failure %d\n", errno); perror("client: "); exit(1); } myname.sa_family = AF_UNIX; strcpy(myname.sa_data, "/tmp/billb"); adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family); if (connect( sock, &myname, adrlen) < 0) { printf("client connect failure %d\n", errno); perror("client: "); exit(1); } /* . . . . . */ strcpy(buf, "Message sent to server"); cnt = write(sock, buf, strlen(buf)); cnt = read(sock, buf, strlen(buf)); printf("Client with pid %d got message %s\n", getpid(), buf); printf("Socket address in server %d is %x, %s\n", getpid(), myname.sa_data, myname.sa_data); /* . . . . . */ exit(0); }
The following pages describe several of the routines available to establish a network connection. All these functions are for use with the AF_INET domain. The concept of network addressing is discussed first.
An IP address is usually written in a dotted-decimal notation of the form N1.N2.N3.N4, where each Ni is a decimal number between 0 and 255 decimal (00 through ff hexadecimal). Addresses are controlled and assigned by the Internet Network Information Center at SRI International. There are currently 5 defined classes of networks, called A, B, C, D and E; classes A, B and C are available to users. Each interface on a host system connected to the Internet must have its own IP address.
A brief synopsis of IP address classes A, B, and C is contained in the following table.
Class/Attribute ABC Address Format N.H.H.HN1.N2.H.HN1.N2.N3.HFirst Octet0 < N < 127128 <= N1 < 192192<= N1 < 224Network IDNN1.N2N1.N2.N3Host IDH.H.HH.HHAvailabilityVery difficultAlmost exhaustedAmple supplyExample10.133.51.224128.247.116.3192.58.103.7SubnetsAs the number of hosts on a given network grew large and more geographically separated, network management considerations and physical layer limitations such as maximum cable length gave impetus to research into possible changes in network installations without massive disruptions. Also, the use of heterogeneous physical layer networks (e.g., Ethernet and Token Ring) with the same IP network address was increasing.
The concept of subnet was introduced to help solve these problems. The benefits for Class A and Class B networks especially are worth the subnet planning and implementation effort.
The basic idea in subnetworking (a common word also used is subnetting) is to partition the host identifier portion of the IP address into two parts:
At the top of the domain are two-letter country designators and three-letter (usually) general category designators within the USA. Some examples are:
ukuug.uk -- UNIX Users Group in the United Kingdom utdallas.edu -- University of Texas at Dallas in the USA ti.com -- Texas Instruments in the USAFurther hierarchies may be established within these organizations and are controlled locally. Some examples of host names are:
csservr2.utdallas.edu -- host csservr2 at UT-D pac1.pac.sc.ti.com -- host pac1 within domain pac within domain sc at TI sunk.ssc.gov -- host sunk at the Superconducting SuperCollider LaboratoryDNS and other software help in maintenance of these naming conventions and in the translation of host names to IP addresses and vice versa.
Entries in this file look like the following:
127.0.0.1 localhost 192.217.44.208 snoopy beagle hound metlife 153.110.34.18 bugs wabbit wascal 153.110.34.19 elmer 153.110.34.20 samNote that more than one name may be associated with a given IP address. This file is used when converting from IP address to host name and vice versa.
Certain network services have been assigned Well Known Ports. The port assignments to network services can be found in the file /etc/services. Selection of a Well Known Port involves searching this file and is done with the following functions:
#include <netdb.h> struct servent *getservbyname(char *name, char *proto) struct servent *getservbyport(int port, char *proto)The two options for proto in each call are tcp for stream oriented communications, and udp for datagram oriented communications. port is the (known) port number when the service name is requested, while name is the character string containing the service name when the port number is requested.
The return value for each function is a pointer to a structure with the following form:
struct servent { char *s_name; /* official name of service */ char **s_aliases; /* alias service name list */ long s_port; /* port service resides at */ char *s_proto; /* protocol to use */ };If a program does not need to communicate with a Well Known Port it is possible to choose an unused port for use by a program. Illustration of this technique is contained in the sample programs below.
#include <netinet/in.h> /* * Internet address (a structure for historical reasons) */ struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; #define s_addr S_un.S_addr /* can be used for most tcp & ip code */ }; /* * Socket address, internet style. */ struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };Filling in the fields for sockaddr_in will produce an Internet version of a socket address.
#include <netdb.h> struct hostent *gethostbyname(char *name) struct hostent *gethostbyaddr(char *addr, int len, int type)name contains the host name for which the IP address is needed. addr points to a structure of type in_addr and len is the size in bytes of this structure. In this discussion type is always AF_INET since the discussion is limited to use of IP addresses on the Internet.
Both calls return a pointer to a host entry structure. This structure has the following form:
struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses from name server */ #define h_addr h_addr_list[0] /* address for backward compatibility */ };
Left as is, communications between, e.g., IBM-compatible PCs and Sun Microsystems workstations would be difficult at best. To overcome this all data sent over a network is converted to Network Byte Order which is, in the context of the above discussion, "big-endian" order.
Routines for converting data between a host's internal representation and Network Byte Order are:
#include <sys/types.h> #include <netinet/in.h> u_long htonl(u_long hostlong); u_short htons(u_short hostshort); u_long ntohl(u_long netlong); u_short ntohs(u_short netshort);These functions are macros and result in the insertion of conversion source code into the calling program. On little-endian machines the code will change the values around to network byte order. On big-endian machines no code is inserted since none is needed; the functions are defined as null.
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> /* in_addr structure */ unsigned long inet_addr(char *ptr) char *inet_ntoa(struct in_addr inaddr)inet_addr() converts a character string in dotted-decimal notation to a 32-bit Internet address. The return value is not consistent, unfortunately. The correct return should be a pointer to a structure of type in_addr but many systems, following an older convention, return only the internal representation of the dotted-decimal notation. The man pages will clarify the situation for the host system on which the function is used.
inet_ntoa() expects a structure of type in_addr as a parameter (note that the structure itself is passed, not a pointer) and returns a pointer to a character string containing the dotted-decimal representation of the Internet address.
vcclient localhost server-port-numberIf vcclient is started on a host different from the host on which vcserver is running, the command line to start vcclient is
vcclient server-host-name server-port-numbervcserver will remain resident until the user terminates it. The cleanest way to do this is
kill -15 pidwhere pid is the process id of vcserver.
/* vcserver.c -- TCP network (virtual
circuit) server */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in structure */
/* This entry allows the program to look up the name
of the host and any alias names associated with it. */
#include <netdb.h>
/* /etc/hosts table entries */
main (int argc, char *argv[])
{
int rc,
/* system call return code */
new_sd, sock, /* server/listen socket
descriptors */
adrlen,
/* sockaddr length */
cnt;
/* number of bytes I/O */
struct sockaddr_in myname; /* Internet socket name */
struct sockaddr_in *nptr; /* ptr to get port number */
struct sockaddr addr; /*
generic socket name */
char buf[80]; /* I/O buffer, kind of small */
/* For lookup in /etc/hosts file. */
struct hostent *hp, *gethostbyaddr();
/* Identify the server process. */
printf("\nThis is the network server with pid %d\n",
getpid() );
/* As in UNIX domain sockets, create a "listen" socket */
if (( sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
printf("network server socket failure
%d\n", errno);
perror("network server");
exit(1);
/* Initialize the fields in the Internet socket name
structure.
*/
myname.sin_family = AF_INET; /* Internet address */
myname.sin_port = 0; /* System will assign port # */
myname.sin_addr.s_addr = INADDR_ANY; /* "Wildcard"
*/
/* Bind the Internet address to the Internet socket */
if (bind(sock, &myname, sizeof(myname) ) < 0 ) {
close(sock); /* defensive programming
*/
printf("network server bind failure
%d\n", errno);
perror("network server");
exit(2);
}
/* Get the port number assigned to the Internet socket.
getsockname() obtains the port number associated
with the bound socket and returns it as part of the
information in the sockaddr addr structure. Note
that, since the port number is not passed directly
by this program to any client, the only way to
"advertise" it is to print it, that is, send it to
the user's stdout. Other than this printout, this
code is not intrinsic to the connectivity process.
*/
adrlen = sizeof(addr); /* need int for return value */
if ( ( rc = getsockname( sock, &addr, &adrlen ) ) < 0 )
{
printf("setwork server getsockname failure
%d\n",
errno);
perror("network server");
close (sock);
exit(3);
}
/* DEBUG CODE: the generic address "addr" is used to
return the socket value obtained from the
getsockname() call. Print this information. In the
generic structure definition, all but the address
family is defined as a char string. After this
call, the generic address structure addr is used to
hold information about the client process. */
printf("\nAfter getsockname():");
printf(" server listen socket data\n");
printf("\taddr.sa_family field value is: %d\n",
addr.sa_family);
printf("\taddr.sa_data string is %d bytes long;\n",
sizeof ( addr.sa_data ) );
printf("\taddr.sa_data string is:");
for ( cnt = 0: cnt < sizeof (addr.sa_data); cnt++)
printf(" %x", addr.sa_data[cnt]);
printf("\n");
/* Now "advertise" the port number assigned to the
socket. In this example, this port number must be
used as the second command line parameter when
starting up the client process. */
/* Note the use of the pointer nptr, with a different
mapping of the allocated memory, to point to the
generic address structure. */
nptr = (struct sockaddr_in *) &addr; /* port # */
printf("\n\tnetwork server: server has port number:
%d\n",
ntohs ( nptr -> sin_port ) );
/* Mark the socket as a "listen-only" or passive
socket */
if ( listen ( sock, 5 ) < 0 ) {
printf("network server bind failure
%d\n", errno);
perror("network server");
close (sock);
exit(4);
}
/* Debug output: information contained in myname structure
(the Internet
socket).
*/
printf("Server has set up client socket with values:\n");
printf("\tInternet address is %lx\n",
myname.sin_addr.s_addr);
printf("\tPort number used is %d\n", myname.sin_port);
printf("\tInternet family ID is %d\n", myname.sin_family);
printf("\tValues are filled in after connection request
");
printf("is accepted.");
/* Set up "infinite loop" to listen for clients.
Since the
structure "myname" is bound to the
listen socket, the
socket structure name and socket length parameter
values are omitted from the accept call.
The bound values
are used.
*/
while (1) {
if ( ( new_sd = accept ( sock, 0, 0 ) ) < 0 )
{
printf("network server
accept failure %d\n", errno);
perror("network server");
close (sock);
exit(5);
}
/* Fork child process to handle client service
request */
if ( ( fork() ) == 0 ) { /*
Child process */
int pid;
pid = getpid(); /*
PID of child process */
close (sock); /* Do not need
listen socket in child. */
/* Find out who the client is.
Note the use of the
generic
address structure addr to hold information
about the (connected)
client.
*/
if ((rc = getpeername( new_sd,
&addr, &adrlen )) < 0) {
printf("network
server %d getpeername failure %d\n",
pid, errno);
perror("network
server");
close(new_sd);
exit(6);
}
/* Just for grins, "announce"
the client. Note that,
since pointer
nptr is of type struct sockaddr_in,
the field
names as defined in the structure template
sockaddr_in
can be used to access values in the addr
generic
structure.
*/
printf("\n\tnetwork server
%d:", pid);
printf(" client socket
from host %s\n",
inet_ntoa ( nptr -> sin_addr ) );
printf("\t
has port number %d\n",nptr -> sin_port);
/* Now find all names
associated with the client; this
is the reason
for the /etc/hosts file lookup
declarations.
*/
if (( hp = gethostbyaddr (&nptr
-> sin_addr,4,AF_INET))
!= NULL ) {
printf
("\tfrom hostname: %s\n\twith aliases: ",
hp -> h_name );
while ( *hp
-> h_aliases )
printf ("\n\t\t\t%s", *hp -> h_aliases++ );
printf("\n\n");
}
else {
printf("network
server %d ", pid);
printf("gethostbyaddr
failure %d\n", h_errno);
perror("network
server");
}
/* Exchange data with
client. Clear buffer first. */
do {
/* Take your
pick, depending on system pedigree.
The System V function has not been tested as of
this edition.
*/
bzero( buf,
sizeof(buf)); /* zero buf, BSD call. */
/* memset (buf,0,sizeof(buf));
/* zero buf, S5. */
/* Read
message from remote client; if message length
= 0, quit. */
if (( cnt =
read (new_sd, buf, sizeof(buf))) < 0 ) {
printf("network server %d ", pid);
printf("socket read failure &d\n", errno);
perror("network server");
close(new_sd);
exit(7);
}
else
if (cnt == 0)
{
printf("network server received message");
printf(" of length %d\n", cnt);
printf("network server closing");
printf(" client connection...\n");
close (new_sd);
continue; /* break out of loop */
}
else {
/* Print out message received from client. Send
a message back. */
printf("network server %d received message",pid);
printf(" of length %d\n", cnt);
printf("network server %d received", pid));
printf(" the message %s\n", buf);
bzero (buf, sizeof(buf)); /* zero buf, BSD. */
/* memset(buf,0,sizeof(buf)); /* zero buf, S5. */
strcpy(buf, "Message from server to client");
write (new_sd, buf, sizeof(buf));
} /*
end of message-print else */
} /* end of do loop
statement */
while (cnt != 0); /* do
loop condition */
exit(0); /* Exit child
process */
} /* End of if-child-process true condition */
else /* Not child
process; must be parent process */
close (new_sd); /* Parent
doesn't need work socket. */
} /* end of while (1) */
} /* end of main procedure */
/* vcclient.c -- TCP network (virtual
circuit) client */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in structure */
/* This entry allows the program to look up the name
of the host and any alias names associated with
it. */
#include <netdb.h>
/* /etc/hosts table entries */
main (argc, argv)
int argc;
char *argv[];
/* Expected command line parameters:
argv[0] -- name of executable
argv[1] -- the host name to which connection is desired
argv[2] -- the port number to be used by the client: the
value is the port number assigned to the
server by the server's host system.
This is not elegant, but is very useful for debugging the
connectivity code. For example, if the host name ( argv[1] )
were speficied as "localhost" both client and server
could run on the same system but the network connectivity
code would still be fully exercised.
*/
{
int sock, /*
socket descriptor */
val, /* scratch
variable */
cnt;
/* number of bytes I/O */
struct sockaddr_in myname; /* Internet socket name (addr) */
struct sockaddr_in *nptr; /* pointer to get port number
*/
char buf[80]; /* I/O buffer, kind of small */
/* For lookup in /etc/hosts file. */
struct hostent *hp, *gethostbyaddr();
/* Check that the user supplied all parameters on the
command line. If so, convert argv[2] to
integer; copy it
into the sin_port field of the myname
structure. Use
the htons function to insure that the value is
stored
in network byte order.
*/
if ( argc < 3 ) {
printf("network client failure: required
parameters");
printf(" missing from the command
line\n");
printf("network client: usage");
printf("[executable-name] [host name] [port
number]\n");
exit(1);
}
/* As in UNIX domain, create a client socket to request
service */
if (( sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
printf("network client socket failure
%d\n", errno);
perror("network client");
exit(2);
}
/* Convert user-supplied port number to integer. There is
no consistency check possible in this
set-up. */
myname.sin_port = htons( atoi(argv[2]) ); /* Server port # */
myname.sin_family = AF_INET; /* Internet domain
*/
/* For display purposes only, print out the host name and
the converted port number.
*/
printf("network client %s will try to connect to host
%s\n",
argv[0], argv[1]);
printf("network client %s will use port number %d\n",
argv[0], ntohs (
myname.sin_port ) );
/* Obtain server host information. */
hp = gethostbyname ( argv[1] );
/* This is mainly debug code; if it becomes necessary to
insert it (or if it just feels more comfortable
to
have it in place!!!) don't remove it when all is
working
well. Rather, leave it in place as a
comment. */
if ( hp == NULL ) {
printf("network client gethostbyname failure
%d\n",
errno);
perror("network client");
close ( sock );
exit(3);
}
else {
printf("\nServer information obtained
via");
printf(" gethostbyname:\n");
printf("\tThe official host name is
%s\n", hp -> h_name);
printf("\tThe address type is %d\n", hp
-> h_addrtype);
printf("\tThe length of the address is %d
bytes\n",
hp ->
h_length);
printf("\tThe first host address is %lx\n",
ntohl (
* (int * ) hp -> h_addr_list[0] ) );
printf("\tAlias names for the host
are:\n");
while ( *hp -> h_aliases )
printf( "\t\t%s\n", *hp
-> h_aliases++ );
}
/* Use either memcpy or bcopy as appropriate
(System V vs BSD). */
bcopy ( hp -> h_addr_list[0], &myname.sin_addr.s_addr,
hp -> h_length
);
/* memcpy ( &myname.sin_addr.s_addr, hp -> h_addr_list[0],
hp -> h_length
); */
/* More debug code: Verify the contents of structure myname
prior to trying to connect to the (remote) server.
*/
printf("\nInformation provided in client's");
printf(" connect request\n");
printf("\tRemote host address is %lx\n",
ntohl ( myname.sin_addr.s_addr ) );
printf("\tPort number supplied is %d\n",
ntohs ( myname.sin_port ) );
printf("\tInternet family ID is %d\n", myname.sin_family);
printf("\tsin_zero character string is: %s\n",
myname.sin_zero);
/* Establish socket connection with (remote) server. */
if ( ( connect ( sock, &myname, sizeof(myname) ) ) < 0 ) {
printf("network client %s
connect failure %d\n",
argv[0], errno);
perror("network
client");
close (sock);
exit(4);
}
/* Exchange data with client. Clear buffer bytes first.
*/
/* Take your pick, depending on your system's pedigree. The
System V function has not been tested. */
bzero ( buf, sizeof( buf) ); /* zero
buffer, BSD. */
/* memset ( buf, 0, sizeof( buf) ); /* zero buffer S5. */
strcpy ( buf, "Message from client to server" );
write ( sock, buf, sizeof(buf) );
/* Now read message sent back by server. */
if ( ( cnt = read (sock, buf, sizeof(buf) ) ) < 0 ) {
printf("network client socket read failure
&d\n", errno);
perror("network client");
close(sock);
exit(5);
}
else
printf("network client received the message %s\n", buf);
/* Now send a message with 0 bytes. */
bzero ( buf, sizeof( buf) ); /* zero
buffer, BSD. */
/* memset ( buf, 0, sizeof( buf) ); /* zero buffer S5. */
write ( sock, buf, 0 );
close (sock);
exit(0);
} /* end of main procedure */