Networking in UNIX
Network programming under UNIX is relatively simple in C.
This guide assumes you already have a good general idea about C, UNIX and networks.
A simple client
To start with we'll look at one of the simplest things you can do, initialize a stream connection and receive a message from a remote server.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MAXRCVLEN 500
#define PORTNUM 2343
#define PORTNUM 2343
int main(int argc, char *argv[])
{
char buffer[MAXRCVLEN+1]; /* +1 so we can add null terminator */ int len, mysocket;
struct sockaddr_in dest;
mysocket = socket(AF_INET, SOCK_STREAM, 0);
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1"); /* Sets destination IP number */ dest.sin_port = htons (PORTNUM); /* Sets destination port number */
memset(&(dest.sin_zero), '\0', 8); /* Zeroes rest of struct */
memset(&(dest.sin_zero), '\0', 8); /* Zeroes rest of struct */
connect(mysocket, (struct sockaddr *)&dest,sizeof(struct sockaddr)); len=recv(mysocket, buffer, MAXRCVLEN, 0);
buffer[len]='\0'; /* We have to null terminate the received data ourselves */ printf("Rcvd: %s",buffer);
close(mysocket); /* Close the file descriptor when finished */ return EXIT_SUCCESS;
}
This is the very bare bones of a client; in practice, we would check every function that we called for failure, however for clarity, error checking the code has been left out for now.
As you can see, the code mainly resolves around "dest" which is a struct of type sockaddr_in; in this struct we store information about the machine we want to connect to.
mysocket = socket(AF_INET, SOCK_STREAM, 0);
The socket function tells our OS that we want a file descriptor for a socket which we can use for a network stream connection , what the parameters mean is mostly irrelevent for now.
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1"); /*Sets destination IP number*/ dest.sin_port = htons(PORTNUM); /*Sets destination port number*/
memset(&(dest.sin_zero), '\0', 8); /*Zeroes rest of struct*/
memset(&(dest.sin_zero), '\0', 8); /*Zeroes rest of struct*/
Now we get on to the interesting part,
The first line sets the address family, this should be the same as was used in the socket function, for most purposes AF_INET will serve.
The second line is where we set the IP of the machine we need to connect to. The variable
dest.sin_addr.s_addr is just an integer stored in Big Endian format, but we don't have to know that as the inet_addr function will do the conversion from string into Big Endian integer for us.
The third line sets the destination port number, the htons() function converts the port number into a Big Endian short integer. If your program is going to be solely run on machines which use Big
Endian numbers as default then dest.sin_port = 21; would work just as well, however for portability reasons htons() should be used.
Endian numbers as default then dest.sin_port = 21; would work just as well, however for portability reasons htons() should be used.
The fourth line uses memset to zero the rest of the struct.
Now that's all the preliminary work done, now we can actually make the connection and use it,
connect(mysocket, (struct sockaddr *)&dest,sizeof(struct sockaddr));
This tells our OS to use the socket mysocket to create a connection to the machine specified in dest.
inputlen=recv(mysocket, buffer, MAXRCVLEN, 0);
Now this receives upto MAXRCVLEN bytes of data from the connection and stores them in the
buffer string. The number of characters received is returned by recv(). It is important to note that the data received will not automatically be null terminated when stored in buffer hence we need to do it ourselves with buffer[inputlen]='\0'.
And that's about it !
The next step after learning how to receive data is learning how to send it, if you've understood the
previous section than this is quite easy, all you have to do is used the the send() function which uses
the same parameters as receive. If in our previous example "buffer" had the text we wanted to send
and its length was stored in "len" we would do send(mysocket, buffer, len, 0), send() returns the
number of bytes that were sent. It is important to remember send() for various reasons may not be
able to send all of the bytes so it is imporant to check that this value is equal to the number of bytes
you were sending (in most cases this can be resolved by resending the unsent data).
previous section than this is quite easy, all you have to do is used the the send() function which uses
the same parameters as receive. If in our previous example "buffer" had the text we wanted to send
and its length was stored in "len" we would do send(mysocket, buffer, len, 0), send() returns the
number of bytes that were sent. It is important to remember send() for various reasons may not be
able to send all of the bytes so it is imporant to check that this value is equal to the number of bytes
you were sending (in most cases this can be resolved by resending the unsent data).
A simple server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define PORTNUM 2343
int main(int argc, char *argv[])
{
char buffer[]="Hello World !\n";
int socksize; /*we use this to store the size of out sockaddr_in struct */ int mysocket; /* The socket we use to listen with */
int consocket; /* Once we have a connection we get a "session" socket to use */ struct sockaddr_in dest; /* Info on the machine connecting to us */
struct sockaddr_in serv; /* Info on our server */
socksize = sizeof(struct sockaddr_in);
mysocket = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET;
serv.sin_addr.s_addr = INADDR_ANY; /* Set the IP to our IP (given by INADDR_ANY) */
serv.sin_port = htons(PORTNUM); /*Sets server port number*/
memset(&(dest.sin_zero), '\0', 8); /*Zeroes rest of struct*/
bind(mysocket, (struct sockaddr *)&serv,sizeof(struct sockaddr)); /*bind serv information to mysocket */
listen(mysocket,1); /* start listening, allow a queue of upto 1 pending connection*/
while(1)
{
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize); /* Get data the program waits here for a connection*/
printf("Incoming connection from %s sending welcome\n",inet_ntoa(dest.sin_addr));
send(consocket, buffer, strlen(buffer), 0);
close(consocket);
send(consocket, buffer, strlen(buffer), 0);
close(consocket);
}
close(mysocket);
return EXIT_SUCCESS;
}
}
Superficially this is very similar to the client, the first important difference is that rather than
creating a sockaddr_in with information about the machine we're connecting to we create it with
information about the server, and then we "bind" it to the socket. This allows the machine to know
the data recieved on the port specified in the sockaddr_in should be handled by our specified socket.
information about the server, and then we "bind" it to the socket. This allows the machine to know
the data recieved on the port specified in the sockaddr_in should be handled by our specified socket.
The listen command then tells our program to start listening using that socket, the second parameter of listen() allows us to specify the maximum number of connections that can be queued. Each time a connection is made to the server it is added to the queue, we take connections from the queue using
the accept() function.
the accept() function.
Then we have the core of the server code, we use the accept() function to take a connection from the queue, if there is no connection waiting on the queue the function causes the program to wait until a connection is received. The accept() function returns us another socket this is essentially a "session"
socket solely for communicating with connection we took of the queue. The original socket (mysocket) continues to listen on the prespecified port for further connections.
Once we have "session" socket we can treated it in the same way as with the client using send() and receive() to handle data transfers.
Note that this server can only accept one connection at a time, if you want to simultaneously handle
multiple clients then you'll need to use forking off seperate processes to handle the connections.
multiple clients then you'll need to use forking off seperate processes to handle the connections.
Useful network functions
int gethostname(char *hostname, size_t size);
This function takes as parameter a pointer to an array of chars and the size of that array, if possible
it then finds out the hostname of the localhost and stores it in the array. On failure it returns 1.
it then finds out the hostname of the localhost and stores it in the array. On failure it returns 1.
struct hostent *gethostbyname(const char *name);
This function obtains information about a domain name and stored it in a hostent struct, in the
hostent structure the most useful part is the (char**) h_addr_list field, this is a null terminated array of IP addresses associated with that domain. The field h_addr is a pointer to the first IP address in the h_addr_list array. Returns NULL on failure.
FAQs
What about stateless connections ?
If you don't want to exploit the properties of TCP in your program but rather just have to use a UDP protocol then you can just switch "SOCK_STREAM" in your socket call for "SOCK_DGRAM" and use it in the same way. It is important to remember that UDP does not guarantee delivery of packets and order of delivery, so checking is important.
If you want to exploit the properties of UDP, then you can use sendto() and recvfrom() which
operate like send() and recv() except you need to provide extra parameters specifiying who you are communicating with.
You should have more info on correcting a failed condition. I have a network connection that is lost. I was looking for the recovery methods. Maybe some information on "netstat a | grep ##.##.##.##"
would help. Thanks
0 comments:
Post a Comment