Professional Documents
Culture Documents
Name: __________________________________________________________
Documents to read:
1. UNIX Network Programming
By W. Richard Stevens
Chapter-6, on Berkeley Sockets
In this experiment you have to write TCP [ echo ] client programs and
UDP [ echo ] client programs. A few internal servers ( echo, daytime,
time etc etc ) run on a host, using TCP and UDP protocols. Internal
TCP [ echo ] server and UDP [ echo ] server will be used to test the
[ echo ] client programs written by you.
If ports 7 ( using TCP and UDP ) are not "open" in your host, they are
to be enabled by editing file [/etc/inetd.conf] and sending SIGHUP to
[inetd] process.
Edit [/etc/inetd.conf] if required and make needed changes.
T-5 # nano /etc/inetd.conf
Scan ports 7 ( TCP and UDP ) to confirm that they are "open".
T-5 # nmap -sTU 127.0.0.1 -p 7
T-5 # nmap -sTU IP-of-your-eth0 -p 7
Request your friend to scan TCP and UDP ports 7 of your host. The two
ports should appear to be "open" to your friend, if the two servers of
your host is accepting [ echo ] requests on your eth0 interface.
friend # nmap -sTU IP-of-your-eth0 -p 7
[ netcat ] program may also be used test TCP echo server. Enter any
string to test. Press CTRL-C to exit.
T-2 $ nc -t IP-of-your-eth0 7
Use [ netcat ] to check the UDP [ echo ] server running in your host.
Enter a string to receive an echo. Terminate with CTRL-C.
T-2 $ nc -u IP-of-your-eth0 7
To check the number of packets exchanged for one UDP echo (request and
reply) [ tshark ] is used with option to capture UDP packets.
T-5 # tshark -i eth0 -p udp and host IP-of-your-eth0
Request your friend to test UDP [ echo ] server running on your host.
A string is to be entered, then CTRL-C are to be pressed by your
friend.
friend T-2 $ nc -u IP-of-your-eth0 7
Examine packets captured in T-5. Edited output might be:
Number of packets captured for one UDP echo (request and reply)-->
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. However we will use port number
as an argument to the client program. Such a client program can be
used to connect to experimental [ echo ] servers using a non standard
port.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
if( argc != 3 )
{
printf("Usage: %s server-address server-port \n", argv[0] );
printf("Example: %s 172.16.4.11 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
initialised.
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) ].
Other 8 bytes are not used and are set to zero. The following
statement may be used to verify the size of unused portion.
printf("Unused = %u bytes\n", sizeof(server_addr.sin_zero) );
*/
/*
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) ].
/*
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) ].
*/
/*
[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] ( all interfaces ).
*/
return 0;
}
//--------------------------------------------------------------------
Assignment-1:
Compile....
T-2 $ gcc -Wall ./f1.c -o ./one
Run the program using wrong number of arguments, to see usage message.
T-2 $ ./one
T-2 $ ./one 172.16.4.11
Run the program to connect to the [ TCP echo ] server of your host,
listening on loopback interface.
T-2 $ ./one 127.0.0.1 7
Run the program to connect to the [ TCP echo ] server of your host,
listening on eth0 interface.
T-2 $ ./one IP-of-your-eth0 7
If the [ PATH ] is set correctly, you should be able to run the client
program from any directory of your account.
Packets exchanged between the standard [ TCP echo ] server and the
[ TCP echo ] client program written by you, are to be examined.
Terminate [tshark]
T-5 CTRL-C
End of Assignment-1.
SERVER
|
socket()
|
bind() CLIENT
| |
recvfrom() socket()
| |
Blocks until data is bind()
received from a client |
| Request |
recvfrom()----------------<-------------------- sendto()
| |
process request |
| |
sendto() --------------->------------------- recvfrom()
Reply
Figure-2
[f2.c] is the UDP client program. From the above figure it is observed
that [ f2.c ] has to call [ socket() ], [ bind() ], [ sendto() ] and
[ recvfrom() ] calls.
Sending datagrams:
Normally [ sendto() ] system call is used to send
data to a datagram socket.
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 may also store the address
from which the datagram came.
[recvfrom()] call:
For full description and data types of the arguments, read the
manual of [recvfrom(2)].
In [f2.c], server address and server port number, are supplied as two
arguments to the program.
//--------------------------------------------------------------------
// file-name f2.c UDP echo client
// usage -> program-name server-address server-port
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#ifdef RECVFROM_FULL
// following variables are used to illustrate use of
// [ recvfrom ] call in full form
struct sockaddr_in remote_addr;
socklen_t remote_addr_length;
unsigned long nbo;
unsigned long hbo;
char *host_addr;
#endif
if( argc != 3 )
{
printf("usage -> prog-name server-address server-port \n");
printf("example -> %s 192.168.5.58 7 \n", argv[0] );
exit(1);
}
// Bind the local address with the socket. This is needed as we are
// interested in a reply from the server. The server needs to know
// our address to send a reply.
n = bind( sd, (struct sockaddr *) &client_addr,
sizeof(client_addr) );
if( n == -1 ) { perror("bind-call"); exit(1); }
n = 0;
// clear the buffer, before receiving from server
memset( buffer, '\0', SIZE );
#ifdef RECVFROM_FULL
printf("%s: Using recvfrom() call in full form \n", argv[0] );
remote_addr_length = sizeof(remote_addr);
n = recvfrom( sd, buffer,SIZE, MSG_PEEK,
(struct sockaddr *) &remote_addr,
&remote_addr_length );
if( n == -1 ) { perror("recvfrom-call-full"); exit(1); }
nbo = remote_addr.sin_addr.s_addr;
printf("rf-full: remote address in network byte order ");
printf("= %lX Hex \n", nbo );
hbo = ntohl(remote_addr.sin_addr.s_addr);
printf("rf-full: remote address in host byte order = %lX Hex \n",
hbo );
host_addr = inet_ntoa(remote_addr.sin_addr);
#ifdef RECVFROM_STRIPPED
printf("%s: Using recvfrom() call in stripped form\n", argv[0]);
// We are not interested to save remote address
n = recvfrom( sd, buffer,SIZE, 0, NULL, 0 );
if( n == -1 ) { perror("recvfrom-call-stripped"); exit(1); }
#endif
#ifdef RECV
printf("%s: Using recv() call \n", argv[0]);
// We are not interested to store remote address but want
// to use [flag] argument
n = recv( sd, buffer, SIZE, MSG_PEEK );
if( n == -1 ) { perror("recvfrom-call-stripped"); exit(1); }
#endif
#ifdef READ
printf("%s: Using read() call \n", argv[0] );
// We are not interested to store remote address and
// don't want to use [flag] argument
n = read( sd, buffer, SIZE);
if( n == -1 ) { perror("read-call"); exit(1); }
#endif
return 0;
}
//--------------------------------------------------------------------
Assignment-2:
2 | 172 Remainder
|-----
2 | 86 ----- 0 <------ LSB
|-----
2 | 43 ----- 0
|-----
2 | 21 ----- 1
|-----
2 | 10 ----- 1
|-----
2 | 5 ----- 0
|-----
2 | 2 ----- 1
|-----
1 ----- 0
End of Assignment-2.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
if( argc != 3 )
{
printf("usage -> %s server-address server-port \n", argv[0] );
exit(1);
}
Assignment 3:
Compile ...
T-2 $ gcc -Wall ./f3.c -o ./udp-echo-client
Sniff [ eth0 ] interface for packets using UDP port 7, to or from your
host. Captured packets are stored in binary file [xx].
T-5 # tshark -i eth0 -n -w ./xx host IP-of-your-eth0 and udp port 7
Stop [[tshark]
T-5 # CTRL-C
End of Assignment 4.
----------------------------
Optional Assignment-1:
return 0;
} // end of main
--------------------------------------------------------------
Then write one line of code and compile. Remove compilation error if
any. In this way complete the program.
Optional Assignment-2:
Write a minimal UDP [echo] client program.
Optional Assignment-3:
UDP [echo] client program may use [connect()]
system call. [read(2) and write(2)] are used on the UDP socket.
//-------------------------------------------------------------------
// file-name f5.c UDP echo client ( using connect() call )
// usage -> program-name server-address server-port
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
if( argc != 3 )
{
printf("usage -> prog-name server-address server-port \n");
printf("example -> %s 192.168.5.58 7 \n", argv[0] );
exit(1);
}
return 0;
} // end of main
//-------------------------------------------------------------------
-------------------------------------
Feedback:
Please point out mistakes and suggest which portions should
be removed, added, expanded etc etc. vu2nil@cse.cemk.ac.in
---------------------- end of file socket2.txt -----------------------