Document 7331201

Download Report

Transcript Document 7331201

Client/Server Distributed Systems
240-322, Semester 1, 2005-2006
7. Sockets (1)
Chapter 4, Brown
 Objectives
– introduce sockets
– look at iterative servers (using TCP)
– look at clients (using TCP)
240-322 Cli/Serv.: sockets 1/7
1
Overview
1. Socket Definition
2. Socket Data Structures
3. Connection-oriented Services
4. Iterative Servers
5. Clients
6. Clients of System Services
7. More Details
240-322 Cli/Serv.: sockets 1/7
2
1. Socket Definition
 A socket
is an ‘end-point’ for
communication at the transport layer.
– for TCP, a socket is like a telephone
– for UDP, a socket is like a mailbox
 There
is a socket at each end of the
communication:
– one used by the client, one used by the server
240-322 Cli/Serv.: sockets 1/7
3
2. Socket Data Structures
 There
are several kinds of data structure
for sockets, depending on their use:
– as end-points between processes on the same
machine
very rarely used
– as end-points between networked processes
240-322 Cli/Serv.: sockets 1/7
4
Networked Socket Data Structures
struct sockaddr_in {
short sin_family;
/* AF_INET in IPv4*/
u_short sin_port;
/* port no. */
struct in_addr sin_addr; /* IP address */
char sin_zero[8];
/* padding of O’s */
};
struct in_addr {
u_long s_addr;
};
 Details
/* nastier in SunOS */
in /usr/include/netinet/in.h
240-322 Cli/Serv.: sockets 1/7
continued
5
 Due
to the two possible uses of sockets,
most functions require that sockets be cast
into a ‘generic’ socket data type:
struct sockaddr {
u_short sa_family;
char sa_data[14];
}
– see /usr/include/sys/socket.h
240-322 Cli/Serv.: sockets 1/7
6
Socket Types
 Many
socket functions also require a socket
type:
– SOCK_STREAM
for
connection-oriented transport, e.g. TCP
– SOCK_DGRAM
for
connectionless transport, e.g. UDP
– SOCK_RAW
– others
240-322 Cli/Serv.: sockets 1/7
7
3. Connection-oriented Servers
 We
will use TCP for connection-oriented
communication.
 Two
basic kinds of server:
– iterative: the server deals with one client at
once
– concurrent: the server can deal with multiple
clients at once
240-322 Cli/Serv.: sockets 1/7
8
Basic client-server Operation
Server
Create socket with socket()
dealt with by
my
tcp_serv_sock()
Initialise socket address data structure
Bind server address to socket with bind()
Client
Initialise server
address details
Create socket with socket()
Listen for connections with listen()
blocks until connection from a client
read()
Connect to server’s
port with connect()
data (request)
write()
process request
data (reply)
read()
write()
240-322 Cli/Serv.: sockets 1/7
9
3.1. tcp_serv_sock()
 My
function which hides the first 4 stages of
the server’s operation:
– contains calls to socket(), bind() and
listen()
 The
binding of the server’s address to the
socket (stage 3) requires a sockaddr_in
struct to be initialised.
240-322 Cli/Serv.: sockets 1/7
10
Initialising a sockaddr_in struct
for a server
serv_addr
sin_family
AF_INET
sin_port
htons(port)
sin_addr
htonl(INADDR_ANY)
sin_zero[8]
pad with 0’s
240-322 Cli/Serv.: sockets 1/7
continued
11
Notes
 The
port number must be > 1023 if the
server is user-defined.

INADDR_ANY means that the socket will
work with all the network interfaces (i.e.
network cards) on the machine.
– nothing to do with client addresses
240-322 Cli/Serv.: sockets 1/7
12
tcp_serv_sock()
int tcp_serv_sock(int port)
{ int sockfd;
struct sockaddr_in serv_addr;
/* create a TCP socket */
if ((sockfd = socket(AF_INET,
SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "Could not
open a socket");
exit(1);
}
:
240-322 Cli/Serv.: sockets 1/7
continued
13
/* initialise socket address data struct */
memset(&serv_addr, 0, sizeof(serv_addr));
/* bzero((char *)&serv_addr,
sizeof(serv_addr)); */
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr =
htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
:
240-322 Cli/Serv.: sockets 1/7
continued
14
/* bind socket to address */
if (bind(sockfd,
(struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) {
fprintf(stderr, "Could not
bind socket to address\n");
exit(1);
}
/* Listen for incoming connections */
if (listen(sockfd, 5) < 0) {
fprintf(stderr, "listen error\n");
exit(1);
}
return sockfd;
}
240-322 Cli/Serv.: sockets 1/7
15
3.1.1. socket()
 Create
a socket:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int family, int type,
int protocol);
 Return
240-322 Cli/Serv.: sockets 1/7
a socket descriptor if ok, -1 on error.
continued
16

family is one of:
– AF_INET, AF_UNIX, and others

type is one of:
– SOCK_STREAM, SOCK_DGRAM, and others
 If protocol
is 0 it means:
– “let the system choose the protocol based on the
supplied family and type information”
240-322 Cli/Serv.: sockets 1/7
17
3.1.2. bind()
 Bind
an address to a socket:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,
struct sockaddr *myaddr,
int addrlen);
 Returns
0 if ok, -1 on error.
 For more info. use “man 2 bind”
240-322 Cli/Serv.: sockets 1/7
18
3.1.3. listen()
 Listen
for incoming connections:
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
 Returns
240-322 Cli/Serv.: sockets 1/7
0 if ok, -1 on error.
continued
19

backlog is how many connection requests
from clients can be queued by the system
while it waits for the server to execute an
accept() call.
240-322 Cli/Serv.: sockets 1/7
20
4. An Iterative Server
 An
iterative server which uses a TCP
connection can now be outlined:
– it sets up its socket using tcp_serv_sock()
– it uses accept() to accept a connection
– it calls the user-defined do_something() to
do the server task
– when do_something() is finished, it can
accept another connection
240-322 Cli/Serv.: sockets 1/7
21
Outline Code
#define PORT 1666
int main()
{ int rdsock, cdsock;
:
rdsock = tcp_serv_sock(PORT);
while(1) {
cdsock = accept(rdsock, ...);
if (cdsock < 0)
fprintf(stderr, “accept failed\n”);
else {
do_something(cdsock, ...);
close(cdsock);
}
}
return 0;
}
240-322 Cli/Serv.: sockets 1/7
22
4.0.1. accept()
 Accept
a client connection:
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd,
struct sockaddr *peer,
int *addrlen);
 Returns
240-322 Cli/Serv.: sockets 1/7
a socket descriptor if ok, -1 on error.
continued
23

accept() removes the first connection
request from the listen queue attached to
rdsock.

accept() returns a new socket (cdsock) for
1-1 communication between the server and
client.

rdsock = rendezvous socket descriptor
cdsock = connection socket descriptor
240-322 Cli/Serv.: sockets 1/7
continued
24

peer is usually a pointer to a sockaddr_in
struct (cast to sockaddr *)
– peer is assigned details about the accepted
client
 The
value in addrlen changes:
– its input value is the size of the sockaddr_in
struct
– its output value is the actual size of the peer
details
240-322 Cli/Serv.: sockets 1/7
25
Code Fragment
struct sockaddr_in client;
int client_len;
:
client_len = sizeof(client);
cdsock = accept(rdsock,
(struct sockaddr *)&client,
&client_len);
:
240-322 Cli/Serv.: sockets 1/7
26
4.0.2. do_something()
 The
server-specific functionality:
– e.g. “hangman” game code
will use read() and write() to
communicate with the client through
cdsock.
 It
 The
client and server must agree on the
order of their read() and write()’s.
240-322 Cli/Serv.: sockets 1/7
continued
27
communication possible with read()
and write()’s is a stream of characters
(bytes):
 The
– the client and server must agree on how a
‘message’ is represented by a sequence of
characters (bytes)
– e.g. use a ‘$’ or ‘\n’ to end a ‘message’
240-322 Cli/Serv.: sockets 1/7
28
4.1. Example: A Daytime Server
 The
daytime server returns the current time
on the server when requested by a client.
 It
runs on takasila, listening at port 1666
– 1666 is not used in /etc/services
 It
expects no input from the client. It outputs
the time and then closes the connection.
240-322 Cli/Serv.: sockets 1/7
29
day_serv.c
#include <stdio.h>
#include <string.h>
/* #include <bstring.h> */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
/* time funcs */
#include <unistd.h>
#define PORT 1666
int tcp_serv_sock(int port);
:
240-322 Cli/Serv.: sockets 1/7
continued
30
int main()
{ int rdsock, cdsock, client_len;
struct sockaddr_in client;
time_t now;
char *now_str;
rdsock = tcp_serv_sock(PORT);
while(1) {
client_len = sizeof(client);
cdsock = accept(rdsock,
(struct sockaddr *)&client,
&client_len);
:
240-322 Cli/Serv.: sockets 1/7
continued
31
if (cdsock < 0)
fprintf(stderr, “accept failed\n”);
else {
time(&now);
now_str = ctime(&now);
write(cdsock, now_str,
strlen(now_str));
close(cdsock);
}
application specific
}
return 0;
}
240-322 Cli/Serv.: sockets 1/7
32
Running day_serv.c
Sun OS also requires:
-lnsl -lsocket
on takasila
$ gcc -Wall -o day_serv day_serv.c
$ ./day_serv &
[1] 1701
$ netstat -a | grep 1666
$ ...
$ kill -9 1701
$ netstat -a | grep 1666
/* port will not be released immediately */
240-322 Cli/Serv.: sockets 1/7
33
Using day_serv
 Use telnet
to open a connection:
I did this
from fivedots
$ telnet takasila 1666
Trying 172.30.0.82...
Connected to takasila.coe.psu.ac.th.
Escape character is '^]'.
Wed May 4 11:15:34 2005
Connection closed by foreign host.
$
240-322 Cli/Serv.: sockets 1/7
34
4.2. Example: An Echo Server

The echo server reads the lines sent by the client,
and echoes them back, converted into upper case.

A line ends with ‘\n’, and no line is longer
than 128 characters.

An empty line terminates the connection.

The server runs on takasila, listening at port 1667.
240-322 Cli/Serv.: sockets 1/7
35
echo_serv.c
#include
#include
#include
#include
#include
#include
#include
<stdio.h>
<string.h>
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<unistd.h>
<ctype.h>
/* for toupper() */
#define PORT 1667
#define BUFSIZE 128
int tcp_serv_sock(int port);
:
240-322 Cli/Serv.: sockets 1/7
continued
36
int main()
{
int rdsock, cdsock, client_len;
struct sockaddr_in client;
rdsock = tcp_serv_sock(PORT);
while(1) {
client_len = sizeof(client);
cdsock = accept(rdsock,
(struct sockaddr *)&client,
&client_len);
:
240-322 Cli/Serv.: sockets 1/7
continued
37
if (cdsock < 0)
fprintf(stderr, “accept failed\n”);
else {
echo_upper(cdsock);
close(cdsock);
}
application specific part
}
return 0;
}
240-322 Cli/Serv.: sockets 1/7
continued
38
void echo_upper(int sd)
{
char buf[BUFSIZE];
int n;
while (((n = read(sd, buf, sizeof(buf)) != 0) &&
(!end_input(buf, n)))
if (n < 0)
fprintf(stderr, “echo read error\n”);
else {
/*
buf[n] = ‘\0’;
printf(“n: %d, buf: \”%s\”\n”, n, buf);
*/
touppers(buf);
if (write(sd, buf, n) < 0)
fprintf(stderr, “echo write error\n”);
}
}
continued
240-322 Cli/Serv.: sockets 1/7
39
void touppers(char buf[])
{
int i = 0;
while (buf[i] != ‘\n’) {
buf[i] = toupper(buf[i]);
i++;
}
}
240-322 Cli/Serv.: sockets 1/7
40
int end_input(char buf[], int len)
/* Check if buf[] contains the
characters meaning end-of-input
for this server. Return 1 if true.
*/
{
if ((len == 2) &&
(buf[0] == ‘\015’) && (buf[1] == ‘\n’))
return 1;
if ((len == 1) && (buf[0] == ‘\n’))
return 1;
return 0;
}
240-322 Cli/Serv.: sockets 1/7
41
Three Input Termination Cases
 The
client socket can close due to an error:
– read() returns < 0
 The
client socket can be closed by the client:
– read() returns 0
 The
client can send a newline only:
– test with end_input()
– check for telnet output (and other programs)
which includes a carriage return (‘\015’) before
the newline.
240-322 Cli/Serv.: sockets 1/7
42
Running echo_serv.c
on takasila
$ gcc -Wall -o echo_serv echo_serv.c
$ ./echo_serv &
[1] 1944
$ netstat -a | grep 1667
$ ...
$ kill -9 1944
$ netstat -a | grep 1667
/* port will not be released immediately */
240-322 Cli/Serv.: sockets 1/7
43
Using echo_serv
 Use telnet
to open a connection:
$ telnet takasila 1667
on fivedots
Trying 172.30.0.82...
Connected to takasila.coe.psu.ac.th.
Escape character is '^]'.
hello
HELLO
I typed a
goodbye
newline.
GOODBYE
Connection closed by foreign host.
$
240-322 Cli/Serv.: sockets 1/7
44
4.3. Problems with read() / write()
 Over
a network, read() / write() may not be
able to read / write all the requested chars
– due to bandwidth restrictions
– due to network load
e.g.
240-322 Cli/Serv.: sockets 1/7
n1 = read(sd, buf, 1023);
n1 assigned 1000
continued
45
call read() / write() again until the
requested number of characters have been
obtained.
 Must
 Sometimes
reading in the server should stop
when a special character is read (e.g. a ‘\n’).
240-322 Cli/Serv.: sockets 1/7
continued
46
 One
problem:
– the code cannot rely on a ‘termination’
character (e.g. ‘\n’) being the last character
 another
separate message may follow it in the
socket queue
– reading N characters can read all of the first
message and some of the next one
sd
queue
a b c \n a b c d e \n
read(sd, buf, 5);
240-322 Cli/Serv.: sockets 1/7
buf is assigned "a b c \n a"
continued
47
 The
problems continues:
– a message may be split into smaller packets at
the IP level
– e.g. the message sent as "abcde\n" arrives as
two packets "abc" and "de\n"
– the read() call only gets the first packet, without
a '\n'
sd
queue
abc
read(sd, buf, 6);
240-322 Cli/Serv.: sockets 1/7
d e \n
buf is assigned "a b c"
48
4.3.1. readline()
 User-defined
function that reads a stream of
characters upto, and including, a ‘\n’.
 It
adds a ‘\0’ like fgets(), so that string
functions can use the line directly.

readline() uses read() to read one
character (byte) at a time
– very slow
240-322 Cli/Serv.: sockets 1/7
49
int readline(int sd, char *buf, int maxlen)
{ int n, rch;
char ch;
for (n=1; n < maxlen; n++) {
if ((rch = read(sd, &ch, 1)) == 1) {
*buf = ch;
buf++;
if (ch == ‘\n’)
break;
} else if (rch == 0) {
if (n == 1)
return 0;
/* EOF, no data read */
else
break;
/* EOF, some data read */
} else
return -1;
/* error */
}
*buf = ‘\0’;
return n;
}
240-322 Cli/Serv.: sockets 1/7
50
4.3.2. writeN()
 User-defined
function that is guaranteed to
write N bytes from a character array.
repeatedly calls write(), and looks at
the number returned to determine if
write() needs to be called again.
 It
240-322 Cli/Serv.: sockets 1/7
51
int writeN(int sd, char *buf, int nbytes)
{ int nleft, nwritten;
nleft = nbytes;
while (nleft > 0) {
nwritten = write(sd, buf, nleft);
if (nwritten <= 0)
return nwritten;
/* error */
nleft -= nwritten;
buf += nwritten;
}
return (nbytes - nleft);
}
240-322 Cli/Serv.: sockets 1/7
52
4.3.3. Recoding echo_upper()
void echo_upper(int sd)
{
char buf[BUFSIZE];
int n;
while (((n = readline(sd, buf, sizeof(buf)) != 0) &&
(!end_input(buf, n)))
if (n < 0)
fprintf(stderr, “echo read error\n”);
else {
touppers(buf);
if (writeN(sd, buf, n) < 0)
fprintf(stderr, “echo write error\n”);
}
}
240-322 Cli/Serv.: sockets 1/7
53
4.4. Example: Hangman Game
 The
server lets the client try to guess a word
– the word is sent, replaced by ‘-’s
– the client sends a letter that may be in the word
– the server updates the guess word, and resends it
 After
12 incorrect guesses, the client is
‘hanged’ (loses).
 The
server runs on takasila at port 1668.
240-322 Cli/Serv.: sockets 1/7
54
hang_serv.c
#include
#include
#include
#include
#include
#include
#include
#include
#define
#define
#define
#define
240-322 Cli/Serv.: sockets 1/7
<stdio.h>
<string.h>
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<unistd.h>
<stdlib.h>
/* for random functions */
<time.h>
PORT 1668
BUFSIZE 128
MAXLIVES 12
NUMWORDS 10
:
/* max guesses */
/* no. of words */
continued
55
enum game_states
{INCOMPLETE, WON, LOST};
char *word[] = {
“aardvark”, “abacus”, “albatross”,
..., “parallelism”, “zoology” };
int tcp_serv_sock(int port);
int readline(int sd, char *buf,
int maxlen);
int writeN(int sd, char *buf,int nbytes);
int end_input(char buf[], int len);
void play_hangman(int sd);
:
240-322 Cli/Serv.: sockets 1/7
continued
56
int main()
{
int rdsock, cdsock, client_len;
struct sockaddr_in client;
rdsock = tcp_serv_sock(PORT);
srand(time(NULL)); /* seed random */
while(1) {
client_len = sizeof(client);
cdsock = accept(rdsock,
(struct sockaddr *)&client,
&client_len);
:
240-322 Cli/Serv.: sockets 1/7
continued
57
if (cdsock < 0)
fprintf(stderr, “accept failed\n”);
else {
play_hangman(cdsock);
close(cdsock);
}
application specific part
}
return 0;
}
240-322 Cli/Serv.: sockets 1/7
continued
58
void play_hangman(int sd)
{
char *whole_word, part_word[BUFSIZE],
guess[BUFSIZE], outbuf[BUFSIZE];
enum game_states
game_state = INCOMPLETE;
int lives = MAXLIVES;
/* no of lives left */
int i, good_guess, word_length, n;
:
240-322 Cli/Serv.: sockets 1/7
continued
59
/* Pick a word at random from word[] */
whole_word = word[rand()%NUMWORDS];
word_length = strlen(whole_word);
for(i=0; i < word_length; i++)
part_word[i] = '-';
part_word[i] = '\0';
sprintf(outbuf, " %s
%d\n",
part_word, lives);
writeN(sd, outbuf, strlen(outbuf));
:
240-322 Cli/Serv.: sockets 1/7
continued
60
while (game_state == INCOMPLETE) {
/* Get guess letter from player */
if (((n = readline(sd, guess, BUFSIZE)) <= 0) ||
(end_input(buf, n))) {
fprintf(stderr, "readline error\n");
return;
}
good_guess = 0;
for(i=0; i < word_length; i++)
if (guess[0] == whole_word[i]) {
good_guess = 1;
part_word[i] = whole_word[i];
}
:
240-322 Cli/Serv.: sockets 1/7
continued
61
if (!good_guess)
lives--;
if (strcmp(whole_word, part_word) == 0)
game_state = WON;
else if (lives == 0) {
game_state = LOST;
strcpy(part_word, whole_word);
}
sprintf(outbuf, " %s
%d\n",
part_word, lives);
writeN(sd, outbuf, strlen(outbuf));
}
}
240-322 Cli/Serv.: sockets 1/7
62
Usage

$ ./hang_serv &

$ telnet takasila 1668
/* on fivedots */
Trying 172.30.0.82...
Connected to takasila.coe.psu.ac.th.
Escape character is ‘^]’
----------- 12
e
------e---- 12
s
------e--s- 12
:
240-322 Cli/Serv.: sockets 1/7
/* on takasila */
63
5. Clients
Initialise server
address details
dealt with by my
get_addr()
Create socket with socket()
connect
to server
Connect to server’s
port with connect()
dealt with by my
open_tcp_sock()
write()
read()
240-322 Cli/Serv.: sockets 1/7
64
5.1. Building the Server’s Address
in the client
addr
sin_family
AF_INET
sin_port
htons(host_port)
sin_addr
gethostbyname(host)->h_addr
sin_zero[8]
pad with 0’s
The server is also known as the host.
240-322 Cli/Serv.: sockets 1/7
65
5.1.1. gethostbyname()
 Get
details on the host (the server):
#include <netdb.h>
e.g. fivedots.coe.psu.ac.th
struct hostent *
gethostbyname(char *hostname);
 Return
240-322 Cli/Serv.: sockets 1/7
host details if ok, NULL on error.
66
struct hostent
(in netdb.h)
struct hostent {
char *h_name;
/* official host name
char **h_aliases;
/* alias list
int h_addrtype;
/* host address type
int h_length;
/* address length
char **h_addr_list;
/* array of addresses;
ends with NULL */
};
*/
*/
*/
*/
#define h_addr h_addr_list[0]
/* first address in list */
240-322 Cli/Serv.: sockets 1/7
67
Diagram Form
....
NULL
h_addr_list
hp
addr
addr
h_addr
...
hostent struct
240-322 Cli/Serv.: sockets 1/7
68
5.1.2. Code Fragment
struct sockaddr_in addr;
struct hostent *hp;
:
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if ((hp = gethostbyname(host)) == NULL){
fprintf(stderr, “Unknown addr\n”);
exit(1);
}
memcpy(&addr.sin_addr.s_addr, hp->h_addr,
hp->h_length);
240-322 Cli/Serv.: sockets 1/7
69
5.1.3. bstring.h Problem
 Some
UNIXs (but not Linux) use bstring.h:
– memset(&addr, 0, sizeof(addr));
bzero(&addr, sizeof(addr));
replaced by
– memcpy(&addr.sin_addr.s_addr, hp->h_addr,
hp->h_length);
replaced by
bcopy(hp->h_addr, &addr.sin_addr.s_addr,
hp->h_length);
240-322 Cli/Serv.: sockets 1/7
70
5.2. Address Conversion Functions
 Convert
a dotted-decimal address in string
format (e.g. "192.100.77.3") into an
in_addr structure:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
"192.100.77.3"
goes here
int inet_aton(const char *addr,
struct in_addr *iaddr);
 Returns
240-322 Cli/Serv.: sockets 1/7
non-negative if ok, 0 on error.
continued
71
Networked Socket Data Structures Again
struct sockaddr_in {
short sin_family;
/* always AF_INET */
u_short sin_port;
/* port no. */
struct in_addr sin_addr; /* IP address */
char sin_zero[8];
/* padding of O’s */
};
struct in_addr {
u_long s_addr;
};
– Details in /usr/include/netinet/in.h
240-322 Cli/Serv.: sockets 1/7
72
Using inet_aton()
struct sockaddr_in foo;
struct in_addr iaddr;
:
if (inet_aton("192.100.77.3", &iaddr)
!= 0)
memcpy(&foo.sin_addr, &iaddr,
sizeof(iaddr));
:
240-322 Cli/Serv.: sockets 1/7
73
inet_addr()

inet_aton() is not supported on some
UNIXes (e.g. SunOS), so you must use:
#include <sys/socket.h>
"192.100.77.3"
#include <netinet/in.h>
goes here
#include <arpa/inet.h>
unsigned long
inet_addr(const char *addr);
– Returns -1 (INADDR_NONE) on error which
can be confused with the long integer for
address 255.255.255.255
240-322 Cli/Serv.: sockets 1/7
74
Using inet_addr()
struct sockaddr_in foo;
unsigned long lgaddr;
:
if ((lgaddr = inet_addr("192.100.77.3"))
!= -1)
foo.sin_addr.s_addr = lgaddr;
:
240-322 Cli/Serv.: sockets 1/7
75
inet_ntoa()
 Convert in_addr
to a dotted-decimal
address (in string format):
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr inaddr);
 Returns
error.
240-322 Cli/Serv.: sockets 1/7
dotted-decimal string if ok, NULL on
76
5.2.1. get_addr()
 My
function for reading a host string (in
dotted-decimal or dotted-name format) and
a port number from the command line.
 If
no port number is supplied, the function
uses the value in #define PORT

get_addr() returns a sockaddr_in struct
containing the address details.
240-322 Cli/Serv.: sockets 1/7
77
Usage
the program
using get_addr()




$
$
$
$
foobar
foobar
foobar
foobar
240-322 Cli/Serv.: sockets 1/7
processed by get_addr()
dotted-name or
dotted-decimal
optional port
number
takasila.coe.psu.ac.th 1667
takasila
takasila 1667
172.30.0.82
78
In the foobar.c code
int main(int argc, char *argv[])
{
:
int sd; // socket descriptor
struct sockaddr_in netaddr;
netaddr = get_addr(argc, argv);
sd = open_tcp_sock(netaddr);
:
240-322 Cli/Serv.: sockets 1/7
79
struct sockaddr_in get_addr(int argc,
char *argv[])
{
struct sockaddr_in addr;
unsigned long lgaddr;
struct hostent *hp;
int port;
if ((argc < 2) || (argc > 3)) {
fprintf(stderr,
"Expect address [port]\n");
exit(1);
}
:
240-322 Cli/Serv.: sockets 1/7
continued
80
if (argc == 2)
port = PORT;
/* default value */
else {
port = atoi(argv[2]);
if (port < 1024) {
fprintf(stderr, "The port number
cannot be < 1024\n");
exit(1);
}
}
:
240-322 Cli/Serv.: sockets 1/7
continued
81
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if ((lgaddr = inet_addr(argv[1])) != -1)
/* dotted-decimal */
addr.sin_addr.s_addr = lgaddr;
else if ((hp = gethostbyname(argv[1])) != NULL)
/* lookup name */
memcpy(&addr.sin_addr.s_addr, hp->h_addr,
hp->h_length);
else {
fprintf(stderr, "Unknown address\n");
exit(1);
}
return addr;
}
240-322 Cli/Serv.: sockets 1/7
82
5.3. open_tcp_sock()
 My
function that carries out socket creation
and connection in a client.
 It
assumes that the TCP protocol is being
used.
240-322 Cli/Serv.: sockets 1/7
83
Code
int open_tcp_sock(struct sockaddr_in netaddr)
{ int sd;
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "socket() error\n");
exit(1);
}
if (connect(sd, (struct sockaddr *)&netaddr,
sizeof(netaddr)) < 0) {
fprintf(stderr, "Can not connect\n");
exit(1);
}
return sd;
}
240-322 Cli/Serv.: sockets 1/7
84
connect()
 Connect
the socket to the server at the
specified address:
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sd,
struct sockaddr *servaddr,
int addrlen);
 Return
240-322 Cli/Serv.: sockets 1/7
0 if ok, -1 on error.
85
5.4. Example: A Daytime Client
#include
#include
#include
#include
#include
#include
#include
#include
#include
240-322 Cli/Serv.: sockets 1/7
<stdio.h>
<string.h>
<stdlib.h>
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<arpa/inet.h>
<netdb.h>
<unistd.h>
:
continued
86
#define PORT 1666
#define LINELEN 128
struct sockaddr_in get_addr(int argc,
char *argv[]);
int open_tcp_sock(struct sockaddr_in
netaddr);
int readline(int sd, char *buf,
int maxlen);
:
240-322 Cli/Serv.: sockets 1/7
continued
87
int main(int argc, char *argv[])
{ struct sockaddr_in netaddr;
int sd, buflen;
char buf[LINELEN];
netaddr = get_addr(argc, argv);
sd = open_tcp_sock(netaddr);
while ((buflen = readline(sd,
buf, LINELEN)) != 0)
if (buflen < 0) {
fprintf(stderr, “read failed\n”);
exit(1);
}
else
fputs(buf, stdout);
close(sd);
return 0;
}
240-322 Cli/Serv.: sockets 1/7
88
Using day_cli

$ gcc -Wall -o day_cli day_cli.c
$ ./day_cli takasila
Wed May 4 11:27:40 2005
$ ./day_cli takasila
Wed May 4 11:27:42 2005

executed on
fivedots
must be running at port 1666 on
takasila.
day_serv
240-322 Cli/Serv.: sockets 1/7
89
5.5. Example: A Hangman Client
#include <stdio.h>
#include <string.h>
:
/* same as for day_cli.c */
#include <unistd.h>
#define PORT 1668
#define LINELEN 128
struct sockaddr_in get_addr(int argc,
char *argv[]);
int open_tcp_sock(struct sockaddr_in netaddr);
int readline(int sd, char *buf, int maxlen);
int writeN(int sd, char *buf, int nbytes);
:
240-322 Cli/Serv.: sockets 1/7
continued
90
int main(int argc, char *argv[])
{
struct sockaddr_in netaddr;
int sd;
char buf[LINELEN];
netaddr = get_addr(argc, argv);
sd = open_tcp_sock(netaddr);
while (readline(sd, buf, LINELEN) > 0) {
fputs(buf, stdout);
fgets(buf, LINELEN, stdin);
writeN(sd, buf, strlen(buf));
}
return 0;
//
//
//
//
1
2
3
4
}
240-322 Cli/Serv.: sockets 1/7
91
Interaction Diagram
3) Possible
letter: 'e'
hang_cli
2) ------
on fivedots
4) 'e'
hang_serv
1) Guess the
word: -------
on takasila
network
240-322 Cli/Serv.: sockets 1/7
92
Using hang_cli

$ gcc -Wall -o hang_cli hang_cli.c
$ ./hang_cli
----------e
------e---s
------e--s:

takasila
12
executed on
fivedots
12
12
hang_serv must be running at port 1668 on
takasila.
240-322 Cli/Serv.: sockets 1/7
93
6. Clients of System Services
 System
services include:
– ftp, Web server, finger, telnet
 A list
of possible services are in /etc/services:
:
ftp
21/tcp
:
finger
www
www
:
79/tcp
80/tcp
80/udp
240-322 Cli/Serv.: sockets 1/7
94
What services are running?
 Most
system services are started by inetd.
 The
list of the services it starts are in
/etc/inetd.conf:
:
finger stream tcp nowait root
/usr/sbin/tcpd
/usr/sbin/cfingerd
:
240-322 Cli/Serv.: sockets 1/7
takasila does not start finger; calvin does
95
6.1. A System Service Address
can use getservbyname() to obtain
the reserved port number for a given service
name (on the client’s machine).
 We
 This
approach assumes that the service will
use the same port on its own machine.
240-322 Cli/Serv.: sockets 1/7
96
6.1.1. Build the Service Address
addr
sin_family
AF_INET
sin_port
getservbyname(service)->s_port
sin_addr
gethostbyname(host)->h_addr
sin_zero[8]
pad with 0’s
240-322 Cli/Serv.: sockets 1/7
97
6.1.2. getservbyname()
 Lookup
service details for a given service
name:

#include <netdb.h>
struct servent *
getservbyname(char *servname,
char *protname);
e.g. getservbyname("finger","tcp");
 Return
240-322 Cli/Serv.: sockets 1/7
service details if ok, NULL on error.
98
6.1.3. struct servent
struct servent {
char *s_name;
/* service name
char **s_aliases;
/* alias list
int s_port;
/* port number, in
network byte order
char *s_proto;
/* protocol to use
}
240-322 Cli/Serv.: sockets 1/7
*/
*/
*/
*/
99
6.2. get_tcp_serv_addr()
 My
function which uses the host name (in
dotted-decimal or dotted-name string form)
and service name to build a sockaddr_in
struct for a service.
 It
assumes that the service uses the TCP
protocol.
240-322 Cli/Serv.: sockets 1/7
continued 100
Usage
program using
dotted-name or
get_tcp_serv_addr() dotted-decimal



$ foobar takasila.coe.psu.ac.th
$ foobar takasila
$ foobar 172.30.0.82
240-322 Cli/Serv.: sockets 1/7
101
In the foobar.c code
int main(int argc, char *argv[])
{
:
int sd; // socket descriptor
struct sockaddr_in netaddr;
dotted name or
dotted decimal
netaddr = get_tcp_serv_addr(argv[1],
"service name");
sd = open_tcp_sock(netaddr);
:
240-322 Cli/Serv.: sockets 1/7
102
struct sockaddr_in
get_tcp_serv_addr(char *host,
char *service)
{ struct sockaddr_in addr;
unsigned long lgaddr;
e.g. "finger"
struct hostent *hp;
struct servent *sp;
if ((sp = getservbyname(service,
“tcp”)) == NULL) {
fprintf(stderr, "Unknown service\n”);
exit(1);
}
:
240-322 Cli/Serv.: sockets 1/7
continued 103
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = sp->s_port;
if ((lgaddr = inet_addr(host)) != -1)
/* dotted-decimal */
addr.sin_addr.s_addr = lgaddr;
else if ((hp = gethostbyname(host)) != NULL)
/* lookup name */
memcpy(&addr.sin_addr.s_addr, hp->h_addr,
hp->h_length);
else {
fprintf(stderr, "Unknown address\n");
exit(1);
}
return addr;
}
240-322 Cli/Serv.: sockets 1/7
104
6.3. Example: finger using telnet
 Using
the finger service:
executed on
fivedots
$ telnet calvin 79
Trying 172.30.161.2...
Connected to calvin.coe.psu.ac.th.
Escape character is '^]'.
you must type at
ad
least a return
/* many lines of finger
information sent back */
Connection closed by foreign host.
$
240-322 Cli/Serv.: sockets 1/7
105
A finger Client

finger_cli.c reads the hostname and user
login ID from the command line, and then
queries “finger” on that host for that login
ID.
240-322 Cli/Serv.: sockets 1/7
106
finger_cli.c
#include <stdio.h>
#include <string.h>
:
/* same as for day_cli.c */
#include <unistd.h>
#define LINELEN 128
struct sockaddr_in
get_tcp_serv_addr(char *host, char *service);
int open_tcp_sock(struct sockaddr_in netaddr);
int readline(int sd, char *buf, int maxlen);
int writeN(int sd, char *buf, int nbytes);
:
240-322 Cli/Serv.: sockets 1/7
continued 107
int main(int argc, char *argv[])
{
struct sockaddr_in netaddr;
int sd;
hostname
char buf[LINELEN];
netaddr = get_tcp_serv_addr(argv[1],
“finger”);
sd = open_tcp_sock(netaddr);
sprintf(buf, “%s\n”, argv[2]);
writeN(sd, buf, strlen(buf));
login ID
// 1
while (readline(sd, buf, LINELEN) > 0)
fputs(buf, stdout);
return 0;
// 2
// 3
}
240-322 Cli/Serv.: sockets 1/7
108
Interaction Diagram
1) "ad"
finger_cli
3) lines
to screen
on fivedots
2) lines of
output
finger
daemon
on calvin
network
240-322 Cli/Serv.: sockets 1/7
109
Using finger_cli
$ gcc -Wall -o finger_cli finger_cli.c
$ finger_cli calvin ad
/* many lines of finger
information sent back */
$
240-322 Cli/Serv.: sockets 1/7
executed on
fivedots
110
7. More Details
 UNIX
Network Programming, Vol. 1
W. Richard Stevens
Prentice Hall, 1998 (2nd ed.)
– tough, but the introductory chapters (1-9)
are fairly ‘easy’.
 Brown,
240-322 Cli/Serv.: sockets 1/7
chapter 4, is easier.
111