Scripting‎ > ‎C examples‎ > ‎

Networking and socket programing in C

Thank you for visiting this page, this page has been update in another link Networking and socket programming in C
I spent some time on TCP tuning on linux, see link TCP performance tuning, then I thought that people may not know how to verify how much window size their application is picking.
So, I wanted to write a C program to verify the configuration. Then, googlling tells me that there are lots of people have done it already. Here is a one I think it looks easy and good.

In case it disappears,  I past the whole article to the bottom of this page, thanks to

One thing need to mention is that the default window size is from net.core.rmem_default, which defined for all protocols. Default is 87380 bytes. But for TCP, it is overwritten by the parameter tcp_rmem.
Here is the full explain of the parameter from TCP man page.
       tcp_rmem (since Linux 2.4)
              This is a vector of 3 integers: [min, default, max].  These
              parameters are used by TCP to regulate receive buffer sizes.
              TCP dynamically adjusts the size of the receive buffer from
              the defaults listed below, in the range of these values,
              depending on memory available in the system.

              min       minimum size of the receive buffer used by each TCP
                        socket.  The default value is the system page size.
                        (On Linux 2.4, the default value is 4K, lowered to
                        PAGE_SIZE bytes in low-memory systems.)  This value
                        is used to ensure that in memory pressure mode,
                        allocations below this size will still succeed.
                        This is not used to bound the size of the receive
                        buffer declared using SO_RCVBUF on a socket.

              default   the default size of the receive buffer for a TCP
                        socket.  This value overwrites the initial default
                        buffer size from the generic global
                        net.core.rmem_default defined for all protocols.
                        The default value is 87380 bytes.  (On Linux 2.4,
                        this will be lowered to 43689 in low-memory
                        systems.)  If larger receive buffer sizes are
                        desired, this value should be increased (to affect
                        all sockets).  To employ large TCP windows, the
                        net.ipv4.tcp_window_scaling must be enabled

              max       the maximum size of the receive buffer used by each
                        TCP socket.  This value does not override the global
                        net.core.rmem_max.  This is not used to limit the
                        size of the receive buffer declared using SO_RCVBUF
                        on a socket.  The default value is calculated using
                        the formula

                            max(87380, min(4MB, tcp_mem[1]*PAGE_SIZE/128))

                        (On Linux 2.4, the default is 87380*2 bytes, lowered
                        to 87380 in low-memory systems).

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> #define MAXRCVLEN 500 #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); memset(&dest, 0, sizeof(dest)); /* zero the struct */ dest.sin_family = AF_INET; dest.sin_addr.s_addr = inet_addr(""); /* set destination IP number */ dest.sin_port = htons(PORTNUM); /* set destination port number */ connect(mysocket, (struct sockaddr *)&dest, sizeof(struct sockaddr)); len = recv(mysocket, buffer, MAXRCVLEN, 0); /* We have to null terminate the received data ourselves */ buffer[len] = '\0'; printf("Received %s (%d bytes).\n", buffer, len); close(mysocket); return EXIT_SUCCESS; }

This is the very bare bones of a client; in practice, we would check every function that we call for failure, however, error checking has been left out for clarity.

As you can see, the code mainly revolves around dest which is a struct of type sockaddr_in. This struct stores 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 irrelevant for now.

memset(&dest, 0, sizeof(dest));                /* zero the struct */
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr(""); /* set destination IP number */ 
dest.sin_port = htons(PORTNUM);                /* set destination port number */

Now we get on to the interesting part:

The first line uses memset() to zero the struct.

The second line sets the address family. This should be the same value that was passed as the first parameter to socket(); for most purposes AF_INET will serve.

The third 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 fourth 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 run solely 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 always be used.

Now that all of the preliminary work is done, 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.

len = recv(mysocket, buffer, MAXRCVLEN, 0);

Now this receives up to 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 the buffer, so 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 then this is quite easy. All you have to do is use the send() function, which uses the same parameters as recv(). If in our previous example buffer had the text we wanted to send and its length was stored in len we would write send(mysocket, buffer, len, 0). send() returns the number of bytes that were sent. It is important to remember that send(), for various reasons, may not be able to send all of the bytes, so it is important to check that its return value is equal to the number of bytes you tried to send. 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> #define PORTNUM 2343 int main(int argc, char *argv[]) { char msg[] = "Hello World !\n"; struct sockaddr_in dest; /* socket info about the machine connecting to us */ struct sockaddr_in serv; /* socket info about our server */ int mysocket; /* socket used to listen for incoming connections */ socklen_t socksize = sizeof(struct sockaddr_in); memset(&serv, 0, sizeof(serv)); /* zero the struct before filling the fields */ serv.sin_family = AF_INET; /* set the type of connection to TCP/IP */ serv.sin_addr.s_addr = htonl(INADDR_ANY); /* set our address to any interface */ serv.sin_port = htons(PORTNUM); /* set the server port number */ mysocket = socket(AF_INET, SOCK_STREAM, 0); /* bind serv information to mysocket */ bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr)); /* start listening, allowing a queue of up to 1 pending connection */ listen(mysocket, 1); int consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize); while(consocket) { printf("Incoming connection from %s - sending welcome\n", inet_ntoa(dest.sin_addr)); send(consocket, msg, strlen(msg), 0); consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize); } 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 received on the port specified in the sockaddr_in should be handled by our specified socket.

The listen() function then tells our program to start listening using the given 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. If there is no connection waiting on the queue the program waits until a connection is received. The accept() function returns another socket. This socket is essentially a "session" socket, and can be used solely for communicating with connection we took off the queue. The original socket (mysocket) continues to listen on the specified port for further connections.

Once we have "session" socket we can handle it in the same way as with the client, using send() and recv() 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 fork() off separate processes, or use threads, to handle the connections.

Useful network functions

int gethostname(char *hostname, size_t size);

The parameters are a pointer to an array of chars and the size of that array. If possible, it finds the hostname 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 stores it in a hostent struct. The most useful part of a hostent structure is the (char**) h_addr_list field, which is a null terminated array of the 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.


What about stateless connections?

If you don't want to exploit the properties of TCP in your program and would rather just use a UDP connection, then you can just replace SOCK_STREAM with SOCK_DGRAM in your call to socket() and use the result 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 specifying who you are communicating with.

How do I check for errors?

The functions socket(), recv() and connect() all return -1 on failure and use errno for further details.