#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2003
    Posts
    217
    Rep Power
    0

    Sending a file using sockets


    Does anyone know how to properly send a file using sockets? At the moment I am using this:

    Code:
    sock.sendall(somefile.read())
    and

    Code:
    input = sock.recv(1024)
    on the other end. It works for simple stuff like tiny text files (well anything under 1024 bytes) but it's obviously not going to work for something like an mp3. Am I supposed to loop my recv until I get a 0?
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    London, England
    Posts
    1,585
    Rep Power
    1373
    It has been a while since I have done any programming with raw sockets, but I think you are right, loop until recv returns zero. The example in the docs does exactly that.

    An alternative way is to create a file-like object from the socket, and read from it exactly like you would a file. From the docs for the socket object:

    makefile( [mode[, bufsize]])

    Return a file object associated with the socket. (File objects are described in 2.3.8, ``File Objects.'') The file object references a dup()ped version of the socket file descriptor, so the file object and socket object may be closed or garbage-collected independently. The socket should be in blocking mode. The optional mode and bufsize arguments are interpreted the same way as by the built-in file() function; see ``Built-in Functions'' (section 2.1) for more information.
    Hope this helps.

    Dave - The Developers' Coach
  4. #3
  5. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    I only know ftp:
    The ftplib uses sendall for sending
    and the following code snippet for receiving:
    Code:
            while 1: 
                data = conn.recv(blocksize)
                if not data: 
                    break
                callback(data)
            conn.close()
    (The callback is used to write the data to a file).

    sendall manages the transfer - splitting the large block of data into smaller blocks and sending/resending as necessary until all is received, the data will be passed up to your higher level in the correct sequence although the order in which it is actually received may be different.

    FTP obviously wraps this in a protocol so that both sides of the conversation know what to expect. Before the transfer there is an exchange of commands and acknowledgements. The problem is it's dedicating the connection for file transfer. You have no clean way to interrupt it.
    One solution is to create a second socket connection just for file transfer (this is what ftp does) leaving the original socket for commands and other traffic
    Code:
    From RFC 959
                                                -------------
                                                |/---------\|
                                                ||   User  ||    --------
                                                ||Interface|<--->| User |
                                                |\----^----/|    --------
                      ----------                |     |     |
                      |/------\|  FTP Commands  |/----V----\|
                      ||Server|<---------------->|   User  ||
                      ||  PI  ||   FTP Replies  ||    PI   ||
                      |\--^---/|                |\----^----/|
                      |   |    |                |     |     |
          --------    |/--V---\|      Data      |/----V----\|    --------
          | File |<--->|Server|<---------------->|  User   |<--->| File |
          |System|    || DTP  ||   Connection   ||   DTP   ||    |System|
          --------    |\------/|                |\---------/|    --------
                      ----------                -------------
    
                      Server-FTP                   USER-FTP
    One alternative could be to use "inband" commands. In this scenario you can do something like put an info-field at the head of every packet which states it's purpose - either a command or a file packet. The file-data block you want to send will be split into sub-blocks by you and you use send() or sendall() to send each sub-block. It becomes your job to untangle the packets and re-order the sub-blocks if necessary. This way you socket is not locked when the file transfer fails - you still keep the chat going. This assumes that your receiving socket is not being battered by a rogue file sender of course.

    If you wrap this concept into a virtual socket class then you might have something that is very useful. By this I mean taking one "physical" socket object and allowing 'n' virtual connections to multiplex over it. This idea is used in "tunnelling" situations where routes through firewalls are needed or you only allow secure connections like ssh. This would allow anything you like - chat, file transfer, buddy lists etc.

    I know my msn client cannot work through my firewall for anything other than simple chat because of the need to create additional peer to peer socket connections for the actual transfer. A solution to this would be to allow all data transfers to go via the main server but this obviusly puts extra load on the server which is why they don't do it.

    Anyone know other "solutions"?

    Grim
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2003
    Posts
    217
    Rep Power
    0
    That's a cool idea Grim, I'll look into it later. Btw, thanks for both of your replys .
  8. #5
  9. WebDeveloper++;
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2003
    Location
    New York City
    Posts
    85
    Rep Power
    12
    I was playing around with sockets a few weeks ago. heres two scripts i came up with. one is the server, the other is the client. you need to run the server script then call the client script with relative file as the first argument to the script.

    Code:
    # USAGE: python FileSender.py [file]
    
    import sys, socket
    
    HOST = 'localhost'
    CPORT = 9091
    MPORT = 9090
    FILE = sys.argv[1]
    
    cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    cs.connect((HOST, CPORT))
    cs.send("SEND " + FILE)
    cs.close()
    
    ms = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ms.connect((HOST, MPORT))
    
    f = open(FILE, "rb")
    data = f.read()
    f.close()
    
    ms.send(data)
    ms.close()

    Code:
    # USAGE: python FileReciever.py
    
    import socket, time, string, sys, urlparse
    from threading import *
    
    #------------------------------------------------------------------------
    
    class StreamHandler ( Thread ):
    
        def __init__( this ):
            Thread.__init__( this )
    
        def run(this):
            this.process()
    
        def bindmsock( this ):
            this.msock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            this.msock.bind(('', 9090))
            this.msock.listen(1)
            print '[Media] Listening on port 9090'
    
        def acceptmsock( this ):
            this.mconn, this.maddr = this.msock.accept()
            print '[Media] Got connection from', this.maddr
        
        def bindcsock( this ):
            this.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            this.csock.bind(('', 9091))
            this.csock.listen(1)
            print '[Control] Listening on port 9091'
    
        def acceptcsock( this ):
            this.cconn, this.maddr = this.csock.accept()
            print '[Control] Got connection from', this.maddr
            
            while 1:
                data = this.cconn.recv(1024)
                if not data: break
                if data[0:4] == "SEND": this.filename = data[5:]
                print '[Control] Getting ready to receive "%s"' % this.filename
                break
    
        def transfer( this ):
            print '[Media] Starting media transfer for "%s"' % this.filename
    
            f = open(this.filename,"wb")
            while 1:
                data = this.mconn.recv(1024)
                if not data: break
                f.write(data)
            f.close()
    
            print '[Media] Got "%s"' % this.filename
            print '[Media] Closing media transfer for "%s"' % this.filename
        
        def close( this ):
            this.cconn.close()
            this.csock.close()
            this.mconn.close()
            this.msock.close()
    
        def process( this ):
            while 1:
                this.bindcsock()
                this.acceptcsock()
                this.bindmsock()
                this.acceptmsock()
                this.transfer()
                this.close()
    
    #------------------------------------------------------------------------
    
    s = StreamHandler()
    s.start()

IMN logo majestic logo threadwatch logo seochat tools logo