- A socket is defined as an endpoint for communication. A pair of processes communicating over a network employs a pair of sockets—one for each process.
- Sockets are a C-language programming interface between Layer 7 (applications) and Layer 4 (transport).
- A socket is identified by an IP address concatenated with a port number. In general, sockets use a client–server architecture.
- The server waits for incoming client requests by listening to a specified port. Once a request is received, the server accepts a connection from the client socket to complete the connection. Servers implementing specific services (such as telnet, FTP, and HTTP) listen to well-known ports (a telnet server listens to port 23; an FTP server listens to port 21; and a web, or HTTP, server listens to port 80). All ports below 1024 are considered well known; we can use them to implement standard services.
When a client process initiates a request for a connection, it is assigned a port by its host computer. This port has some arbitrary number greater than 1024. For example, if a client on host X with IP address 146.86.5.20 wishes to establish a connection with a web server (which is listening on port 80) at address 161.25.19.8, host X may be assigned port 1625. The connection will consist of a pair of sockets: (146.86.5.20:1625) on host X and (161.25.19.8:80) on the web server. This situation is illustrated in below figure. The packets traveling between the hosts are delivered to the appropriate process based on the destination port number.
All connections must be unique. Therefore, if another process also on host X wished to establish another connection with the same web server, it would be assigned a port number greater than 1024 and not equal to 1625. This ensures that all connections consist of a unique pair of sockets.
The client server model
Most inter-process communication uses the client server model. These terms refer to the two processes which will be communicating with each other. One of the two processes, the client, connects to the other process, the server, typically to make a request for information. A good analogy is a person who makes a phone call to another person.
Remember that the client needs to know of the existence of and the address of the server, but the server does not need to know the address of (or even the existence of) the client prior to the connection being established.
Note also that once a connection is established, both sides can send and receive information.
The system calls for establishing a connection are somewhat different for the client and the server, but both involve the basic construct of a socket.
A socket is one end of an inter-process communication channel. The two processes each establish their own socket.
The steps involved in establishing a socket on the client side are as follows:
Most inter-process communication uses the client server model. These terms refer to the two processes which will be communicating with each other. One of the two processes, the client, connects to the other process, the server, typically to make a request for information. A good analogy is a person who makes a phone call to another person.
Remember that the client needs to know of the existence of and the address of the server, but the server does not need to know the address of (or even the existence of) the client prior to the connection being established.
Note also that once a connection is established, both sides can send and receive information.
The system calls for establishing a connection are somewhat different for the client and the server, but both involve the basic construct of a socket.
A socket is one end of an inter-process communication channel. The two processes each establish their own socket.
The steps involved in establishing a socket on the client side are as follows:
- Create a socket with the socket() system call
- Connect the socket to the address of the server using the connect() system call
- Send and receive data. There are a number of ways to do this, but the simplest is to use the read() and write() system calls.
The steps involved in establishing a socket on the server side are as follows:
- Create a socket with the socket() system call
- Bind the socket to an address using the bind() system call. For a server socket on the Internet, an address consists of a port number on the host machine.
- Listen for connections with the listen() system call
- Accept a connection with the accept() system call. This call typically blocks until a client connects with the server.
- Send and receive data
client/server relationship of the socket APIs for connection-oriented protocol (TCP)
client/server relationship of the socket APIs for a connectionless protocol (UDP)
Socket Types
When
a socket is created, the program has to specify the address domain and
the socket type. Two processes can communicate with each other only if
their sockets are of the same type and in the same domain.
Types of sockets are:
Stream Socket
Provides two-way, sequenced, reliable, and unduplicated flow of data with no record boundaries. A stream operates much like a telephone conversation. The socket type is SOCK_STREAM, which, in the Internet domain, uses Transmission Control Protocol (TCP).
E.g., SSH, HTTP (Web), SMTP (email)
Datagram Socket
Supports a two-way flow of messages. A on a datagram socket may receive messages in a different order from the sequence in which the messages were sent. Record boundaries in the data are preserved. Datagram sockets operate much like passing letters back and forth in the mail. The socket type is SOCK_DGRAM, which, in the Internet domain, uses User Datagram Protocol (UDP).
E.g., IP Telephony (Skype), simple request/reply protocols
(hostname → address lookups via DNS; time synchronization via NTP)
Sequential Packet Socket
Provides a two-way, sequenced, reliable, connection, for datagrams of a fixed maximum length. The socket type is SOCK_SEQPACKET. No protocol for this type has been implemented for any protocol family.
Raw Socket
Provides access to the underlying communication protocols.
Difference between Stream Sockets, and Datagram Sockets :
Types of sockets are:
Stream Socket
Provides two-way, sequenced, reliable, and unduplicated flow of data with no record boundaries. A stream operates much like a telephone conversation. The socket type is SOCK_STREAM, which, in the Internet domain, uses Transmission Control Protocol (TCP).
E.g., SSH, HTTP (Web), SMTP (email)
Datagram Socket
Supports a two-way flow of messages. A on a datagram socket may receive messages in a different order from the sequence in which the messages were sent. Record boundaries in the data are preserved. Datagram sockets operate much like passing letters back and forth in the mail. The socket type is SOCK_DGRAM, which, in the Internet domain, uses User Datagram Protocol (UDP).
E.g., IP Telephony (Skype), simple request/reply protocols
(hostname → address lookups via DNS; time synchronization via NTP)
Sequential Packet Socket
Provides a two-way, sequenced, reliable, connection, for datagrams of a fixed maximum length. The socket type is SOCK_SEQPACKET. No protocol for this type has been implemented for any protocol family.
Raw Socket
Provides access to the underlying communication protocols.
Difference between Stream Sockets, and Datagram Sockets :
- Stream sockets use TCP (Transmission Control Protocol), which is a reliable, stream oriented protocol, and datagram sockets use UDP (Unix Datagram Protocol), which is unreliable and message oriented.
- Stream sockets treat communications as a continuous stream of characters, while datagram sockets have to read entire messages at once. Each uses its own communications protocol.
Using Stream Sockets
“Hi there!” and “Hope you are well” separately
Datagram Sockets
- No need to packetize data
- Data arrives in the form of a “byte stream”
- Receiver needs to separate messages in stream
- TCP may send the messages joined together-“Hi there!Hope you are well” or may send them separately, or might even split them like “Hi there!Ho” and “pe you are well”
“Hi there!” and “Hope you are well” separately
Datagram Sockets
- User packetizes data before sending
- Maximum size of 64 KB
- Using previous example, “Hi there!” and “Hope you are well” definitely sent in separate packets at network layer
- Message boundaries preserved
But note: your message had better fit within 64 KB or else you’ll have to layer your own boundary mechanism on top of the datagram delivery anyway.
Address Domains-There are two widely used address domains:
- The Unix domain, in which two processes share a common file system communicate. It provides a socket address space on a single system. UNIX domain sockets are named with UNIX paths. Sockets can also be used to communicate between processes on different systems.
- The Internet domain, in which two processes running on any two hosts on the Internet communicate. Each of these has its own address format. So basically the socket address space between connected systems is called the Internet domain.
- The address of a socket in the Unix domain is a character string which is basically an entry in the file system.
- The address of a socket in the Internet domain consists of the Internet address of the host machine (every computer on the Internet has a unique 32 bit address, often referred to as its IP address).
Port Number
The examples in this tutorial will use sockets in the Internet domain using the TCP protocol.
Sample code
C code for a very simple client and server are provided for you. These communicate using stream sockets in the Internet domain. The code is described in detail below. However, before you read the descriptions and look at the code, you should compile and run the two programs to see what they do.
server.c
/* A simple server in the internet domain using TCP
The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
close(sockfd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
close(sockfd);
return 0;
}
Compile these two files called server.c and client.c separately into two executable called server and client.
Ideally, you should run the client and the server on separate hosts on the Internet. Start the server first. Suppose the server is running on a machine called centos. When you run the server, you need to pass the port number in as an argument. You can choose any number between 2000 and 65535. If this port is already in use on that machine, the server will tell you "ERROR on binding: Address already in use" and exit. If this happens, just choose another port and try again. If the port is available, the server will block until it receives a connection from the client. Don't be alarmed if the server doesn't do anything;
It's not supposed to do anything until a connection is made.
Here is a typical command line:
server 2000
To run the client you need to pass in two arguments, the name of the host on which the server is running and the port number on which the server is listening for connections.
Here is the command line to connect to the server described above:
client centos 2000
- Each socket needs a port number on that host. Port numbers are 16 bit unsigned integers.
- The lower numbers are reserved in Unix for standard services. For example, the port number for the FTP server is 21.
- It is important that standard services be at the same port on all computers so that clients will know their addresses.
- However, port numbers above 2000 are generally available.
The examples in this tutorial will use sockets in the Internet domain using the TCP protocol.
Sample code
C code for a very simple client and server are provided for you. These communicate using stream sockets in the Internet domain. The code is described in detail below. However, before you read the descriptions and look at the code, you should compile and run the two programs to see what they do.
server.c
/* A simple server in the internet domain using TCP
The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
close(sockfd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
close(sockfd);
return 0;
}
Compile these two files called server.c and client.c separately into two executable called server and client.
Ideally, you should run the client and the server on separate hosts on the Internet. Start the server first. Suppose the server is running on a machine called centos. When you run the server, you need to pass the port number in as an argument. You can choose any number between 2000 and 65535. If this port is already in use on that machine, the server will tell you "ERROR on binding: Address already in use" and exit. If this happens, just choose another port and try again. If the port is available, the server will block until it receives a connection from the client. Don't be alarmed if the server doesn't do anything;
It's not supposed to do anything until a connection is made.
Here is a typical command line:
server 2000
To run the client you need to pass in two arguments, the name of the host on which the server is running and the port number on which the server is listening for connections.
Here is the command line to connect to the server described above:
client centos 2000
- The client will prompt you to enter a message.
- If everything works correctly, the server will display your message on stdout, send an acknowledgement message to the client and terminate.
- The client will print the acknowledgement message from the server and then terminate.
You
can simulate this on a single machine by running the server in one
window and the client in another. In this case, you can use the keyword
localhost as the first argument to the client.
example : when I compiled on same machine and executed on two different window :
server window :
example : when I compiled on same machine and executed on two different window :
server window :
[root@cisco socket]# server 2000
Here is the message: hey buddy, socket connection is made
client window :
[root@cisco socket]# client localhost 2000
Please enter the message: hey buddy, socket connection is made
I got your message
header files :
#include <sys/types.h>
This header file contains definitions of a number of data types used in system calls. These types are used in the next two include files.
#include <sys/socket.h>
The header file socket.h includes a number of definitions of structures needed for sockets.
#include <netinet/in.h>
The header file in.h contains constants and structures needed for internet domain addresses.
#include <netdb.h>
The file netdb.h defines the structure hostent.
int atoi(const char *nptr);
converts the initial portion of the string pointed to by nptr to int
struct hostent *gethostbyname(const char *name);
returns a structure of type hostent for the given host name
name is a hostname, or an IPv4 address in standard dot notation
e.g. gethostbyname(“www.cdynamicprogramming.blogspot.in”);
void error(char *msg){
perror(msg);
exit(1);
}
This function is called when a system call fails. It displays a message about the error on stderr and then aborts the program. The perror man page gives more information.
This header file contains definitions of a number of data types used in system calls. These types are used in the next two include files.
#include <sys/socket.h>
The header file socket.h includes a number of definitions of structures needed for sockets.
#include <netinet/in.h>
The header file in.h contains constants and structures needed for internet domain addresses.
#include <netdb.h>
The file netdb.h defines the structure hostent.
int atoi(const char *nptr);
converts the initial portion of the string pointed to by nptr to int
struct hostent *gethostbyname(const char *name);
returns a structure of type hostent for the given host name
name is a hostname, or an IPv4 address in standard dot notation
e.g. gethostbyname(“www.cdynamicprogramming.blogspot.in”);
void error(char *msg){
perror(msg);
exit(1);
}
This function is called when a system call fails. It displays a message about the error on stderr and then aborts the program. The perror man page gives more information.
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen, n;
sockfd and newsockfd are file descriptors, i.e. array subscripts into the file descriptor table . These two variables store the values returned by the socket system call and the accept system call.
{
int sockfd, newsockfd, portno, clilen, n;
sockfd and newsockfd are file descriptors, i.e. array subscripts into the file descriptor table . These two variables store the values returned by the socket system call and the accept system call.
- A file descriptor is a non-negative integer, represented in C programming language as the type int. It typically serves as an index into a table maintained by the kernel that tracks which files are "opened" by a process for performing input/output.
portno stores the port number on which the server accepts connections.
clilen stores the size of the address of the client. This is needed for the accept system call.
n is the return value for the read() and write() calls; i.e. it contains the number of characters read or written.
char buffer[256];
The server reads characters from the socket connection into this buffer.
clilen stores the size of the address of the client. This is needed for the accept system call.
n is the return value for the read() and write() calls; i.e. it contains the number of characters read or written.
char buffer[256];
The server reads characters from the socket connection into this buffer.
struct sockaddr_in serv_addr, cli_addr;
A sockaddr_in is a structure containing an internet address. This structure is defined in netinet/in.h.
Here is the definition:
struct sockaddr_in
{
short sin_family; /* must be AF_INET */
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8]; /* Not used, must be zero */
};
An in_addr structure, defined in the same header file, contains only one field, a unsigned long called s_addr.
The variable serv_addr will contain the address of the server, and cli_addr will contain the address of the client which connects to the server.
if (argc < 2){
fprintf(stderr,"ERROR, no port provided");
exit(1);
}
The user needs to pass in the port number on which the server will accept connections as an argument. This code displays an error message if the user fails to do this.
A sockaddr_in is a structure containing an internet address. This structure is defined in netinet/in.h.
Here is the definition:
struct sockaddr_in
{
short sin_family; /* must be AF_INET */
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8]; /* Not used, must be zero */
};
An in_addr structure, defined in the same header file, contains only one field, a unsigned long called s_addr.
The variable serv_addr will contain the address of the server, and cli_addr will contain the address of the client which connects to the server.
if (argc < 2){
fprintf(stderr,"ERROR, no port provided");
exit(1);
}
The user needs to pass in the port number on which the server will accept connections as an argument. This code displays an error message if the user fails to do this.
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
The socket() system call creates a new socket. It takes three arguments. The first is the address domain(AF_INET) of the socket.
Recall that there are two possible address domains, the Unix domain for two processes which share a common file system, and the Internet domain for any two hosts on the Internet. The symbol constant AF_UNIX is used for the former(Unix Domain), and AF_INET for the latter (there are actually many other options which can be used here for specialized purposes).
The second argument(SOCK_STREAM) is the type of socket. Recall that there are two choices here, a stream socket in which characters are read in a continuous stream as if from a file or pipe, and a datagram socket, in which messages are read in chunks. The two symbolic constants are SOCK_STREAM and SOCK_DGRAM.
The third argument is the protocol. If this argument is zero (and it always should be except for unusual circumstances), the operating system will choose the most appropriate protocol. It will choose TCP for stream sockets and UDP for datagram sockets.
The socket system call returns an entry into the file descriptor table (i.e. a small integer). This value is used for all subsequent references to this socket. If the socket call fails, it returns -1.
In this case the program displays and error message and exits. However, this system call is unlikely to fail.
This is a simplified description of the socket call; there are numerous other choices for domains and types, but these are the most common. The socket() man page has more information.
bzero((char *) &serv_addr, sizeof(serv_addr));
The function bzero() sets all values in a buffer to zero. It takes two arguments, the first is a pointer to the buffer and the second is the size of the buffer. Thus, this line initializes serv_addr to zeros.
if (sockfd < 0)
error("ERROR opening socket");
The socket() system call creates a new socket. It takes three arguments. The first is the address domain(AF_INET) of the socket.
Recall that there are two possible address domains, the Unix domain for two processes which share a common file system, and the Internet domain for any two hosts on the Internet. The symbol constant AF_UNIX is used for the former(Unix Domain), and AF_INET for the latter (there are actually many other options which can be used here for specialized purposes).
The second argument(SOCK_STREAM) is the type of socket. Recall that there are two choices here, a stream socket in which characters are read in a continuous stream as if from a file or pipe, and a datagram socket, in which messages are read in chunks. The two symbolic constants are SOCK_STREAM and SOCK_DGRAM.
The third argument is the protocol. If this argument is zero (and it always should be except for unusual circumstances), the operating system will choose the most appropriate protocol. It will choose TCP for stream sockets and UDP for datagram sockets.
The socket system call returns an entry into the file descriptor table (i.e. a small integer). This value is used for all subsequent references to this socket. If the socket call fails, it returns -1.
In this case the program displays and error message and exits. However, this system call is unlikely to fail.
This is a simplified description of the socket call; there are numerous other choices for domains and types, but these are the most common. The socket() man page has more information.
bzero((char *) &serv_addr, sizeof(serv_addr));
The function bzero() sets all values in a buffer to zero. It takes two arguments, the first is a pointer to the buffer and the second is the size of the buffer. Thus, this line initializes serv_addr to zeros.
portno = atoi(argv[1]);
The port number on which the server will listen for connections is passed in as an argument, and this statement uses the atoi() function to convert this from a string of digits to an integer.
The port number on which the server will listen for connections is passed in as an argument, and this statement uses the atoi() function to convert this from a string of digits to an integer.
serv_addr.sin_family = AF_INET;
The variable serv_addr is a structure of type struct sockaddr_in. This structure has four fields. The first field is short sin_family, which contains a code for the address family. It should always be set to the symbolic constant AF_INET.
serv_addr.sin_port = htons(portno);
The
second field of serv_addr is unsigned short sin_port, which contain the
port number. However, instead of simply copying the port number to this
field, it is necessary to convert this to network byte order using the
function htons() which converts a port number in host byte order to a
port number in network byte order.
- htons(): host byte order -> network byte order
serv_addr.sin_addr.s_addr = INADDR_ANY;
The
third field of sockaddr_in is a structure of type struct in_addr which
contains only a single field unsigned long s_addr. This field contains
the IP address of the host. For server code, this will always be the IP
address of the machine on which the server is running, and there is a
symbolic constant INADDR_ANY which gets this address.
if(bind(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
The bind() system call binds a socket to an address, in this case the address of the current host and port number on which the server will run. It takes three arguments, the socket file descriptor, the address to which is bound, and the size of the address to which it is bound. The second argument is a pointer to a structure of type sockaddr, but what is passed in is a structure of type sockaddr_in, and so this must be cast to the correct type. This can fail for a number of reasons, the most obvious being that this socket is already in use on this machine. The bind() manual has more information.
error("ERROR on binding");
The bind() system call binds a socket to an address, in this case the address of the current host and port number on which the server will run. It takes three arguments, the socket file descriptor, the address to which is bound, and the size of the address to which it is bound. The second argument is a pointer to a structure of type sockaddr, but what is passed in is a structure of type sockaddr_in, and so this must be cast to the correct type. This can fail for a number of reasons, the most obvious being that this socket is already in use on this machine. The bind() manual has more information.
listen(sockfd,5);
The listen system call allows the process to listen on the socket for connections. The first argument is the socket file descriptor, and the second is the size of the backlog queue, i.e., the number of connections that can be waiting while the process is handling a particular connection. This should be set to 5, the maximum size permitted by most systems. If the first argument is a valid socket, this call cannot fail, and so the code doesn't check for errors. The listen() man page has more information.
The listen system call allows the process to listen on the socket for connections. The first argument is the socket file descriptor, and the second is the size of the backlog queue, i.e., the number of connections that can be waiting while the process is handling a particular connection. This should be set to 5, the maximum size permitted by most systems. If the first argument is a valid socket, this call cannot fail, and so the code doesn't check for errors. The listen() man page has more information.
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
The accept() system call causes the process to block until a client connects to the server. Thus, it wakes up the process when a connection from a client has been successfully established. It returns a new file descriptor, and all communication on this connection should be done using the new file descriptor. The second argument is a reference pointer to the address of the client on the other end of the connection, and the third argument is the size of this structure. The accept() man page has more information.
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
The accept() system call causes the process to block until a client connects to the server. Thus, it wakes up the process when a connection from a client has been successfully established. It returns a new file descriptor, and all communication on this connection should be done using the new file descriptor. The second argument is a reference pointer to the address of the client on the other end of the connection, and the third argument is the size of this structure. The accept() man page has more information.
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s",buffer);
Note that we would only get to this point after a client has successfully connected to our server. This code initializes the buffer using the bzero() function, and then reads from the socket. Note that the read call uses the new file descriptor, the one returned by accept(), not the original file descriptor returned by socket(). Note also that the read() will block until there is something for it to read in the socket, i.e. after the client has executed a write().
It will read either the total number of characters in the socket or 255, whichever is less, and return the number of characters read. The read() man page has more information.
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
Once a connection has been established, both ends can both read and write to the connection. Naturally, everything written by the client will be read by the server, and everything written by the server will be read by the client. This code simply writes a short message to the client. The last argument of write is the size of the message. The write() man page has more information.
Client code
As before, we will go through the program client.c line by line.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
The header files are the same as for the server with one addition. The file netdb.h defines the structure hostent, which will be used below.
void error(char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
The error() function is identical to that in the server, as are the variables sockfd, portno, and n. The variable serv_addr will contain the address of the server to which we want to connect. It is of type struct sockaddr_in.
The variable server is a pointer to a structure of type hostent. This structure is defined in the header file netdb.h as follows:
struct hostent
{
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses from name server */
#define h_addr h_addr_list[0] /* address, for backward compatiblity */
};
It defines a host computer on the Internet. The members of this structure are:
h_name Official name of the host.
h_aliases A zero terminated array of alternate names for the host.
h_addrtype The type of address being returned; currently always AF_INET.
h_length The length, in bytes, of the address.
h_addr_list A pointer to a list of network addresses for the named host. Host addresses are returned in network byte order.
Note that h_addr is an alias for the first address in the array of network addresses.
char buffer[256];
if (argc < 3)
{
fprintf(stderr,"usage %s hostname port", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
All of this code is the same as that in the server.
server = gethostbyname(argv[1]);
if (server == NULL)
{
fprintf(stderr,"ERROR, no such host");
exit(0);
}
The variable argv[1] contains the name of a host on the Internet, e.g. cs.rpi.edu. The function:
struct hostent *gethostbyname(char *name)
Takes such a name as an argument and returns a pointer to a hostent containing information about that host.
The field char *h_addr contains the IP address.
If this structure is NULL, the system could not locate a host with this name.
In the old days, this function worked by searching a system file called /etc/hosts but with the explosive growth of the Internet, it became impossible for system administrators to keep this file current. Thus, the mechanism by which this function works is complex, often involves querying large databases all around the country. The gethostbyname() man page has more information.
As before, we will go through the program client.c line by line.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
The header files are the same as for the server with one addition. The file netdb.h defines the structure hostent, which will be used below.
void error(char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
The error() function is identical to that in the server, as are the variables sockfd, portno, and n. The variable serv_addr will contain the address of the server to which we want to connect. It is of type struct sockaddr_in.
The variable server is a pointer to a structure of type hostent. This structure is defined in the header file netdb.h as follows:
struct hostent
{
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses from name server */
#define h_addr h_addr_list[0] /* address, for backward compatiblity */
};
It defines a host computer on the Internet. The members of this structure are:
h_name Official name of the host.
h_aliases A zero terminated array of alternate names for the host.
h_addrtype The type of address being returned; currently always AF_INET.
h_length The length, in bytes, of the address.
h_addr_list A pointer to a list of network addresses for the named host. Host addresses are returned in network byte order.
Note that h_addr is an alias for the first address in the array of network addresses.
char buffer[256];
if (argc < 3)
{
fprintf(stderr,"usage %s hostname port", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
All of this code is the same as that in the server.
server = gethostbyname(argv[1]);
if (server == NULL)
{
fprintf(stderr,"ERROR, no such host");
exit(0);
}
The variable argv[1] contains the name of a host on the Internet, e.g. cs.rpi.edu. The function:
struct hostent *gethostbyname(char *name)
Takes such a name as an argument and returns a pointer to a hostent containing information about that host.
The field char *h_addr contains the IP address.
If this structure is NULL, the system could not locate a host with this name.
In the old days, this function worked by searching a system file called /etc/hosts but with the explosive growth of the Internet, it became impossible for system administrators to keep this file current. Thus, the mechanism by which this function works is complex, often involves querying large databases all around the country. The gethostbyname() man page has more information.
/*initializes serv_addr to zeros*/
bzero((char *) &serv_addr, sizeof(serv_addr));
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
This code sets the fields in serv_addr. Much of it is the same as in the server. However, because the field server->h_addr is a character string, we use the function:
void bcopy(char *s1, char *s2, int length)
which copies length bytes from s1 to s2. ----
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
The connect function is called by the client to establish a connection to the server. It takes three arguments, the socket file descriptor, the address of the host to which it wants to connect (including the port number), and the size of this address. This function returns 0 on success and -1 if it fails. The connect() man page has more information.
Notice that the client needs to know the port number of the server, but it does not need to know its own port number. This is typically assigned by the system when connect is called.
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s",buffer);
return 0;
}
The remaining code should be fairly clear. It prompts the user to enter a message, uses fgets to read the message from stdin, writes the message to the socket, reads the reply from the socket, and displays this reply on the screen.
accept() system call
The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET)It extracts the first connection request on the queue of pending connections, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call.
Interview Questions on Socket Programming :
1. What is multicasting or multicast transmission? Which Protocol is generally used for multicast? TCP or UDP?
Multi-casting or multicast transmission is one to many distribution, where message is delivered to a group of subscribers simultaneously in a single transmission from publisher. Copies of messages are automatically created in other network elements e.g. Routers, but only when the topology of network requires it. Tibco Rendezvous supports multicast transmission. Multi-casting can only be implemented using UDP, because it sends full data as datagram package, which can be replicated and delivered to other subscribers. Since TCP is a point-to-point protocol, it can not deliver messages to multiple subscriber, until it has link between each of them. Though, UDP is not reliable, and messages may be lost or delivered out of order. Reliable multicast protocols such as Pragmatic General Multicast (PGM) have been developed to add loss detection and re-transmission on top of IP multicast. IP multicast is widely deployed in enterprises, commercial stock exchanges, and multimedia content delivery networks. A common enterprise use of IP multicast is for IPTV applications
2. What is sliding window protocol?
Sliding window protocol is a technique for controlling transmitted data packets between two network computers where reliable and sequential delivery of data packets is required, such as provided by Transmission Control Protocol (TCP). In the sliding window technique, each packet includes a unique consecutive sequence number, which is used by the receiving computer to place data in the correct order. The objective of the sliding window technique is to use the sequence numbers to avoid duplicate data and to request missing data
3. What is TIME_WAIT state in TCP protocol? When does a socket connection goes to TIME_WAIT state?
When one end of TCP Connection closes it by making system call, it goes into TIME_WAIT state. Since TCP packets can arrive in wrong order, the port must not be closed immediately to allow late packets to arrive. That's why that end of TCP connection goes into TIME_WAIT state. For example, if client closes a socket connection than it will go to TIME_WAIT state, similarly if server closes connection than you will see TIME_WAIT there. You can check status of your TCP and UDP sockets by using these networking commands in UNIX.
4. What will happen if you have too many socket connections in TIME_WAIT state on Server?
When a socket connection or port goes into TIME_WAIT state, it doesn't release file descriptor associated with it. File descriptor is only released when TIME_WAIT state is gone i.e. after some specified configured time. If too many connections are in TIME_WAIT state than your Server may ran out of file descriptors and start throwing "too many files open" error, and stop accepting new connections.
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
This code sets the fields in serv_addr. Much of it is the same as in the server. However, because the field server->h_addr is a character string, we use the function:
void bcopy(char *s1, char *s2, int length)
which copies length bytes from s1 to s2. ----
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
The connect function is called by the client to establish a connection to the server. It takes three arguments, the socket file descriptor, the address of the host to which it wants to connect (including the port number), and the size of this address. This function returns 0 on success and -1 if it fails. The connect() man page has more information.
Notice that the client needs to know the port number of the server, but it does not need to know its own port number. This is typically assigned by the system when connect is called.
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s",buffer);
return 0;
}
The remaining code should be fairly clear. It prompts the user to enter a message, uses fgets to read the message from stdin, writes the message to the socket, reads the reply from the socket, and displays this reply on the screen.
accept() system call
The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET)It extracts the first connection request on the queue of pending connections, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call.
Interview Questions on Socket Programming :
1. What is multicasting or multicast transmission? Which Protocol is generally used for multicast? TCP or UDP?
Multi-casting or multicast transmission is one to many distribution, where message is delivered to a group of subscribers simultaneously in a single transmission from publisher. Copies of messages are automatically created in other network elements e.g. Routers, but only when the topology of network requires it. Tibco Rendezvous supports multicast transmission. Multi-casting can only be implemented using UDP, because it sends full data as datagram package, which can be replicated and delivered to other subscribers. Since TCP is a point-to-point protocol, it can not deliver messages to multiple subscriber, until it has link between each of them. Though, UDP is not reliable, and messages may be lost or delivered out of order. Reliable multicast protocols such as Pragmatic General Multicast (PGM) have been developed to add loss detection and re-transmission on top of IP multicast. IP multicast is widely deployed in enterprises, commercial stock exchanges, and multimedia content delivery networks. A common enterprise use of IP multicast is for IPTV applications
2. What is sliding window protocol?
Sliding window protocol is a technique for controlling transmitted data packets between two network computers where reliable and sequential delivery of data packets is required, such as provided by Transmission Control Protocol (TCP). In the sliding window technique, each packet includes a unique consecutive sequence number, which is used by the receiving computer to place data in the correct order. The objective of the sliding window technique is to use the sequence numbers to avoid duplicate data and to request missing data
3. What is TIME_WAIT state in TCP protocol? When does a socket connection goes to TIME_WAIT state?
When one end of TCP Connection closes it by making system call, it goes into TIME_WAIT state. Since TCP packets can arrive in wrong order, the port must not be closed immediately to allow late packets to arrive. That's why that end of TCP connection goes into TIME_WAIT state. For example, if client closes a socket connection than it will go to TIME_WAIT state, similarly if server closes connection than you will see TIME_WAIT there. You can check status of your TCP and UDP sockets by using these networking commands in UNIX.
4. What will happen if you have too many socket connections in TIME_WAIT state on Server?
When a socket connection or port goes into TIME_WAIT state, it doesn't release file descriptor associated with it. File descriptor is only released when TIME_WAIT state is gone i.e. after some specified configured time. If too many connections are in TIME_WAIT state than your Server may ran out of file descriptors and start throwing "too many files open" error, and stop accepting new connections.
A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.
ReplyDeletewebsite: geeksforgeeks.org
A Computer Science portal for geeks. It contains well written, well thought and well
ReplyDeleteexplained computer science and programming articles, quizzes and practice/competitive
programming/company interview Questions.
website: geeksforgeeks.org