socket2_modified
socket2_modified
Documents to read:
1. UNIX Network Programming
By W. Richard Stevens
Chapter-6, on Berkeley Sockets
Square brackets are used to separate keywords, such as filenames, programs names etc etc from lines of
text. They have no other meaning.
A few internal servers (echo, daytime, time etc etc) run on a host, using TCP and UDP protocols. In
this experiment, the two internal [echo] servers (using TCP and UDP) will be used, to test the TCP and
UDP [echo] client programs.
Port number seven is assigned to [echo] servers. The following command and part of the possible
output is shown...
T-2 $ cat /etc/services | grep echo
echo 7/tcp
echo 7/udp
However the [echo] servers can be disabled by commenting appropriate lines in file [/etc/inetd.conf].
If the two [echo] servers (using TCP and UDP) are not enabled in your host, they are to be enabled by
editing file [ /etc/inetd.conf ]and sending SIGHUP to [inetd] process.
$ telnet 127.0.0.1 7
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Are you the echo server?
Are you the echo server?
The UDP [echo] server cannot be checked as yet from the command line.
SERVER
| CLIENT
socket(), bind(), listen(), accept() |
| socket()
| connection establishment |
|<-----------------------------------> connect()
| |
read() <-------------------------------- write()
| data (request from client) |
| |
process client's request |
| |
write()-----------------------------------> read()
data (reply from server)
Figure-1
The program [f1.c] is the TCP [echo] client. This program has to call [socket] and [connect] calls to
establish connection with a server. Then the client sends request to server and reads reply from server.
In [ f1.c ] the IP address of the [ echo ] server and TCP port of the [echo] server are supplied as
arguments to the program. We know that assigned number of [echo] server is 7. Still we are using port
number as an argument to the client program. This would enable us to connect to experimental [echo]
servers using any free port number.
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
//#define CHECK
if( argc != 3 )
{
printf("Usage: %s server-address server-port \n", argv[0] );
printf("Example: %s 192.168.5.148 12345 \n", argv[0] );
exit(1);
}
/*
The descriptor [sd] is not useful now. This is to be connected to server's address with [connect() ]
system call. Before making this call, the members of structure [ server_addr ] are to be initialized.
struct sockaddr_in {
sa_family_t sin_family; // address family: AF_INET
u_int16_t sin_port; // port in network byte order
struct in_addr sin_addr; // Internet address
};
Internet address:
struct in_addr {
u_int32_t s_addr; // address in network byte order
};
You may read manual [ ip(7) ].
The variable [server_addr], used by you, is an instance of the structure [ struct sockaddr_in ]
*/
// First member of structure [ server_addr ] is initialized with the address family consistent with the
//protocol family of the socket.
server_addr.sin_family = AF_INET;
/*
The port number is to be converted from host byte order to network byte order. Network byte order is
big endian, whereas host byte order may be little endian or big endian. In your PC ( i80x86 ) host byte
order is little endian. The function [ htons() ] is used to convert port number from host byte order to
network byte order. Even if the host byte order is big endian, calling [htons()] is not a mistake. You
may read manual [ htons(3) ].
argv[2] is the port number in our program.
*/
/*
Internet host address given as standard numbers-and-dots notation is to be converted into binary
data and is to be stored as the [ server_addr.sin_addr ] of [ server_addr ] structure. The function
[inet_aton()] is used to do that. This function returns nonzero if the address is valid, zero if not. You
may read manual [ inet_aton(3) ].
*/
#ifdef CHECK
printf("Check with [ # netstat -tn ] in another terminal\n" );
printf("Check TCP connection establishment\n" );
while(1) { sleep(1); } // endless loop
#endif
/*
[bind()] system call was not called on [sd] before [ connect() ] system call. As [connect()] was called
on a unbound socket, [sd] was automatically bound to a random free port ( ephemeral port ) with the
local address set to [INADDR_ANY].
*/
return 0;
}
----------------------------------------------------------------------
Assignment-1:
Run the program using wrong number of arguments to see usage message
$ ./one-a
$ ./one-a 192.168.5.251
Run the program correctly. Replace 192.168.5.251 with a suitable server address
$ ./one-a 192.168.5.251 7
From another terminal use [ # netstat -tnp ] command. From the output of the command, record the
line related to your program
Try to connect to a non-existent TCP server in localhost or in another host. The [ connect ] call of your
program should fail with a suitable error message.
Try to connect to TCP [echo] servers running on different hosts in the laboratory or outside the
laboratory. Your TCP [echo] client program should work if TCP [echo] servers are enabled in the
specified hosts.
$ ./one-b 127.0.0.1 7
$ ./one-b 192.168.5.251 7
$ ./one-b 192.168.5.252 7
$ ./one-b 192.168.5.160 7
$ ./one-b 172.16.1.100 7
$ ./one-b 210.212.4.3 7
$ PATH=$PATH:$HOME/bin/
If the inclusion is correct, you should be able to run the TCP client from any directory of your account.
If you intend to include the [bin] directory in PATH variable, on every login, include the following line
at the end of file [.bash_profile], in your home directory.
#-- .bash_profile file --
.
.
.
PATH=$PATH:$HOME/bin/
#---------------------------
The executable [tcp-echo-client] should not be deleted. This would be used to connect to TCP [echo]
server, to be written by you.
End of Assignment-1.
The next program [f2.c] is a TCP [echo] client which connects to port-7 of standard TCP [echo]
servers. [f1.c] is modified as [f2.c]. The port number is no longer supplied as an argument.
------------------------------------------------------------------
//file-name f2.c TCP echo client
// usage -> program-name server-address
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
if( argc != 2 )
{
printf("Usage: %s server-address \n", argv[0] );
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons( PORT );
n = inet_aton( argv[1], &(server_addr.sin_addr) );
if( n == 0 ) { printf("inet_aton-Invalid address\n"); exit(1); }
return 0;
}
------------------------------------------------------------------
Assignment-2:
Compile [f2.c]
$ gcc -Wall ./f2.c -o ./two
End of Assignment-2.
SERVER
|
socket()
|
bind()
| CLIENT
| |
recvfrom() socket()
| |
Blocks until data is bind()
received from a client |
| Request |
|---------------------<------------------------ sendto()
| |
process request |
| |
sendto() --------------->------------------- recvfrom()
Reply
Figure-2
[f3.c] is the UDP client program. From the above figure it is observed that [ f3.c ] has to call
[socket()], [ bind() ], [ sendto() ] and [ recvfrom() ] calls.
In connection-less communication, data are put into packets and each packet is an independent
communication. UDP packets may arrive at the destination in wrong order or not at all. If the packet is
lost or there is no process to accept the data at the destination address, usually the sending end system
does not get an indication.
Sending datagrams:
[sendto()] call:
Number of bytes specified by [ buffer-size ] in [ buffer ] are transmitted through the socket, specified
by [socket-fd], to the destination address specified by [dest-address] having a length [dest-address-
length].
MSG_OOB
MSG_DONTROUTE
The flag argument may be zero if none of the above are used. For the correct data types of the
arguments, read manual [sendto(2)].
Receiving datagrams:
Normally [recvfrom()] call is used to receive one datagram from a socket. This system call also stores
the address from which the datagram came.
[recvfrom()] call:
This call reads one datagram from the socket specified by[ socket-fd ], in the buffer specified by [
buffer ]. The maximum number of bytes to be read are specified by [buffer-size ].
The [src-address] and [ src-address-length ] specify the address, the datagram came from.
For full description and data types of the arguments, read the manual of [recvfrom(2)].
A null pointer can be used as [ src-address ] and zero as [ src-address-length ] if this information is
not needed.[recv()] call may be used in such situation.
In [f3.c], server address and server port number, are supplied as two arguments to the program.
----------------------------------------------------------------------
// file-name f3.c UDP echo client
// usage -> program-name server-address server-port
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
// Uncomment the following line to print numeric values of some symbolic constants
//#define DEBUG
#ifdef RECVFROM_FULL
// following variables are used to illustrate use of [ recvfrom ] call in full form
socklen_t source_addr_length;
unsigned long nbo; // host address in network byte order
unsigned long hbo; // host address in network byte order
char *host_addr;
#endif
if( argc != 3 )
{
printf("usage -> prog-name server-address server-port \n");
printf("example -> %s 172.16.2.15 7 \n", argv[0] );
exit(1);
}
/* [recvfrom()] system call has filled in members of the variable [source_addr]. The values of the
members are now retrieved. */
nbo = source_addr.sin_addr.s_addr;
printf("rf-full: source address in network byte order ");
printf("= %lX Hex \n", nbo );
hbo = ntohl(source_addr.sin_addr.s_addr);
printf("rf-full: source address in host byte order = %lX Hex \n", hbo );
host_addr = inet_ntoa(source_addr.sin_addr);
#ifdef RECVFROM_STRIPPED
printf("u-e-c: Using recvfrom() call in stripped form \n");
//We are not interested in source address
n = recvfrom( sd, buffer,SIZE, 0, NULL, 0 );
if( n == -1 ) { perror("recvfrom-call-stripped"); exit(1); }
#endif
#ifdef RECV
printf("u-e-c: Using recv() call \n");
// We are not interested in source address but want to use [flag]
n = recv( sd, buffer, SIZE, MSG_PEEK );
if( n == -1 ) { perror("recvfrom-call-stripped"); exit(1); }
#endif
#ifdef READ
printf("u-e-c: Using read() call \n");
// We are not interested in source address and don't want to use [flag]
n = read( sd, buffer, SIZE);
if( n == -1 ) { perror("read-call"); exit(1); }
#endif
return 0;
}
----------------------------------------------------------------------
Assignment-3:
End of Assignment-3.