How to Create a Basic Client-Server Connection in C Part-2

Apr 24, 2016 01:46 PM
635970676672933164.jpg

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.

Little Endian and Big Endian

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:

Little Endian:

In Little Endian we store the least significative byte on the smallest address.

It should be represented in memory like this:

Address:1000

Value: 30

Address:1001

Value:FF

Address:1002

Value:AB

Address:1003

Value:50

Big Endian:

In Big Endian we store the most significative byte on the smallest address and it should be represented in memory like this:

Address:1000

Value: 50

Address:1001

Value: AB

Address:1002

Value:FF

Address:1003

Value:30

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.

Step 1: Write the Libraries We Need

635970676672933164.jpg

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

Step 2: Write a Simple Error Message

635970679108883920.jpg

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.

Step 3: Let's Build or Main() Function

First we will declare our variables:

635970682632424555.jpg

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

635970703076466264.jpg

Now we will create a socket with the function socket()

635970706375809503.jpg

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

635970713003396546.jpg

If sock_fd fails it will return -1.

We are going to use a function called bzero to set serv_addr to zeros.

635970716257091648.jpg

Next we can give to variable port the port number

635970718111510440.jpg

Now it's time to use our sockaddr_in struct.

This struct as four fields/

635970726561477062.jpg

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

635970734689478965.jpg

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

635970741843691773.jpg

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

635970749236635972.jpg

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

635970754398832165.jpg

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

635970760107276759.jpg

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

635970763708273495.jpg

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.

Ending

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.

Comments

No Comments Exist

Be the first, drop a comment!