Thread: Files and Sockets

  1. #1
    Registered User
    Join Date
    Mar 2012
    Location
    the c - side
    Posts
    373

    Files and Sockets

    Can any of the boffins here help with the following. I'm learning sockets with a book i found online called Definitive Guide to Linux Network Programming. The section of code I am struggling with is for a server which reads a file with handle fd and sends it through the socket socket2.

    Things are slightly more complicated since I'm also translating the code from Posix functions to Ansi-c.

    This is the original code.

    Code:
        int fd;
        int readCounter, writeCounter;
        char* bufptr;
        char buf[MAXBUF];
        
        while((readCounter = read(fd, buf, MAXBUF)) > 0)
        {
            writeCounter = 0;
            bufptr = buf;
            while (writeCounter < readCounter)
            {
                readCounter -= writeCounter;
                bufptr += writeCounter;
                writeCounter = write(socket2, bufptr, readCounter);
                if (writeCounter == -1)
                {
                    fprintf(stderr, "Could not write file to client!\n");
                    close(socket2);
                    continue;
                }
            }
        }

    This is the translation into ansi-c, which works ok.


    Code:
        int readCounter, writeCounter;
        char* bufptr;
        char buf[MAXBUF];
        FILE *fd;
    
        /* read the file, and send it to the client in chunks of size MAXBUF */
        while((readCounter = fread(buf,1, MAXBUF,fd)) > 0)
        {
            writeCounter = 0;
            bufptr = buf;
            while (writeCounter < readCounter)
            {
                readCounter -= writeCounter;
                bufptr += writeCounter;
                writeCounter = send(socket2, bufptr, readCounter,0);
                if (writeCounter == -1)
                {
                    fprintf(stderr, "Could not write file to client!\n");
                    closesocket(socket2);
                    continue;
                }
            }
        }
    But I'm unsure of the code. So I just tried the following and it also works fine.

    Code:
        /* read the file, and send it to the client in chunks of size MAXBUF */
        while((readCounter = fread(buf,1, MAXBUF,fd)) > 0)
        {
    
                writeCounter = send(socket2, buf, readCounter,0);
                if (writeCounter == -1)
                {
                    fprintf(stderr, "Could not write file to client!\n");
                    closesocket(socket2);
                    continue;
                }
    
        }
    So what IS the additional code of the original doing?

  2. #2
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Your translation isn't actually ANSI C. Yes, it uses standard file IO functions (fread/fwrite instead of read/write), but it still uses send and recv which are not ANSI. Sockets are OS/implementation specific, thus any socket related functions are specific to that OS and implementation, and not ANSI.

    That being said, the extra code with the readCounter and writeCounter handle partial reads and writes. Just because you ask it to read 100 bytes then write those 100 bytes, it doesn't mean it all gets written. It may only write the first 50 bytes. The next call to read will overwrite the 50 bytes at the end of the buffer, and your last code sample would lose data. Check out the man pages for read and write for more info on their return values and behavior.
    Last edited by anduril462; 08-30-2012 at 10:28 AM.

  3. #3
    Registered User
    Join Date
    Mar 2012
    Location
    the c - side
    Posts
    373
    ok, thanks for the info.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    > closesocket(socket2);
    > continue;
    Shouldn't this be break; instead?

    There's no point continuing if the socket you're going to be sending on has closed.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Mar 2012
    Location
    the c - side
    Posts
    373
    Quote Originally Posted by Salem View Post
    > closesocket(socket2);
    > continue;
    Shouldn't this be break; instead?

    There's no point continuing if the socket you're going to be sending on has closed.
    I'm not at my own computer just now, but from memory the continue
    just returns the server back to listening mode on another socket.

  6. #6
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Nope, continue only works on the innermost loop. Thus, you are continuing the read/write loop, not the listen/accept loop.

  7. #7
    Registered User
    Join Date
    Mar 2012
    Location
    the c - side
    Posts
    373
    Quote Originally Posted by anduril462 View Post
    Nope, continue only works on the innermost loop. Thus, you are continuing the read/write loop, not the listen/accept loop.
    Ok, I'm back looking at the book and it appears as if Salem
    has out-boffined the boffins who wrote it.

    This is the server code from the book (p93 of pdf version).

    Code:
    
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <stdio.h>
    
    #define SERVERPORT 8888
    #define MAXBUF 1024
    
    int main()
    {
        int socket1,socket2;
        int addrlen;
        struct sockaddr_in xferServer, xferClient;
        int returnStatus;
    
        /* create a socket */
        socket1 = socket(AF_INET, SOCK_STREAM, 0);
        if (socket1 == -1)
        {
            fprintf(stderr, "Could not create socket!\n");
            exit(1);
        }
    
        /* bind to a socket, use INADDR_ANY for all local addresses */
        xferServer.sin_family = AF_INET;
        xferServer.sin_addr.s_addr = INADDR_ANY;
        xferServer.sin_port = htons(SERVERPORT);
    
        returnStatus = bind(socket1,(struct sockaddr*)&xferServer,sizeof(xferServer));
        if (returnStatus == -1)
        {
            fprintf(stderr, "Could not bind to socket!\n");
            exit(1);
        }
    
        returnStatus = listen(socket1, 5);
        if (returnStatus == -1)
        {
            fprintf(stderr, "Could not listen on socket!\n");
            exit(1);
        }
    
        for(;;)
        {
            int fd;
            int i, readCounter, writeCounter;
            char* bufptr;
            char buf[MAXBUF];
            char filename[MAXBUF];
    
            /* wait for an incoming connection */
            addrlen = sizeof(xferClient);
    
            /* use accept() to handle incoming connection requests */
            /* and free up the original socket for other requests */
            socket2 = accept(socket1, (struct sockaddr*)&xferClient, &addrlen);
            if (socket2 == -1)
            {
                fprintf(stderr, "Could not accept connection!\n");
                exit(1);
            }
    
            /* get the filename from the client over the socket */
            i = 0;
            if ((readCounter = read(socket2, filename + i, MAXBUF)) > 0)
            {
                i += readCounter;
            }
            if (readCounter == -1)
            {
                fprintf(stderr, "Could not read filename from socket!\n");
                close(socket2);
                continue;
            }
    
            filename[i+1] = '\0';
            printf("Reading file %s\n", filename);
            /* open the file for reading */
            fd = open(filename, O_RDONLY);
            if (fd == -1)
            {
                close(socket2);
                continue;
            }
            /* reset the read counter */
            readCounter = 0;
    
            /* read the file, and send it to the client in chunks of size MAXBUF */
            while((readCounter = read(fd, buf, MAXBUF)) > 0)
            {
                writeCounter = 0;
                bufptr = buf;
                while (writeCounter < readCounter)
                {
                    readCounter -= writeCounter;
                    bufptr += writeCounter;
                    writeCounter = write(socket2, bufptr, readCounter);
                    if (writeCounter == -1)
                    {
                        fprintf(stderr, "Could not write file to client!\n");
                        close(socket2);
                        continue;
                    }
                }
            }
            close(fd);
            close(socket2);
        }
        close (socket1);
        return 0;
    }
    Offhand I'm thinking the code needs an error flag set then a break in the innermost loop,
    then a check for the flag and a break if found in the next loop so it then returns
    to listening mode after closing both the file and redundant socket.

    Code:
            int errflag = 0;
    
            while((readCounter = read(fd, buf, MAXBUF)) > 0)
            {
                writeCounter = 0;
                bufptr = buf;
                while (writeCounter < readCounter)
                {
                    readCounter -= writeCounter;
                    bufptr += writeCounter;
                    writeCounter = write(socket2, bufptr, readCounter);
                    if (writeCounter == -1)
                    {
                        fprintf(stderr, "Could not write file to client!\n");
                        errflag = 1;
                        break;
                    }
                }
                if(errflag == 1)
                {
                    break;
                }
            }
    Apart from that I'm left thinking its pretty hard to check that the
    less travelled pathways do in fact do what's intended.

  8. #8
    Registered User
    Join Date
    Mar 2012
    Location
    the c - side
    Posts
    373
    Just in case anyone's interested.

    After a bit of thought,it wasn't so hard to check the partial-send pathway.

    Every check on loopback and simple LAN always sent as much as was in the buffer.

    So it was impossible to check the partial-send path directly.

    So all I did was create a simple pseudo "mysend" function in a debug version of the server.

    Instead of writing to the socket it writes to another file whose size is easy to check.

    The number of bytes written is determined randomly on each call and returned to the writecount variable.

    So it simulates a very dodgy send() function.

    Hey Presto!, path checked.

  9. #9
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Nice!
    Now if only a lot more of the beginners on here would think like that.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help with coding/command sending Jpg files through sockets
    By geewhan in forum Linux Programming
    Replies: 8
    Last Post: 01-21-2012, 10:52 AM
  2. Sockets tutorial, datagram sockets example not working for me
    By caesius in forum Networking/Device Communication
    Replies: 14
    Last Post: 12-26-2009, 03:40 PM
  3. Downloading files (not sockets)
    By Muscovy in forum Linux Programming
    Replies: 3
    Last Post: 09-14-2009, 11:47 PM
  4. send/recv binary files using sockets in C++
    By dafatdude in forum Networking/Device Communication
    Replies: 14
    Last Post: 07-25-2004, 11:00 AM