UNIX Network Programming

W. Richard Stevens

Mentioned 7

A practical book that explains many of the details that have been considered a mystery, this guidebook focuses on the design, development, and coding of networking software under the UNIX operating system. It begins by showing how a fundamental basic for networking programming is interprocess communication (IPC), and a requisite for understanding IPC is a knowledge of what constitutes a process. Throughout, the text provides both a description and examples of how and why a particular solution is arrived at.

More on Amazon.com

Mentioned in questions and answers.

I've read a number of articles about UDP packet sizes but have been unable to come to a conclusion on whats correct.

A number of services restrict the largest UDP packet to 512 bytes (like dns)

Given the minimum MTU on the internet is 576 , and the size of the IPv4 header is 20 bytes, and the UDP header 8 bytes. This leaves 548 bytes available for user data

Would I be able to use packets up to the size of 548 without packet fragmentation? Or is there something the creators of DNS knew about, and that why they restricted it to 512 bytes.

Could I even go higher than 548 bytes safely?

IPv4 minimum reassembly buffer size is 576, IPv6 has it at 1500. Subtract header sizes from here. See UNIX Network Programming by W. Richard Stevens :)

Over the last couple of months I've been working on some implementations of sockets servers in C++ and Java. I wrote a small server in Java that would handle & process input from a flash application hosted on a website and I managed to successfully write a server that handles input from a 2D game client with multiple players in C++. I used TCP in one project & UDP in the other one. Now, I do have some questions that I couldn't really find on the net and I hope that some of the experts could help me. :)

Let's say I would like to build a server in C++ that would handle the input from thousands of standalone and/or web applications, how should I design my server then? So far, I usually create a new & unique thread for each user that connects, but I doubt this is the way to go.

Also, How does one determine the layout of packets sent over the network; is data usually sent over the network in a binary or text state? How do you handle serializated objects when you send data to different media (eg C++ server to flash application)?

And last, is there any easy to use library which is commonly used that supports portability (eg development on a windows machine & deployment on a linux box) other than boost asio.

Thank you.

As far as server design concern, I would say that you are right: although ONE-THREAD-PER-SOCKET is a simple and easy approach, it is not the way to go since it won't scale as well as other server design patterns.

I personally like the COMMUNICATION-THREADS/WORKER-THREADS approach, where a pool of a dynamic number of worker threads handle all the work generated by producer threads.

In this model, you will have a number of threads in a pool waiting for tasks that are going to be generated from another set of threads handling network I/O.

I found UNIX Network Programming by Richard Stevens and amazing source for this kind on network programming approaches. And, despite its name, it will be very useful in windows environments as well.

Regarding the layout of the packets (you should have post a different question for this since it is a totally different question, in my opinion), there are tradeoffs when selecting TEXT vs BINARY approach.

TEXT (i.e. XML) is probably easier to parse and document, and more simple in general, while a BINARY protocol should give you better performance in terms of speed of processing and size of network packets, but you will have to deal with more complicated issues such as ENDIANNES of the words and stuff like that.

Hope it helps.

I was going through the classic book Unix Network Programming, when I stumbled upon this program (Section 6.8, page 179-180)

#include    "unp.h"

int
main(int argc, char **argv)
{
    int                 i, maxi, maxfd, listenfd, connfd, sockfd;
    int                 nready, client[FD_SETSIZE];
    ssize_t             n;
    fd_set              rset, allset;
    char                buf[MAXLINE];
    socklen_t           clilen;
    struct sockaddr_in  cliaddr, servaddr;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(SERV_PORT);

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    Listen(listenfd, LISTENQ);

    maxfd = listenfd;           /* initialize */
    maxi = -1;                  /* index into client[] array */
    for (i = 0; i < FD_SETSIZE; i++)
        client[i] = -1;         /* -1 indicates available entry */
    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);

    for ( ; ; ) {
        rset = allset;      /* structure assignment */
        nready = Select(maxfd+1, &rset, NULL, NULL, NULL);

        if (FD_ISSET(listenfd, &rset)) {    /* new client connection */
            clilen = sizeof(cliaddr);
            connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

            for (i = 0; i < FD_SETSIZE; i++)
                if (client[i] < 0) {
                    client[i] = connfd; /* save descriptor */
                    break;
                }
            if (i == FD_SETSIZE)
                err_quit("too many clients");

            FD_SET(connfd, &allset);    /* add new descriptor to set */
            if (connfd > maxfd)
                maxfd = connfd;         /* for select */
            if (i > maxi)
                maxi = i;               /* max index in client[] array */

            if (--nready <= 0)
                continue;               /* no more readable descriptors */
        }

        for (i = 0; i <= maxi; i++) {   /* check all clients for data */
            if ( (sockfd = client[i]) < 0)
                continue;
            if (FD_ISSET(sockfd, &rset)) {
                if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
                        /*4connection closed by client */
                    Close(sockfd);
                    FD_CLR(sockfd, &allset);
                    client[i] = -1;
                } else
                    Writen(sockfd, buf, n);

                if (--nready <= 0)
                    break;              /* no more readable descriptors */
            }
        }
    }
}    

The author mentions that this program is not safe against DOS attack. Quoting from the book,

"Unfortunately, there is a problem with the server that we just showed. Consider what happens if a malicious client connects to the server, sends one byte of data (other than a newline), and then goes to sleep. The server will call read (system call), which will read the the single byte of data from the client and then block in the next call to read, waiting for more data from this client. The server is then blocked by this one client, and will not service any other clients until malicious client either sends a newline or terminates"

I am not sure if I understand this correctly. Why will the read system call be called the second time for this malicious client, since it only sent 1 byte of data, that gets notified by the first call to select. The subsequent calls to select will never have this malicious file descriptor set as there is no activity. Am I missing something here?

My guess here is that there is a typo in the code, instead of Read, it should be some version of Readline method mentioned at other places in the book.

Note: The code contains Read and Select (with capital R and S), which are nothing but error handled wrappers of read and select system call

Yes, it seems likely that it was intended to be Readline.

In the downloadable source code that file is tcpcliserv/tcpservselect01.c and there is a corresponding .lc file (with line number annotations) which uses Readline instead of Read, and it was Readline in the second edition of the book (source code). About the only way to make sense of the parenthetic comment "(other than a newline)" is to assume that the intended read function reads up to a newline.

Oddly, it hasn't been reported in the errata. Maybe you should do so.

This must be really obvious, but how do I make a winsock listen to a specific port (recieve data?) I cannot find a method that lets me do so!

Sorry that this is probably so obvious.

I am trying to make a chat application, which is obviously running in a server/client setup.

When using TCP sockets you bind, listen and accept and eventually recv. You can find some tutorials over here or check out the official MSDN documentation.

The winsock API is based on UNIX' BSD sockets; so they are very very similar. You might want to look into that. A book that I've recently is UNIX Network Programming by Richard Stevens and he explains pretty much everything there is to know about sockets.

You might also consider running your sockets in a separate thread to improve readability and performance.

Edit: As for listening on a specific port; this is the sin.port parameter of your SOCKADDR_IN struct.

I have Intel Galileo Board ("low" performance pc) and PC ("fast" performance) linked with Ethernet. I wrote UDP server on Galileo and UDP client on PC. Server receives udp packets and accumulates their amount in variable count. Part of code:

static void recvfrom_int(int);
static long count;
unsigned long long received = 0;     

static void recvfrom_int(int signo) {
    printf("\nreceived %d datagrams\n", count);
    printf("\nreceived bytes: %d\n", received);
    exit(0);
} 

int main () {  
    signal(SIGINT, recvfrom_int);        
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;    
    char mesg[MAXLINE];        
    ...
    ...
    sockfd = socket (AF_INET, SOCK_DGRAM, 0); 
    ...
    ...
    count = 0;     
    for ( ; ; ) {    
        received += recvfrom(sockfd, mesg, MAXLINE, 0, (struct sockaddr *)&cliaddr, &len);
        count++;        
    }        
}

And i have client code, which send NDG(50000) datagrams, each datagram has size DGLEN. Part of client:

#define MAXLINE 90000
#define NDG 50000 /* datagrams to send */    
#define DGLEN 3100 /* each datagram size */

int main () {
    int sockfd;
    struct sockaddr_in servaddr;
    char sendline[MAXLINE];   
    int i;
    int n = 0;
    ...
    ...
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    ...
    //SENDING DATAGRAMS
    for (i = 0; i < NDG; i++) {
        n = write(sockfd, sendline, DGLEN);
    }

}

Similar code can be seen Stevens book, chapter 8.13. http://www.amazon.com/UNIX-Network-Programming-Richard-Stevens/dp/0139498761

My results:

DGLEN (DATAGRAM SIZE in bytes)     COUNT (amount of datagrams, RECEIVED ON SERVER)
   10                                 67
   50                                 67
   100                                67
   200                                68
   300                                68
   400                                69
   500                                94
   600                              1067
   700                             12799  
   800                             25884
   900                             35542
  1000                             49957
  1200                        near 50000
  1300                        near 50000
  1400                        near 50000
  1472                             48338
  1473                                71

I don't undertstand what it means. I think, that small count with DGLEN = 1473, it is because ethernet MTU = 1500, and maximum size of datagram without fragmentation is 1472 (1500 Ethernet MTU - 20 for IP header without options - 8 UDP header = 1472). With size 1473 we obtain fragmentation, which leads to small count of received datagrams on server (if i understand correctly, maybee speed of fragmentation on PC more than on Galileo). And I don't understand, why count = 67 with DGLEN = 10/50/100/.../500 and why it
then begins to rise (count = 12799 DGLEN = 700, count = 49957 DGLEN = 1000, etc.)

Rest of data:

1600 70
2000 72
2100 1125
2200 6021
2300 10404
2500 20370
2800 30046
2900 37729
2944 39052
2950 39610
2952 42907
2953 15540
3000 15587
3100 21373

I have just started learning basic networking concepts.I am trying to implement a multithread server-client prog in C.but the problem is instead of running multiple windows/terminals/instances for clients,i should use fork() to create children of client.so by creating children of client multiple clients will be created.now each of these child clients will communicate with the server on a thread.

Earlier i created a similar prog but in that for multiple client you have to open multiple windows for clients and run all of them.

I am having trouble where to modify my code (both in server and client ones.I think server one is ok.but i am having no idea where to fork() in client program and what changes should be made).

Actually i don't want to open multiple windows to run multiple client,thats why i am using fork() to create multiple copies of it.Is there any other way by which i can create multiple clients and connect them to my server prog via threads.

Server :

// socket server example, handles multiple clients using threads

#include<stdio.h>
#include<string.h>    //strlen
#include<stdlib.h>    //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h>    //write
#include<pthread.h> //for threading , link with lpthread

//the thread function
void *connection_handler(void *);

int main(int argc , char *argv[])
{
    int socket_desc , client_sock , c , *new_sock;
    struct sockaddr_in server , client;

    //Create socket
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        printf("Could not create socket");
    }
    puts("Socket created");

    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( 3000 );

    //Bind
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
        //print the error message
        perror("bind failed. Error");
        return 1;
    }
    puts("bind done");

    //Listen
    listen(socket_desc , 3);

    //Accept and incoming connection
    puts("Waiting for incoming connections...");
    c = sizeof(struct sockaddr_in);

        c=sizeof(struct sockaddr_in);
       while(client_sock=accept(socket_desc,(struct sockaddr*)&client,(socklen_t*)&c))
       {
        puts("Connection accepted");

        pthread_t sniffer_thread;
        new_sock = malloc(1);
        *new_sock = client_sock;

        if( pthread_create( &sniffer_thread , NULL ,  connection_handler , (void*) new_sock) < 0)
        {
            perror("could not create thread");
            return 1;
        }

        puts("Handler assigned");
    }

    if (client_sock < 0)
    {
        perror("accept failed");
        return 1;
    }
    return 0;
}
/*
  This will handle connection for each client
  */
void *connection_handler(void *socket_desc)
{
    //Get the socket descriptor
    int sock = *(int*)socket_desc;
    int n;

        char    sendBuff[100], client_message[2000];

      while((n=recv(sock,client_message,2000,0))>0)
      {

        send(sock,client_message,n,0);
      }
      close(sock);

      if(n==0)
      {
        puts("Client Disconnected");
      }
      else
      {
        perror("recv failed");
      }
    return 0;
}

Client:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>

#define MAX_SIZE 50

int main()
{
    int sock_desc;
    struct sockaddr_in serv_addr;
    char sbuff[MAX_SIZE],rbuff[MAX_SIZE];

    if((sock_desc = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        printf("Failed creating socket\n");

    bzero((char *) &serv_addr, sizeof (serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(3000);

    if (connect(sock_desc, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
        printf("Failed to connect to server\n");
        return -1;
    }

    printf("Connected successfully - Please enter string\n");
    while(fgets(sbuff, MAX_SIZE , stdin)!=NULL)
    {
      send(sock_desc,sbuff,strlen(sbuff),0);

          if(recv(sock_desc,rbuff,MAX_SIZE,0)==0)
           printf("Error");
          else
           fputs(rbuff,stdout);

       bzero(rbuff,MAX_SIZE);//to clean buffer-->IMP otherwise previous word characters also came
    }
        close(sock_desc);
    return 0;

}

You can create multiple clients using thread. Create a separate thread for each client and then from thread handler connect to the server. I am not sure if it is a good way or not.

Code:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>

#define MAX_SIZE 50
#define NUM_CLIENT 5
void *connection_handler(void *socket_desc);
int main()
{
    int socket_desc , new_socket , c , *new_sock, i;
    pthread_t sniffer_thread;
    for (i=1; i<=NUM_CLIENT; i++) {
        if( pthread_create( &sniffer_thread , NULL ,  connection_handler , (void*) i) < 0)
        {
            perror("could not create thread");
            return 1;
        }
        sleep(3);
    }
    pthread_exit(NULL);
    return 0;
}

void *connection_handler(void *threadid)
{
    int threadnum = (int)threadid;
    int sock_desc;
    struct sockaddr_in serv_addr;
    char sbuff[MAX_SIZE],rbuff[MAX_SIZE];

    if((sock_desc = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        printf("Failed creating socket\n");

    bzero((char *) &serv_addr, sizeof (serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(8888);

    if (connect(sock_desc, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
        printf("Failed to connect to server\n");
    }

    printf("Connected successfully client:%d\n", threadnum);
    while(1)
    {
        printf("For thread : %d\n", threadnum);
        fgets(sbuff, MAX_SIZE , stdin);
        send(sock_desc,sbuff,strlen(sbuff),0);

        if(recv(sock_desc,rbuff,MAX_SIZE,0)==0)
            printf("Error");
        else
           fputs(rbuff,stdout);

        bzero(rbuff,MAX_SIZE);
        sleep(2);
    }
    close(sock_desc);
    return 0;
}

For understanding purpose, i used sleep.

REF:

http://www.amazon.com/UNIX-Network-Programming-Richard-Stevens/dp/0139498761

http://beej.us/guide/bgnet/

https://computing.llnl.gov/tutorials/pthreads/

Firstly, if you fork(), you will be creating additional processes, not additional threads. To create additional threads, you want to use pthread_create.

Secondly, as you are a student, the canonical answer here is 'read Stephens'. Not only is this an invaluable tool even for those of us experienced in writing socket I/O routines, but also it contains examples of non-threaded non-forking async I/O, and various ways to add threads and forking to them. I believe the one you want is: http://www.amazon.com/Programming-Environment-Addison-Wesley-Professional-Computing/dp/0321637739 (chapter 14 if memory serves). This should be in your college library.

I was going through the classic book Unix Network Programming,I meet a problem which is about compile.
e.g. I can compile correctly the source code which is /home/song/unpv13e/tcpcliserv/tcpserv01.c. after I copy it to /home/song/unpv13e/tcpcliserv/test.c and make test.c, it reminds me error as the below shows.

/tmp/ccI4xfzr.o: In function `main':

/home/song/unpv13e/tcpcliserv/test.c:11: undefined reference to Socket' /home/song/unpv13e/tcpcliserv/test.c:18: undefined reference toBind' /home/song/unpv13e/tcpcliserv/test.c:20: undefined reference to Listen' /home/song/unpv13e/tcpcliserv/test.c:21: undefined reference toSignal' /home/yuhongsong/unpv13e/tcpcliserv/test.c:31: undefined reference to Close' /home/song/unpv13e/tcpcliserv/test.c:24: undefined reference toAccept' /home/song/unpv13e/tcpcliserv/test.c:26: undefined reference to Fork' /home/song/unpv13e/tcpcliserv/test.c:27: undefined reference toClose' /home/song/unpv13e/tcpcliserv/test.c:28: undefined reference to `str_echo' collect2: error: ld returned 1 exit status make: *** [test] Error 1

The function name begins with a capital letter is defined in "unp.h". where do the problem arises,how to solve it,and how to use the header suitablely. the source code shows as below:

#include "unp.h"
#include "sigchldwait.c"

int  main(int argc, char **argv)
{
    int                 listenfd, connfd;
    pid_t               childpid;
    socklen_t           clilen;
    struct sockaddr_in  cliaddr, servaddr;


    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(SERV_PORT);

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    Listen(listenfd, LISTENQ);
        Signal(SIGCHLD, sig_chld);
    for ( ; ; ) {
        clilen = sizeof(cliaddr);
        connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

        if ( (childpid = Fork()) == 0) {    /* child process */
            Close(listenfd);    /* close listening socket */
            str_echo(connfd);   /* process the request */
            exit(0);
        }
        Close(connfd);          /* parent closes connected socket */
    }
}

This is a linkage problem, you nee to link against this lib.

You probably compiled correctly against this lib (using the header) but you did not give the lib at lib time.

It si possible that you even need to compile it if you get it from sources.