I&C SCI 32 Chapter Notes - Chapter 3: Connect Four, Hard Disk Drive, Newline

60 views5 pages
ICS 32 Fall 2015
Code Example: Networks and Sockets 2
Background
Sending and receiving text via sockets
Many programs that communicate over a network do so by sending text back and forth; quite often, textual communication
protocols are based around the concept of lines of text terminated by a special sequence of characters called an end-of-line
sequence. We've previously seen how to read lines of text from files; unfortunately, the mechanisms we've seen for reading data
from a socket are a lot harder to work with, allowing us to retrieve a given number of bytes from a socket's input stream, which
leaves us with two difficulties:
Decoding the bytes we receive, turning them from bytes into strings.
Finding where one line of ends and another begins. And since we have to ask for a certain number of bytes at a time, but are never
necessarily sure ahead of time how many bytes make up a line (since different lines may contain different numbers of bytes), we'll
have to go to a lot of trouble: if we read too few characters, we'll need to read more; if we read too many, we'll need to hang on to
the extra ones so we can use them as part of the next line we read. These kinds of details can be very tricky to get right.
It seems a shame that we can't just treat a socket the way we do a text file. Files contain bytes, too, but when we read from text files
in Python, the file object converts between bytes and strings and finds where one line ends and another begins automatically; we
just have to call readline() and the right things happen.
As is often the case in a programming language library, when you dig a little bit further, you find the tool you were looking for. If you
want to treat a socket similarly to how you treat a text file — reading lines and getting back strings, writing strings — you can do it.
You just have to ask for a "middleman" of sorts, an object that behaves outwardly the way a file object does, but that reads and
writes via a socket rather than a file.
Sockets in Python provide a makefile() method that can give you such a "middleman." The arguments you pass to this method are
the same as the arguments you pass to the built-in open() function that opens files, and the object returned to you appears a lot like
a file object — it supports methods like readline() and write().
This code example makes use of the makefile() method to simplify how we read from and write to our sockets.
Taking the opportunity to think about design
While we could have implemented our program in one long function, we opted for an approach I've advocated a lot this quarter:
breaking it into progressively smaller functions, each with a meaningful name and well-named parameters. Each function had a
single job to do, and we did our very best to keep separate things separate — for example, never mixing the code we wrote to
manipulate sockets with the code we used to interact with the user.
Your initial reaction to this might be that we didn't need so many functions, that we could have written a shorter program without
them. However, we'll see — especially in the next code example — why this kind of approach is so critical. The more complex a
program becomes, the more important it is to break it apart, so you can focus your attention on one part of it, while mostly ignoring
the rest of it. Not only does this let you always work on parts small enough that you can understand what problem you're trying to
solve, but it also gives you the ability to make progress even if you're not sure how your whole program will fit together when it's
done, by choosing one small problem, writing a function to solve that one problem, and then testing it individually in the Python
shell. This helps you to work incrementally, to continually make at least some progress, and to gradually build your confidence in
your own solution.
The code
In lecture, we rewrote the echo client program from the previous code example using the socket's makefile() method to let us read
and write lines of text instead of bytes. That example follows.
The echo_client module
# echo_client.py
#
# ICS 32 Fall 2015
find more resources at oneclass.com
find more resources at oneclass.com
Unlock document

This preview shows pages 1-2 of the document.
Unlock all 5 pages and 3 million more documents.

Already have an account? Log in
# Code Example
#
# As we saw in lecture, the send() and recv() methods that send and
# receive bytes can be cumbersome when our goal is to send and receive
# text. It should always be our goal to find a better tool for a job
# when there is one -- or to build ourselves a better tool when there
# isn't
#
# Fortunately, a better tool exists: we can ask a socket object to
# give us a "pseudo-file object," an object that behaves just like a
# file object, except that it reads or writes to the socket's underlying
# streams, instead of to a file. We end up needing two of them: one that
# reads from the socket's input stream and another that writes to its
# output stream. Once we have them, we can treat our socket a lot like
# a text file, using techniques we already know.
#
# On the other hand, this leaves us with three separate objects to manage:
# the socket, the pseudo-file object for reading, and the pseudo-file
# object for writing. Our best move is to collect these into one data
# structure, which we can pass around wherever we need it. We opted for
# a tuple in lecture (and in this example), but we'll see in the next
# example that a namedtuple would actually be a much better choice.
import socket
# The first few functions are centered around interacting with the user:
# asking for input and printing output in a clean form. We keep these
# functions separate from the others, so that other functions become much
# simpler and more straightforward.
def read_host() -> str:
'''
Asks the user to specify what host they'd like to connect to,
continuing to ask until a valid answer is given. An answer is
considered valid when it consists of something other than just
spaces.
'''
while True:
host = input('Host: ').strip()
if host == '':
print('Please specify a host (either a name or an IP address)')
else:
return host
def read_port() -> int:
'''
Asks the user to specify what port they'd like to connect to,
continuing to ask until a valid answer is given. A port must be an
integer between 0 and 65535.
'''
while True:
try:
port = int(input('Port: ').strip())
if port < 0 or port > 65535:
print('Ports must be an integer between 0 and 65535')
else:
return port
except ValueError:
print('Ports must be an integer between 0 and 65535')
find more resources at oneclass.com
find more resources at oneclass.com
Unlock document

This preview shows pages 1-2 of the document.
Unlock all 5 pages and 3 million more documents.

Already have an account? Log in

Get access

Grade+20% off
$8 USD/m$10 USD/m
Billed $96 USD annually
Grade+
Homework Help
Study Guides
Textbook Solutions
Class Notes
Textbook Notes
Booster Class
40 Verified Answers
Class+
$8 USD/m
Billed $96 USD annually
Class+
Homework Help
Study Guides
Textbook Solutions
Class Notes
Textbook Notes
Booster Class
30 Verified Answers

Related Documents