How to Create a Basic Client-Server Connection in C Part-2
Hello everybody. I'm back with Part-2 of this series. In this part we will be programming in C our server and i will be explaining one more "key concept" so everybody got a better understanding on this.
Ok first let's see that concept.
So, as people should know, memory is one big array that contains bytes. Each position of that said array can be refered as an address.
Now let's say we want to represent 50,AB,FF,30 on the memory. There are two ways to do that:
In Little Endian we store the least significative byte on the smallest address.
It should be represented in memory like this:
In Big Endian we store the most significative byte on the smallest address and it should be represented in memory like this:
We need to know this because on the network the byte order is in Big Endian and most of the CPU's we use, like intel, used the Little Endian convention.
Know let's start coding. I'm going to post all the code on this link http://pastebin.com/TnmwLszE.
So we have some libraries here that some people might not know for what is used so i'm going to explain those.
unistd.h -> Necessary so we can use functions read() and write()
sys/types.h -> This one as information needed for the next 2 headers.
sys/socket.h -> Contain all the structs necessary to create a connection
netinet/in.h -> Contain the structs and constants necessary for IP addresses
This function receives a message and uses the function perror() to show it. I decided to use perror() so i can see a interpreted error instead of the "system default" error.
First we will declare our variables:
sock_fd e new_sock_fd -> Socket descriptors entry (covered in Part-1)
port -> Port number
clilen -> Store the lenght of the address of the client
n -> This little guy will have the return value of the read() and write() calls or in other words it will have the number of charaters of a string.
buffer -> It will read the characters sent on the connection with the server.
sockaddr_in -> It's a struct which gives you the ability to store IP's.
serv_addr -> Store the IP of the server
cli_addr -> Store the IP of the client
Now we will make sure that the port is passed on argv when we execute this program
Now we will create a socket with the function socket()
AF_INET-> Creates a socket using the address family that is used to designate the type of addresses that our socket can communicate with (in this case, IPv4 addresses)
SOCK_STREAM -> Tells the socket to use the TCP protocol (already covered in part-1)
0 -> If this argument is set to zero, the OS chooses the best protocol to be used.
Next, we will test if everything goes well with the last line of code
If sock_fd fails it will return -1.
We are going to use a function called bzero to set serv_addr to zeros.
Next we can give to variable port the port number
Now it's time to use our sockaddr_in struct.
This struct as four fields/
sin_family -> What family of addresses are we using (explained above in AF_INET)
sin_port -> Here we do the conversion of Little Endian to Big Endian using the function htons(). We are using this function because we need to pass to this structure field a unsigned short integer. If we needed to pass a integer we would use the htonl() function.
sin_addr.s_addr -> Sin_addr contains a unique field called s_addr which is a unsigned long where we pass the IP of the host. In this case (since it's a server) this will always be the IP of the machine where the server is. For that we have INADDR_ANY which stores that address.
Now we will use the function bind() to well bind the socket to the IP
Bind as 3 arguments.
The first one is the socket descriptor
The second one is the address that it will be binded. Since our argument (serv_addr) is a pointer to the struct sockaddr but what we need here is the struct sockaddr_in. So we use a cast to put him on the correct type.
The third one is the size of the address that it will be binded.
if this function returns a value less than zero it means something went wrong.
Now we are going to make the server "listen" to connections
First argument is the socket descriptor and the second one is the number of connections that can be waiting while the server is receiving a connection. Most systems allow up to 5 connections
Now that we are listening we need to accept the incoming connections
First we will give clilen the size of the address of the client to pass on our function accept().
Second when we accept we create another socket which will need a socket descriptor, the address of the client (same thing as before on the function bind(), we need to make a cast for the same reason) and the size of the address of the client.
Next if it fails it will return a value of less than zero and it will produce an error.
Notice that the function accept() will block the process until a client connects.
After a client connects we prepare our buffer to receive a message
So as before bzero() will set the buffer positions to zero.
The variable n will store the number of characters that will be read.
Of course we will need to pass as arguments the new socket, the buffer and is positions minus one.
Next we will test it out
If n stored a value less than zero it will produce an error if not it will show you the message the client sent.
If all goes well at this point we will just reply to the client saying that we received is message
The write() function will receive as arguments the new socket, the message and the number of characters of that message
Again we store that on the variable n and if n stored a value less than zero it will produce an error, if not the program ends.
That's it! We made a server using C language. Thanks for reading, hope people have enjoyed it and as always if i made any mistake feel free to correct me. Part-3 will be coming soon.