Transfer A Text File Via Socket In Python

I have a project in PYTHON that is two machines (A , B) , 1) request machine A send a request to B to list a directory(in my code i set it to current directory) 2) in second request machine A wants to download the Text file of the directory. (Put a text file in machine B's directory) 3) after that machine A changes the text file and send back to the machine B. 4) and At the end machine A send two number and machine B send back the result of it. it work till step 2 but nothing happen after that it's like a true while I can't understand why?! Here is my Code Machine A (Client):

# -*- coding: UTF-8 -*-
import os
import socket
PORT = 9000
HOST = 'localhost'
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect((HOST, PORT))
store=[]
direc = raw_input("Enter The Directory to List : ")
socket.sendall(direc)
len_data = socket.recv(2048)
print int(len_data)
for i in range(int(len_data)):
    data = socket.recv(2048)
    store.append(data)
print("List of Files:")
for i in store:
    print(i)
txt_file = raw_input("Please Choose a TEXT file :")
if store.count(txt_file) is 0:
    print("There no such a TXT file!!")
else:
    socket.sendall(txt_file)

def write_file(name):
    fname = open(name,'ab')
    while True:
        string = socket.recv(2048)
        if string:
            fname.write(string)
        else:
            fname.write("changed")
            fname.close()
            break
def read_file(name):
    fileToSend = open(name, 'rb')
    while True:
        data = fileToSend.readline()
        if data:
            socket.send(data)
        else:
            fileToSend.close()
            break

write_file(txt_file)
read_file(txt_file)
x = raw_input("Enter The First Num: ")
socket.send(x)
y = raw_input("Enter The Second Num: ")
socket.send(y)
result = socket.recv(1024)
print result
raw_input()
socket.sendall('')
socket.close()
exit()

and the Machine B (Server):

import os,sys,socket
PORT = 9000
HOST = 'localhost'
tcpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
print >>sys.stderr, 'starting up on %s port %s' % server_address
socket.bind((HOST,PORT))
socket.listen(1)
conn, addr = socket.accept()
directory = conn.recv(2048)
if os.listdir(os.curdir):
    data = os.listdir(os.curdir)
len_data = data.__len__()
print(len_data)
if len_data:
        conn.send(str(len_data))
for i in data:
    if i:
        print >>sys.stderr, 'sending data back to the client'
        conn.send(i)
    else:
        break
txt_file_name = conn.recv(2048)
def write_file(name):
    with open(name,'wb') as fname:
        while True:
            string = conn.recv(2048)
            if string:
                fname.write(string)
            else:
                fname.close()
                break
def read_file(name):
    with open(name, 'rb') as fileToSend:
        while True:
            data = fileToSend.readline()
            if data:
                conn.send(data)
            else:
                fileToSend.close()
                break

def add (x,y):
    return str(x+y)

read_file(txt_file_name)
write_file(txt_file_name)

x = conn.recv(1024)
y = conn.recv(1024)
conn.send(add(x,y))
conn.sendall('')
conn.close()
exit()

Upvotes: 2

Views: 4390

Answers (1)

Hai Vu
Hai Vu

Reputation: 40773

I am fascinated with your problem and looked into it. While we can solve it using socket. I lean toward HTTP protocol for several reasons:

  • You don't have to make up your own "hand shake". The HTTP protocol has provision for requesting file, uploading a file, and do some processing (your step #4)
  • You can test your server using a web browser
  • Web services are very popular now. This is a baby step to learn about web services.

Here is the server code (server.py):

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
import os

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        global running
        if self.path == '/':
            self.list_files()
        elif self.path.startswith('/calculation'):
            self.send_calculation()
        elif self.path.startswith('/quit'):
            self.send_response(200)
            running = False
        else:
            self.send_file(self.path[1:])

    def do_POST(self):
        filename = self.path[1:] # Remove the / from the path
        filesize = int(self.headers['Content-Length'])
        contents = self.rfile.read(filesize)

        with open(filename, 'w') as f:
            f.write(contents.decode())

        self.send_response(200)

    def send_file(self, filename):
        # Check to see if file exists and is a file, not directory
        if os.path.isfile(filename):
            self.send_response(200)
            self.send_header('Content-Type', 'text/plain')
            self.end_headers()

            # Read and send the contents of the file
            with open(filename) as f:
                contents = f.read()
            self.wfile.write(contents)
        else:
            self.send_response(404)
            self.send_header('Content-Type', 'text/plain')
            self.end_headers()
            self.wfile.write('Dude! File not found')

    def send_calculation(self):
        empty, operation, number1, number2 = self.path.split('/')
        result = int(number1) + int(number2)
        self.send_response(200)
        self.send_header('Content-Type', 'text/plain')
        self.end_headers()
        self.wfile.write(result)

    def list_files(self):
        file_list = os.listdir(os.curdir)
        if file_list:
            self.send_response(200)
            self.send_header('Content-Type', 'text/plain')
            self.end_headers()
            for filename in file_list:
                self.wfile.write('{}\n'.format(filename))

#
# Main
#
running = True
server = HTTPServer(('', 9000), MyHandler)
print 'Server started on host:{}, port:{}'.format(*server.server_address)
while running:
    server.handle_request()

And here is the client code (client.py):

import urllib2
import urlparse

def make_url(server, port, path, scheme='http'):
    netloc = '{}:{}'.format(server, port)
    url = urlparse.urlunsplit((scheme, netloc, path, '', ''))
    return url

#
# Main
#
server = '10.0.0.5'
port = 9000

# 1 - Request directory listing
url = make_url(server, port, '/')
file_list = urllib2.urlopen(url).read()
print 'Files from server:'
for filename in file_list.splitlines():
    print '- {}'.format(filename)

# 2 - Request contents of a file
filename = raw_input('Type a file name: ')
url = make_url(server, port, filename)
contents = urllib2.urlopen(url).read()
print 'Contents:'
print contents

# 3 - Upload a file to the server
contents = 'hello, world.\nThe End'
filename = 'foo.txt'
url = make_url(server, port, filename)
f = urllib2.urlopen(url, data=contents)

# 4 - Do some calculation
n1 = 19
n2 = 5
path = '/calculation/{}/{}'.format(n1, n2)
url = make_url(server, port, path)
result = int(urllib2.urlopen(url).read())
print '{} + {} = {}'.format(n1, n2, result)

# Send quit signal

url = make_url(server, port, '/quit')
urllib2.urlopen(url).read()

Web Service

The server is really a web service, which provides the following services:

Get a directory listing

GET http://server:port/

This service will return a list of files in the current directory.

Get contents of a file

GET http://server:port/filename

Returns the contents of a file in plain text format.

Upload a file

POST http://server:port/filename

Copy a file from the client to the server. If the file already exists on the server, override it.

Do some calculation

GET http://server:port/calculation/x/y

Returns x + y

Shut down the server

GET http://server:port/quit

Tells the server to quit.

Error Handling

For the sake of brevity and clarity, I did not add and error handling to the code. Here are a few error condition that I can think of:

  • Retrieve a non-existing file, or a directory (server)
  • Upload failed because of the lack of file write permission (server)
  • In the calculation service, the parameters are not numbers (server)
  • The server has not started, wrong port, wrong server (client)

Other Discussions

  • In a general, GET means data flow from the server to the client, and POST the opposite direction.
  • To test GET action from the server, you can use your browser. For example, to retrieve the directory contents from 192.168.1.5, port 9000, point your web browser to:

    http://192.168.1.5:900/  
    
  • Testing POST is trickier, see the client code in the upload section for idea of using POST.

  • In the server code, the do_GET() function handles all the GET requests, and the do_POST() function handles all the POST requests.

Upvotes: 3

Related Questions