You are on page 1of 57

UNIX

SOCKET PROGRAMMING

Outline
Socket and Internet Sockets
Network programming functions
Socket System Calls
TCP Sockets Programming
UDP Sockets Programming
Sockets
• Sockets provide mechanisms to communicate
between computers across a network

• There are different kind of sockets

• Berkeley sockets is the most popular Internet


Socket
– runs on Linux, FreeBSD, Windows
Network API
• Operating system provides Application
Programming Interface (API) for network
application

• API is defined by a set of function types, data


structures, and constants

• Application Programming Interface for networks


is called socket
Internet Sockets

• Support stream and datagram packets (e.g.


TCP, UDP, IP)

• Is Similar to UNIX file I/O API

• Based on C.
Types of Internet Sockets
• Different types of sockets implement different
communication types (stream vs. datagram)
• Type of socket: stream socket
– connection-oriented
– two way communication
– reliable (error free), in order delivery
– can use the Transmission Control Protocol (TCP)
– e.g. telnet, http
• Type of socket: datagram socket
– connectionless, does not maintain an open
connection, each packet is independent
– can use the User Datagram Protocol (UDP)
– e.g. IP telephony
Data types

int8_t signed 8bit int


uint8_t unsigned 8 bit int
int16_t signed 16 bit int
uint16_t unsigned 16 bit int
int32_t signed 32 bit int
uint32_t unsigned 32 bit int

u_char, u_short, u_int, u_long


More data types
sa_family_t address family
socklen_t length of struct
in_addr_t IPv4 address
in_port_t IP port number
Naming and Addressing
• Host name
– identifies a single host
– variable length string (e.g. www.berkeley.edu)
– is mapped to one or more IP addresses

• IP Address
– written as dotted octets (e.g. 10.0.0.1)
– 32 bits. Not a number! But often needs to be
converted to a 32-bit to use.
• Port number
– identifies a process on a host
– 16 bit number
– Reserved ports ( 0 -1024 )
struct sockaddr_in (IPv4)
struct sockaddr_in {
uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
A special kind of sockaddr structure
struct in_addr
struct in_addr {
in_addr_t s_addr;
};

in_addr_t is uint32_t
Generic Socket Address
• The sockets API is generic.

• There must be a generic way to specify


endpoint addresses.

• TCP/IP requires an IP address and a port


number for each endpoint address.
Generic socket addresses
struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};

• sa_family specifies the address type.


• sa_data specifies the address value.
Generic Socket Address
• We don’t need to deal with sockaddr
structures since we will only deal with a real
protocol family.
• We can use sockaddr_in structures.

BUT: Convert this socket address structure


into generic one using type casting.
sockaddr sockaddr_in
sa_len sin_len
sa_family AF_INET
sin_port

sin_addr
sa_data

sin_zero
Network Programming Functions

• Byte Ordering
• Byte Manipulation functions
• Addressing
• Socket system calls
Byte Ordering of Integers
• Different CPU architectures have different byte
ordering
memory memory
address A +1 address A

Stored at little-endian high-order byte low-order byte


computer

Integer representation (2 D3 F2
byte)

Stored at big-endian low-order byte high-order byte


computer
Byte Order

• Host Byte order


Byte ordering used by a specific host

• Network Byte order


All values stored in a sockaddr_in
must be in network byte order.
– sin_port a TCP/IP port number.
– sin_addr an IP address.
Network Byte Order Functions

‘h’ : host byte order ‘n’ : network byte order


‘s’ : short (16bit) ‘l’ : long (32bit)

uint16_t htons(uint16_t);
uint16_t ntohs(uint_16_t);

uint32_t htonl(uint32_t);
uint32_t ntohl(uint32_t);
Byte Manipulation Functions

• Void bzero (void *dest, size_t nbytes)

• Void bcopy (const void *src, void *dest,


size_t nbytes)

• Int bcmp (const void *ptr1, const void


*ptr2, size_t nbytes)
IPv4 Address Conversion
int inet_aton( char *, struct in_addr
*);

Convert ASCII dotted-decimal IP address to network


byte order 32 bit value. Returns 1 on success, 0 on
failure.

char *inet_ntoa(struct in_addr);

Convert network byte ordered value to ASCII dotted-


decimal (a string).

a – ASCII dotted-decimal ipv4 address


n –32 bit binary ipv4 address in network byte order
Creating a Socket

int socket(int family,int type,int


proto);

• family specifies the protocol family


(AF_INET for TCP/IP).
• type specifies the type of service
(SOCK_STREAM, SOCK_DGRAM).
• protocol specifies the specific protocol
(usually 0, which means the default).
socket()
• The socket() system call returns a
socket descriptor (small integer) or -1 on
error.

• socket() allocates resources needed for


a communication endpoint - but it does not
deal with endpoint addressing.
Bind()
• The bind() system call is used to assign
an address to an existing socket.

• It tells the os to assign a local IP address


and local port number to the socket.

int bind( int sockfd,


const struct sockaddr *myaddr,
int addrlen);

• bind returns 0 if successful or -1 on error.


bind() Example
int mysock,err;
struct sockaddr_in myaddr;

mysock = Socket(AF_INET,SOCK_STREAM,0);
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons( portnum );
myaddr.sin_addr = htonl( ipaddress);

err=bind(mysock, (struct sockaddr *)


&myaddr, sizeof(myaddr));
Uses for bind()
• There are a number of uses for bind():

– Server would like to bind to a well known


address (port number).

– Client can bind to a specific port.

– Client can ask the O.S. to assign any available


port number.
myaddr.port = htons(0);
Uses for bind()

If the computer has multiple network


interfaces?
• There is no realistic way to know the
right IP address for bind()

• specify the IP address as: INADDR_ANY,


this tells the OS to take care of things.
listen()
int listen( int sockfd, int backlog);

sockfd is the TCP socket (already bound to an


address)

Once we call listen(), the O.S. will queue


incoming connections
backlog is the number of incoming connections
the kernel should queue for this socket.

listen() returns -1 on error (otherwise 0).


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

sockfd is the passive mode TCP socket.


cliaddr is a pointer to allocated space.
addrlen is a value-result argument
– must be set to the size of cliaddr
– on return, will be set to be the number of
used bytes in cliaddr.
accept()

accept() returns a new socket


descriptor (small positive integer) or -1
on error.
Connect()
• TCP clients can call connect() which:

– takes care of establishing an endpoint address


for the client socket.
• No need to call bind , the O.S. will take care
of assigning the local endpoint address (TCP
port number, IP address).

– Attempts to establish a connection to the


specified server.
• 3-way handshake
connect()

int connect( int sockfd,


const struct sockaddr *server,
socklen_t addrlen);

sockfd is an already created TCP socket.


server contains the address of the server (IP
Address and TCP port number)

connect() returns 0 if OK, -1 on error


connect()

After connection is established I/O can be


done using the system calls.
Terminating a TCP connection

• Either end of the connection can call the


close() system call.
Value-Result Parameters
• Bind(),connect(), and sendto() pass a socket
address from the process to the kernel.

• Accept(), recvfrom() pass a socket address


structure from kernel to the process.
• Value – Tells the kernel the size of the
structure.
• Result – Tells the process how much
information the kernel actually stored in the
structure.
Concurrent servers

• To handle multiple clients at the


same time

• The simplest way is to fork a child


process to handle each client.
Simple TCP Client-Server Example
response
Client Server
request

socket()
bind()
socket() listen()
Connection
connect() establishment accept()
write()
Data request read()

Data response write()


read()
close() read()
End-of-file notification
close()
Example: Client Programming

• Create stream socket (socket() )


• Connect to server (connect() )
• While still connected:
– send message to server (write() )
– receive (read() ) data from server and
process it
• Close TCP connection and Socket
(close())
Server Programming: Simple

• Create stream socket (socket() )


• Bind port to socket (bind() )
• Listen for new client (listen() )
• While
– accept user connection and create a new
socket (accept() )
– data arrives from client (read() )
– data has to be send to client (write() )
Creating & Binding TCP Socket
int mysock;
struct sockaddr_in myaddr;

mysock=socket(PF_INET,SOCK_STREAM,0);

myaddr.sin_family = AF_INET;
myaddr.sin_port = htons( 80 );
myaddr.sin_addr = htonl( INADDR_ANY);

bind(mysock,(struct sockaddr *) &myaddr,


sizeof(myaddr));
Reading from a TCP socket
int read( int fd, char *buf, int max);

• By default read() will block until data is


available.
Writing to a TCP socket

int write( int fd, char *buf, int


num);
UDP Sockets Programming
Server
(Connectionless
protocol)

socket ( )

bind ( )
Client

socket ( )
recvfrom( )

bind ( )
blocks until data received from a client
data (request)
sendo ( )

process request

sendto ( ) recvfrom( )
data reply

Socket system calls for connectionless protocol


Typical UDP client code
• Create UDP socket.

• Create sockaddr with address of server.

• Call sendto(), sending request to the


server. No call to bind() is necessary!

• Possibly call recvfrom() (if we need a


reply).
Typical UDP Server code
• Create UDP socket and bind to well known
address.

• Call recvfrom() to get a request, noting


the address of the client.

• Process request and send reply back with


sendto().
Creating & Binding UDP
Socket
int mysock;
struct sockaddr_in myaddr;
mysock = socket(PF_INET,SOCK_DGRAM,0);

myaddr.sin_family = AF_INET;
myaddr.sin_port = htons( 1234 );
myaddr.sin_addr = htonl( INADDR_ANY );

bind(mysock, &myaddr, sizeof(myaddr));


Sending UDP Datagrams
ssize_t sendto( int sockfd,
void *buff,
size_t nbytes,
int flags,
const struct sockaddr* to,
socklen_t addrlen);
sockfd is a UDP socket
buff is the address of the data (nbytes long)
to is the address of a sockaddr containing the
destination address.
Return value is the number of bytes sent, or -1
on error.
Receiving UDP Datagrams
ssize_t recvfrom( int sockfd,
void *buff,
size_t nbytes,
int flags,
struct sockaddr* from,
socklen_t *fromaddrlen);
sockfd is a UDP socket
buff is the address of a buffer (nbytes long)
from is the address of a sockaddr.
Return value is the number of bytes received
and put into buff, or -1 on error.
recvfrom()
• If buff is not large enough, any extra data is
lost ...

• recvfrom doesn’t return until there is a


datagram available.

• The sockaddr at from is filled in with the


address of the sender.

• set fromaddrlen before calling.


Server Example
#define MYPORT 3490 // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold

int main(void) {
int sockfd, new_fd; // listen on sockfd, new connection on new_fd
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
int sin_size;

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {


perror("socket");
exit(1);
}

my_addr.sin_family = AF_INET; // host byte order


my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // auto. filled with local IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct

49
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}

if (listen(sockfd, BACKLOG) == -1) {


perror("listen");
exit(1);
}

while(1) { // main accept() loop


sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
perror("accept");
continue;
}

printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr));

if (send(new_fd, "Hello, world!\n", 14, 0) == -1)


perror("send");

close(new_fd);
}
return 0;
}
50
#include <netinet/in.h>
Client Example
#include <sys/socket.h>

#define PORT 3490 // the port client will be connecting to


#define MAXDATASIZE 100 // max number of bytes we can get
// at once
int main(int argc, char *argv[]) {
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in their_addr; // server's address information

if (argc != 2) {
fprintf(stderr,"usage: client hostname\n");
exit(1);
}

if ((he=gethostbyname(argv[1])) == NULL) { // get the host info


perror("gethostbyname");
exit(1);
}

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {


perror("socket");
exit(1);
} 51
their_addr.sin_family = AF_INET; // host byte order
their_addr.sin_port = htons(PORT); // short, network byte order
their_addr.sin_addr = *((struct in_addr *)he->h_addr); // already network byte order
memset(&(their_addr.sin_zero), '\0', 8); // zero the rest of the struct

if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1){


perror("connect");
exit(1);
}

if ((numbytes=recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {


perror("recv");
exit(1);
}

buf[numbytes] = '\0';
printf("Received: %s",buf);
close(sockfd);
return 0;
}

52
Iterative (or sequential) Server
• Handles one request at a time
• Client waits for all previous requests to be processed
• Unacceptable to user if long request blocks short request
while (1) {
accept a connection (or request) from a client
service the client
close the connection (if necessary)
}
Concurrent Server
• Can handle multiple requests at a time by creating
new thread of control to handle each request
• No waiting
while (1) {
accept a connection/request from client
start a new thread to handle this client
/* the thread must close the connection! */
}
Why Was There a Need to Introduce
Concurrency in Client-Server

• Not effective conventional iterative method


• Iterative servers are suitable for the most trivial services, (e.g.,
Time of Day, Local Time, Echo Servers, etc..)
• They are NOT suitable for requests that require substantial
amount of time to be completed, (e.g., FTP, TELNET, FILE
SERVER, etc..)
• Large amount of data could consume lot of time
• This deprives communication for other client terminals
• A concurrent server, on the other hand, is a server that can handle
multiple requests.
- Concurrent Server can
handle multiple requests at the
same time
- Server maintains a queue of
connections.
Client

Internet
Client

Concurrent Server

Client - Iterative Server can handle only


ONE request at a time.
- No queues are maintained.
- If the server is busy, client must
retry again.

Client

Internet
Client

Iterative Server

Client
Thank you

You might also like