One thing which I forgot to mention in last article was about the characterstic of a socket.Socket can be in two types:
- Active Socket: This socket is the default one and this socket initiates the connection .
- Passive Socket: This socket relaxes and waits for incoming connection if any...
Bind
Bind function is used to assign a local protocol address to a socket.
It uses
- a socket descriptor (sockfd) created by socket function
- uses protocol specific address and size( myaddr, addrlen)
According to man page of bind Here name is used in concern with the protocol address.
The function format is:
#include <sys/socket.h>
int bind( int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
|
This function returns 0 on success and -1 on error.
Using bind the servers bind to their well known ports and it restricts the socket to receive connections destined only to that IP address when they start. Important point to note is TCP client doesnt bind to a port number!! TCP client assigns source IP address for sending datagrams.
However,if we dont specify the IP address and port for client(i.e., if we give IPADDR_ANY which is defined in in IP address field) then the kernel will automatically choose the IP address and port .In another condition if TCP server doesnt bind to the IP address to its socket.The kernel chooses the destination IP address of the clients SYN packet as the servers source IP address.
A common error which is returned from bind function call is EADDRINUSE which says Address already in use to avoid this we can use
SO_REUSEADDR: It allows a listening server to start and bind to its well known port, even if there are previously established connections that uses this port as their local port. This is common for a site hosting multiple HTTP servers using the IP alias technique.In UDP sockets this function allows duplicate bindings i.e, a bind of an IP address and port, when the same IP address and port are already bound to another socket,if the transport protocol supports it.
SO_REUSEPORT: This option is used in multicasting to prevent overloading of SO_REUSEADDR function.The problem with this option is that all the systems doesn’t support it .It is equivalent to SO_REUSEADDR if multicasting is used.
Listen
This function is called by a TCP Server only and by using it, server wants to say "Hey all I am waiting for any query at my specified port, does any one wanna connect !!".It converts an unconnected socket into passive socket, telling to the kernel that it should accept incoming connections on this socket.
This function is normally called after both the socket and bind functions and should be called before connect function.
There are two queses maintained in this process: complete connection queue and incomplete connection queue and now you'll ask what is the difference in them??
These are the two queues which kernel maintains for a TCP socket in listening state.
Incomplete connection queue contains an entry for each SYN packet that has arrived from client for which server is awaiting completion of TCP three-way handshaking. If you know about the TCP state diagram then for additional information I will let u know that at this point the sockets are in SYN_RCVD state .
Complete connection queue contains an entry for each client with which the three way handshaking is done.
The listen function format is:
#include <sys/socket.h>
int listen(int sockfd,int backlog);
|
New thing here in this function is backlog it specifies the maximum number of connections the kernel should queue for this socket. Never specify backlog argument as 0 because different systems interpret this differently if you dont want to clients connecting to your listening socket, close it.
The function will return 0 if succeed and -1 if any error occurs.
Accept
Accept function returns the next completed connection from the front of the completed connection queue. There can be two conditions in this case :
- If the completed connection queue is empty then the process goes in sleeping state.
- If the accept call is successful then a new socket descriptor is returned (newsockfd)
The function format is:-
#include <sys/socket.h>
int accept(int sockfd,const struct sockaddr *clnt_addr,socklen_t addrlen);
|
Here,
Sockfd : Socket file descriptor
Clnt_addr : socket address of client ,
Addrlen is : the address length of the socket structure
Function return value is a new socket descriptor on success and -1 on error condition.
Connect
Used by TCP client to establish a connection to the server.It uses a socket descriptor (sockfd) created by socket function and IP and port number of the server.
Clients need not to call bind function prior to connect function (basically it depends whether you are using connection oriented and connection less protocol to communicate).So what happens when connect is called !!!
The kernel assigns a new ephermal port and source IP if necessary.
The function format is :
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen);
|
In case of TCP socket, the connect function initiates TCPs three-way handshaking. This function returns only when the connection is established (i.e., 0 on success), else an error is returned. There are different errors return can be possible in this case:
- If the client TCP receives no response to its SYN segment, ETIMEDOUT is returned. 4.4BSD, for example, sends one SYN when connect is called, another 6 seconds later, and another 24 seconds later. If no response is received after a total of 75 seconds, the error is returned. Some systems provide administrative control over this timeout.
- If the server response to the client SYN packet (packet send to initiate the connection) is RST (Reset), this indicates that there are no process on the server at the specified port which is waiting for connection which may be the condition when the server is not running. This is a hard error and an error ECONNREFUSED is returned to the client as soon as RST is received.
So now there may be one more thing which you find interesting i.e., the difference between listening socket and connected socket: when we call accept then in the first argument we give the socket descriptor created by socket function this socket is called listening socket which we use in bind and listen function calls now as we know that return value of the accept function is another new socket , this socket is different from socket which is created from socket function , this is called a connected socket.
Now before proceeding to the code of Server and client one more thing which I would like to mention is there are two types of servers:
- Iterative Servers: These are the servers which at a time can accept connection from a single client only.
- Concurrent Server: These servers can accept and process multiple client requests simultaneously.
Now here is I will show you how to implement a simple UDP client and Server.
The server is:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define MYPORT 4950 /* the port clients will be connecting to */
#define MAXBUFLEN 100 /* Max value of buffer to store the input */
int main()
{
int sockfd;
struct sockaddr_in my_addr; /* Server address information */
struct sockaddr_in their_addr; /* Client's address information */
int addr_len, numbytes;
char buf[MAXBUFLEN];
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 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-fill with my IP */
bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1) {
perror("bind");
exit(1);
}
addr_len = sizeof(struct sockaddr); /* calculate the address length of socket */
if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN, 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom"); /* if the message cannot be received properly give error*/
exit(1);
}
printf("got packet from %sn",inet_ntoa (their_addr. sin_addr));
printf("packet is %d bytes longn",numbytes );
buf[numbytes] = ' ';
printf("packet contains "%s "n",buf );
close(sockfd);
}
|
Now we’ll see the client :
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define MYPORT 4950 /* the port users will be connecting to */
int main(int argc, char *argv[])
{
int sockfd; /* socket descriptor */
struct sockaddr_in their_addr; /* Server address information */
struct hostent *he; /* structure to collect the host information */
int numbytes;
if (argc != 3) {
fprintf(stderr,"usage: client hostname messagen"); /* Give the proper argument baby !!*/
exit(1);
}
if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */
herror("gethostbyname");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket"); /* Oops some error occured !*/
exit(1);
}
their_addr.sin_family = AF_INET; /* host byte order */
their_addr.sin_port = htons(MYPORT); /* short, network byte order */
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */
if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0,
(struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1) {
perror("sendto");
exit(1);
}
printf("sent %d bytes to %sn",numbytes,inet_ntoa (their_addr. sin_addr));
close(sockfd);
return 0;
}
|
Now run the server first and then client , Here is the output
Run the server , it will remain in blocking state till it doesnt get any request as soon as any request comes the server will give the following output
[root@basileis sock_prog]# ./server
got packet from 127.0.0.1
packet is 19 bytes long
packet contains "Heya i sent a packet"
|
Run the client as shown below with IP address( I have given loopback) and the message to print
[root@basileis sock_prog]# ./client 127.0.0.1 "Heya i sent a packet"
sent 19 bytes to 127.0.0.1
|
More we will discuss in next article...