Socket Programming Notes
Socket Programming Notes
Kernel Support
Hardware
Socket
What are Sockets?
Communication abstractions Support the TCP/IP protocol stack Provide access to both reliable (TCP) and unreliable (UDP) transport services Programming tools to implement client-server applications
What is a socket?
Socket: An interface between an application process and transport layer. The application process can send/receive messages to/from another application process (local or remote)via a socket.
In Unix jargon, a socket is a file descriptor an integer associated with an open file. Types of Sockets: Internet Sockets, unix sockets, X.25 sockets etc.
Socket API
kernel space
hardware
application viewpoint TCP provides reliable, in-order transfer of bytes (pipe) between client and server
Server socket()
bind()
listen() accept()
blocks until connection from client
Connection establishment
Client
socket()
connect() write() read()
read()
Process request
Data (request)
write()
Data (reply)
TCP Server
Web Server
Port 80
TCP
IP
Ethernet Adapter
socket() call
int fd; /* socket descriptor */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) } perror(socket); exit(1); }
bind() call
A socket can be bound to a port
int fd; struct sockaddr_in srv; /* create the socket */ /* socket descriptor */ /* used by bind() */
listen() call
listen indicates that the server will accept a connection
int fd; struct sockaddr_in srv; /* socket descriptor */ /* used by bind() */
/* 1) create the socket */ /* 2) bind the socket to a port */ if(listen(fd, 5) < 0) { perror(listen); exit(1); }
accept() call
accept blocks waiting for a connection
int fd; /* socket descriptor */ struct sockaddr_in srv; /* used by bind() */ struct sockaddr_in cli; /* used by accept() */ int newfd; /* returned by accept() */ int cli_len = sizeof(cli); /* used by accept() */ /* 1) create the socket */ /* 2) bind the socket to a port */ /* 3) listen on the socket */ newfd = accept(fd, (struct sockaddr*) &cli, &cli_len); if(newfd < 0) { perror("accept"); exit(1); }
accept returns a new socket (newfd) with the same properties as the original socket (fd) newfd < 0 indicates that an error occurred
accept() call
struct sockaddr_in cli; int newfd; int cli_len = sizeof(cli); /* used by accept() */ /* returned by accept() */ /* used by accept() */
newfd = accept(fd, (struct sockaddr*) &cli, &cli_len); if(newfd < 0) { perror("accept"); exit(1); }
Now the server can exchange data with the client by using read and write on the descriptor newfd. Why does accept need to return a new descriptor?
read() call
read can be used with a socket read blocks waiting for data from the client but does not guarantee that sizeof(buf) is read
int fd; char buf[512]; int nbytes; /* /* /* /* 1) 2) 3) 4) /* socket descriptor */ /* used by read() */ /* used by read() */
create the socket */ bind the socket to a port */ listen on the socket */ accept the incoming connection */
TCP Client
For example: web client
2 Web Clients
IP
Ethernet Adapter
connect() call
connect allows a client to connect to a server...
int fd; struct sockaddr_in srv; /* create the socket */ /* connect: use the Internet address family */ srv.sin_family = AF_INET; /* connect: socket fd to port 80 */ srv.sin_port = htons(80); /* connect: connect to IP Address 128.2.35.50 */ srv.sin_addr.s_addr = inet_addr(128.2.35.50); if(connect(fd, (struct sockaddr*) &srv, sizeof(srv)) < 0) { perror(connect"); exit(1); } /* socket descriptor */ /* used by connect() */
write() call
write can be used with a socket
int fd; struct sockaddr_in srv; char buf[512]; int nbytes; /* /* /* /* socket descriptor */ used by connect() */ used by write() */ used by write() */
/* 1) create the socket */ /* 2) connect() to the server */ /* Example: A client could write a request to a server */ if((nbytes = write(fd, buf, sizeof(buf))) < 0) { perror(write); exit(1); }
application viewpoint UDP provides unreliable transfer of groups of bytes (datagrams) between client and server
Client
socket() bind() sendto()
Process request
write()
Data (reply)
recvfrom()
IP
Ethernet Adapter
socket() call
The UDP server must create a datagram socket
int fd; /* socket descriptor */
socket returns an integer (socket descriptor) fd < 0 indicates that an error occurred AF_INET: associates a socket with the Internet protocol family SOCK_DGRAM: selects the UDP protocol
bind() call
A socket can be bound to a port
int fd; struct sockaddr_in srv; /* create the socket */ /* bind: use the Internet address family */ srv.sin_family = AF_INET; /* socket descriptor */ /* used by bind() */
recvfrom() call
read does not provide the clients address to the UDP server
int fd; struct sockaddr_in srv; struct sockaddr_in cli; char buf[512]; int cli_len = sizeof(cli); int nbytes; /* 1) create the socket */ /* 2) bind to the socket */ nbytes = recvfrom(fd, buf, sizeof(buf), 0 /* flags */, (struct sockaddr*) &cli, &cli_len); if(nbytes < 0) { perror(recvfrom); exit(1); } /* /* /* /* /* /* socket descriptor */ used by bind() */ used by recvfrom() */ used by recvfrom() */ used by recvfrom() */ used by recvfrom() */
recvfrom() call
nbytes = recvfrom(fd, buf, sizeof(buf), 0 /* flags */, (struct sockaddr*) cli, &cli_len);
IP
Ethernet Adapter
sendto() call
write is not allowed Notice that the UDP client does not bind a port number a port number is dynamically assigned when the first sendto is called
int fd; struct sockaddr_in srv; /* 1) create the socket */ /* sendto: send data to IP Address 128.2.35.50 port 80 */ srv.sin_family = AF_INET; srv.sin_port = htons(80); srv.sin_addr.s_addr = inet_addr(128.2.35.50); nbytes = sendto(fd, buf, sizeof(buf), 0 /* flags */, (struct sockaddr*) &srv, sizeof(srv)); if(nbytes < 0) { perror(sendto); exit(1); } /* socket descriptor */ /* used by sendto() */
UDP
IP
Ethernet Adapter
while(1) { recvfrom(s1, buf, sizeof(buf), ...); /* process buf */ recvfrom(s2, buf, sizeof(buf), ...); /* process buf */ }
select()call
int select(int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
FD_CLR(int fd, fd_set *fds); FD_ISSET(int fd, fd_set *fds); FD_SET(int fd, fd_set *fds); FD_ZERO(fd_set *fds); /* /* /* /* clear the bit for fd in fds */ is the bit for fd in fds? */ turn on the bit for fd in fds */ clear all bits in fds */
writefds: returns a set of fds ready to write exceptfds: returns a set of fds with exception conditions
select() call
int select(int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); struct timeval { long tv_sec; long tv_usec; }
/* seconds / /* microseconds */
timeout
if NULL, wait forever and return only when one of the descriptors is ready for I/O otherwise, wait up to a fixed amount of time specified by timeout
if we dont want to wait at all, create a timeout structure with timer value equal to 0
select() call
select allows synchronous I/O multiplexing
int s1, s2; fd_set readfds; /* socket descriptors */ /* used by select() */ /* create and bind s1 and s2 */ while(1) { FD_ZERO(&readfds); /* initialize the fd set */ FD_SET(s1, &readfds); /* add s1 to the fd set */ FD_SET(s2, &readfds); /* add s2 to the fd set */ if(select(s2+1, &readfds, 0, 0, 0) < 0) { perror(select); exit(1); } if(FD_ISSET(s1, &readfds)) { recvfrom(s1, buf, sizeof(buf), ...); /* process buf */ } /* do the same for s2 */ }
Bijay Mishra
9841695609 biizay.blogspot.com biizay@gmail.com