Sie sind auf Seite 1von 19

Chapter - 1

UNP - Introduction

Client

Server
Network application: client & server

Client normally communicates with one server at a time.


But today it is not uncommon that we are connected to multiple servers at the
same time and communicate with them one after the other.
But from a servers perspective, it is not uncommon for a server to be connected to
multiple clients at the same time.

Client

Client

Server

Client
Server handling multiple clients at the same time

User
process

Protocol
stack
within
kernel

Web Client

TCP

IP

Ethernet Driver

Application Protocol

TCP Protocol

IP Protocol

Ethernet Protocol

Web Server

TCP

IP

Ethernet driver

Application
Layer
Transport
Layer

Network Layer

Datalink Layer

Actual flow between client and server

Client and server on the Ethernet communicating using TCP

A Simple Daytime Client


int main (int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE +1];
struct sockaddr_in servaddr; //servaddr will contain address of server
if (argc !=2)
err_quit (usage: a.out <IP Address of the server> );
if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0)
err_sys (socket error);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET; //AF_INET for IPv4
servaddr.sin_port = htons(13);
//daytime server port number
if(inet_pton(AF_INET,argv[1], &servaddr.sin_addr) <= 0)
err_quit(inet_pton error);
if (connect( sockfd, (SA *)servaddr, sizeof(servaddr)) < 0)
err_sys(connect error);
while((n = read(sockfd, recvline, MAXLINE)) > 0)
{ recvline[n] = 0;
//null terminate
if(fputs(recvline, stdout) == 0)
err_sys(fputs error);
}
exit(0);
}

Sockets are a way to talk with other machines on the network using UNIX file
descriptors.
A file descriptor in UNIX is just a number that is associated with an open file and
is used to access it, similarly socket file descriptor lets us access different
machines on the network.
And this file descriptor is provided by the function call socket( ).
Socket has a generic address structure, so that all protocols can understand it.
sockfd is the socket file descriptor.
char recvline[MAXLINE + 1] set to maxline +1 for the null terminator that is
required for char array.
struct sockaddr_in {
uint_8
sin_len;
/* length of structure */
sa_family_t
sin_family;
/* address family is AF_INET for IP */
in_port_t
sin_port;
/* 16-bit port number of UDP/TCP
network byte ordered */
struct in_addr
sin_addr;
/* 32-bit IP address
network byte ordered */
char
sin_zero[8];
/* unused */

};
struct in_addr{
in_addr_t
};

s_addr;

/* 32-bit IP address
network byte ordered */

int socket(int FAMILY, int type, int protocol) .


Returns non-negative descriptor if OK, -1 on error
In #include <sys/socket.h>
If protocol argument is zero, the operating system will choose the most appropriate
protocol. It will choose TCP for stream sockets and UDP for datagram sockets.

void bzero(void *dest, size_t nbytes)


bzero sets the specified number of bytes at the destination to 0.
In #include<string.h>

uint16_t htons(uint16_t host16bitvalue)


Return address in network byte order
Present in #include <netinet/in.h>
Little endian byte ordering: lower order byte is stored first:

Address A

Address A+1

Low order Byte(least sig byte)

High order Byte (most sig byte)

Big endian byte ordering: higher order byte is stored first:


Address A

Address A+1

Higher order byte (Most sig byte)

Low order byte (Least sig byte)

We refer to the byte addressing used by the host as host-byte ordering.


IP uses Big-endian byte ordering as network-byte ordering.

uint_8 - unsigned 8-bit integer <sys/types.h>


sa_family_t - address family of socket address structure <sys/socket.h>
in_port_t - 16 bit port number - unsigned 16 bit integer <netinet/in.h>
in_addr_t 32 bit IP address unsigned 32 bit integer <netinet/in.h>
FAMILY:

AF_INET
IPv4 protocol (AF stands for address family)
AF_INET6
IPv6 protocol
AF_LOCAL UNIX domain protocols
AF_ROUTE Routing sockets
AF_KEY
key sockets (used for encryption-key exchange)

TYPE:
SOCK_STREAM
SOCK_DGRAM
SOCK_RAW

stream socket TCP


datagram socket UDP
raw socket

Other functions are:


uint32_t htonl(uint32_t host32bitvalue)
s stands for short i.e. 16 bit and l stands for long, i.e. 32 bit
uint16_t ntohs(uint16_t net16bitvalue)
uint32_t ntohl(uint32_t net32bitvalue)
return value in host byte order

int inet_pton(int family, const char *strptr, void *addrptr)


returns 1 if OK, 0 if input not valid presentation format, -1 on error
In #include<arpa/inet.h>
p stands for presentation (dotted decimal format(192.168.2.200), n stands for
numeric (binary format)
converts the address at strptr and saves it at the address addrptr.
this function is supported by only IPv4 and IPv6.
If family not supported is given, function returns -1 with errno set to
EAFNOSUPPORT.

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)


returns 0 if OK, -1 on error
sockfd is the socket descriptor returned by the socket function call.
connect() uses struct sockaddr and not struct sockaddr_in
struct sockaddr {
unsigned short sa_family; /* address family, AF_xxx */
char
sa_data[14]; /* 14 bytes of protocol address */
};
sa_family can be set to the address family.
sa_data contains a destination address and port number for the socket.
Developers didnt wanted to keep address and port number in one variable, so
they created a parallel structure struct sockaddr_in.
A pointer to a struct sockaddr_in can be cast to a pointer to a struct sockaddr and
vice-versa.

Internet protocol address is the combination of either a 32-bit IPv4 address or a


128 bit IPv6 address, along with 16 bit TCP or UDP port number.

TCP is a byte-stream protocol with no record boundaries.


Reply to the TCPdaytime client is a 26-byte string of the form:
Fri Dec 11 14:27:52 2008\r\n
\r is ASCII carriage return and \n is ASCII linefeed. Here these two symbols are
used as record boundary.
TCP can send the above 26 bytes as one segment of size 26, or 26 segments of
1 byte each or any other combination that totals to 26.
So we must make sure some way to delineate the end of record.

A Simple Daytime Server


int main (int argc, char **argv)
{
int listenfd, connfd;
char buff[MAXLINE];
struct sockaddr_in servaddr; /*servaddr will contain address of this server */
time_t ticks;
listenfd = socket(AF_INET, SOCK_STREAM,0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET; //AF_INET for IPv4
servaddr.sin_port = htons(13);
//daytime server port number
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //we are asking kernel to choose IP address
bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
for ( ; ; ) {
connfd = accept(listenfd, (SA*)NULL, NULL);
ticks = time(NULL);
snprintf(buff, sizeof(buff), %.24s\r\n, ctime(&ticks));
write(connfd, buff, strlen(buff));
close(connfd);
}
}

int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);


returns 0 if OK, else -1
in #include<sys/socket.h>
bind function assigns a protocol address to the socket specified.
bind function is not called by the client coz client kernel automatically chooses
ephemeral port number when connect is called, but servers are known by their
port numbers, so we have a bind call in server and not in client.
For client, the address is the source IP address that will be used for IP datagrams
sent on the socket.
For server, this restricts the socket to receive incoming client connections
destined only to that IP address.
Bind function call lets us specify IP address, port number, both or neither.
Whatever we do not specify, the kernel chooses that by itself.

IP Address

Port Number

Result

Wildcard

Kernel chooses IP addresses and port number.

Wildcard

Non-zero

Kernel chooses IP address. Port number specified.

IP address

Kernel chooses port number. IP address specified.

IP address

Non-zero

IP address and Port number both are specified.

In IPv4, wildcard address is specified by the address INADDR_ANY, whose value


is normally 0.
In IPv6 wildcard is specified as under:
struct sockaddr_in6 serv;
serv.sin6_addr = in6addr_any;
The system allocates and initializes the in6addr_any variable to the constant
IN6ADDR_ANY_INIT.

int listen(int sockfd, int backlog)


In #include <sys/socket.h>
returns 0 if OK, -1 on error.

When listen function is called by the server, it performs following functions:


Socket is created by the socket function call by the client, and then connect
function call is made, at this time socket of the client is assumed to be an
active socket.
Listen function call on the server side, converts an unconnected socket into
passive socket, indicating that the server kernel should accept incoming
connection requests directed to this socket.
The second argument to this function specifies the maximum number of
connections that the kernel should queue for this socket.

Listen function is called after socket and bind functions and must be called before
calling the accept function.
After these three function calls, socket, bind and listen, on the server side, the
socket descriptor is said to be listening descriptor.

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)


returns nonnegative client descriptor that has been connected, if OK, else -1 on
error.
If accept is successful, its return value is a brand new descriptor value that is
automatically created by the kernel. The new descriptor is used for
communication with the TCP client, and is called connected descriptor.
in #include<sys/socket.h>
sockfd is the socket descriptor of the server socket that is in listening state.
cliaddr is set to the client address with which the connection is set.

A new descriptor is returned by accept for each client that connects to the server.

time( ) function returns the number of seconds that have elapsed since 00:00:00
january 1, 1970.
Then ctime function returns the time in human readable format:
Fri Jan 12 14:27:24 2008

snprintf is same as sprintf, except that sprintf does not check for destination buffer
overflow, which snprintf does.

int close(int sockfd)


in #include<unistd.h>
Returns 0 if OK, -1 on error
Close is used to close a socket and terminate a TCP connection.
Close function call will initiate TCP connection termination sequence.

Some important points about TCP daytime server client:


The client & server are protocol dependent on IPv4.
The server is iterative coz it iterates through each client one at a time.
There are numerous ways for writing a concurrent server, one that
handles multiple clients at one time.
Simplest technique is to use Unix fork() function call.
Another way is to use threads instead of fork.

A Simple Daytime Client IPv6 version


int main (int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE +1];
struct sockaddr_in6 servaddr; //servaddr will contain address of server
if (argc !=2)
err_quit (usage: a.out <IP Address of the server> );
if ((sockfd = socket(AF_INET6, SOCK_STREAM,0)) < 0)
err_sys (socket error);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(13);
//daytime server port number
if(inet_pton(AF_INET6,argv[1], &servaddr.sin6_addr) <= 0)
err_quit(inet_pton error);
if (connect( sockfd, (SA *)servaddr, sizeof(servaddr)) < 0)
err_sys(connect error);
while((n = read(sockfd, recvline, MAXLINE)) > 0)
{ recvline[n] = 0;
//null terminate
if(fputs(recvline, stdout) == 0)
err_sys(fputs error);
}
exit(0);
}

When error occurs in any Unix function, the global variable errno is set to a
positive value indicating the type of error.
The value of errno is set only if an error occurs, its value is undefined if the
function does not return an error.
All the positive error values are constants with an all upper-case name beginning
with E and are defined in <sys.errno.h> header.
No error has the value 0.
Storing error in a global variable does not work with multiple threads that share all
global variables.

Application Details

Application
Presentation

User
process

Application

Session
Transport

TCP

UDP

Network

IPv4, IPv6

Datalink
Physical

Device Driver
&
Hardware

OSI Model

IP suite

Sockets
XTI

kernel

Communication details

A gap is shown between TCP and UDP to indicate that IPv6 and IPV4
can directly communicate with the application layer. Such a case is called
a raw socket.
Both sockets and XTI are interfaces from the application layer into the
transport layer.
Why both sockets and XTI are provided as an interface between layer 4
and 5?
There are two reasons for this:
The upper three layers handle all the details of the application
(FTP, Telnet, HTTP) and know little about communication details.
The lower 4 layers know little about application details but handle
the communication details (sending data, waiting for an
acknowledgement, sequencing data that arrives out of order,
calculating and verifying checksum)
The second reason is that the upper three layers form a user
process while the lower three layers are normally provided as part
of the operating system kernel.

Das könnte Ihnen auch gefallen