Thursday, January 8, 2009

TCP segmentation example

Let's straight to the point.
Simply to make tcp segmented traffic, we need to turn off Nagle's algorithm. We can accomplish this with TCP_NODELAY option. I needed one for my test, but I couldn't find an example.
Since it is not that hard, I just wrote one. Don't expect too much in this example, however. I just needed this real quick, and wrote very sloppy. Here it comes:

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


#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define MAXDATASIZE 8192
#define SERVER_ADDR "172.16.136.147"
#define SERVER_PORT 80
#define PAYLOAD "GET /index.html"
#define CHUNK 2 // chunk size. 2 byte segment for each.


int main()
{
    int sockfd, numbytes;
    char buf[MAXDATASIZE] = {0, };
    int optval;
    struct sockaddr_in srv;

    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
        handle_error("socket failed");

    optval = 1;
    if ( 0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval))
        handle_error("sockopt, REUSEADDR");
    optval = 1;
    if ( 0 > setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &optval, sizeof optval))
        handle_error("sockopt, NO NAGLE");

    memset(&srv, '\0', sizeof srv);
    srv.sin_family = AF_INET;
    srv.sin_port = htons(SERVER_PORT);
    if ( 0 > inet_pton(AF_INET, SERVER_ADDR, &srv.sin_addr))
        handle_error("lookup fail");

    // CONNECT!
    if ( 0 > connect(sockfd, (struct sockaddr *)&srv, sizeof srv))
        handle_error("connect error");

    // Lazy: I'm not handling buffer overflow.
    strcpy(buf, PAYLOAD);
    
    int i;
    char sbuf[ CHUNK ];
    for (i=0; i<strlen(PAYLOAD); i += CHUNK) {
        strncpy(sbuf, buf+i, CHUNK);
        write(sockfd, sbuf, CHUNK);
        usleep(100000); // give time to clear the system call.
    }
    write(sockfd, "\n\n", 2);

    if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1)
        handle_error("recv");
    buf[numbytes] = '\0';

    printf("client: received '%s'\n",buf);
    close(sockfd);

    return 0;
}